Custom Filters, or CSS Shaders as they used to be called, allow you to use the power of WebGL's shaders with your DOM content. Since in the current implementation the shaders used are virtually the same as those in WebGL, you need to take a step back and understand some 3D terminology and a little bit of the graphics pipeline.
I've included a recorded version of a presentation I recently delivered to LondonJS. In the video I step through an overview of the 3D terminology you need to understand, what the different variable types are that you'll encounter, and how you can start playing with Custom Filters today. You should also grab the slides so you can play with the demos yourself.
I've previously written an introduction to shaders which will give you a good breakdown of what shaders are and how you can use them from a WebGL point of view. If you've never dealt with shaders it's something of a required read before you go much further, because many of the Custom Filters concepts and language hinges on the existing WebGL shader terminology.
So with that said, let's enable Custom Filters and plough on!
Custom Filters are available in both Chrome and Canary as well as Chrome for Android. Simply head over to about:flags and search for "CSS Shaders", enable them and restart the browser. Now you're good to go!
Custom Filters expands on the set of filters that you can already apply, like blur or sepia, to your DOM elements. Eric Bidelman wrote a great playground tool for those, which you should check out.
To apply a Custom Filter to a DOM element you use the following syntax:
.customShader {
-webkit-filter:
custom(
url(vertexshader.vert)
mix(url(fragment.frag) normal source-atop),
/* Row, columns - the vertices are made automatically */
4 5,
/* We set uniforms; we can't set attributes */
time 0)
}
You'll see from this that we declare our vertex and fragment shaders, the number of rows and columns we want our DOM element to get broken down into, and then any uniforms we want to pass through.
A final thing to point out here is that we use the mix() function around the fragment shader with a blend mode (normal), and a composite mode (source-atop). Let's take a look at the fragment shader itself to see why we even need a mix() function.
If you're familiar with WebGL's shaders you'll notice that in Custom Filters things are a little different. For one we don't create the texture(s) that our fragment shader uses to fill in the pixels. Rather the DOM content that has the filter applied gets mapped to a texture automatically, and that means two things:
gl_FragColor is off limits. Rather, it's assumed that you will want to render the DOM content, and what you get to do is manipulate its pixels indirectly through css_ColorMatrix and css_MixColor.That means our Hello World of fragment shaders looks more like this:
void main() {
css_ColorMatrix = mat4(1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0);
css_MixColor = vec4(0.0, 0.0, 0.0, 0.0);
// umm, where did gl_FragColor go?
}
Each pixel of the DOM content is multiplied by the css_ColorMatrix, which in the above case does nothing as its the identity matrix and changes none of the RGBA values. If we did want to, say, just keep the red values we would use a css_ColorMatrix like this:
// keep only red and alpha
css_ColorMatrix = mat4(1.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0);
You can hopefully see that as you multiply the 4D (RGBA) pixel values by the matrix that you get a manipulated pixel value out of the other side, and in this case one that zeros out the green and blue components.
The css_MixColor is mainly used as a base color that you want to, well, mix in with your DOM content. The mixing is done through the blend modes that you'll be familiar with from art packages: overlay, screen, color dodge, hard light and so on.
There are plenty of ways that these two variables can manipulate the pixels, and included in my presentation is a demo that you can play around with. You should check out the Custom Filters specification to get a better handle on how the blend and composite modes interact.
In WebGL we take full responsibility for the creation of our mesh's 3D points, but in Custom Filters all you have to do is specify the number of rows and columns that you want and the browser will automatically break down your DOM content into a bunch of triangles:
Each one of those vertices then gets passed through to our vertex shader for manipulation, and that means we can start moving them around in 3D space as we need. It's not long before you can make some pretty crazy effects!
Bringing in animations to your shaders is what makes them fun and engaging. To do that you simply use a transition (or animation) in your CSS to update uniform values:
.shader {
/* transition on the filter property */
-webkit-transition: -webkit-filter 2500ms ease-out;
-webkit-filter: custom(
url(vshader.vert)
mix(url(fshader.frag) normal source-atop),
1 1,
time 0);
}
.shader:hover {
-webkit-filter: custom(
url(vshader.vert)
mix(url(fshader.frag) normal source-atop),
1 1,
time 1);
}
So the thing to notice in the code above is that time is going to ease from 0 to 1 during the transition. Inside the shader we can declare the uniform time and use whatever its current value is:
uniform float time;
uniform mat4 u_projectionMatrix;
attribute vec4 a_position;
void main() {
// copy a_position to position - attributes are read only!
vec4 position = a_position;
// use our time uniform from the CSS declaration
position.x += time;
gl_Position = u_projectionMatrix * position;
}
Custom Filters are great fun to play with, and the amazing effects you can create are difficult (and in some cases impossible) without them. It is still early days, and things are changing around, but adding them will add a little bit of showbiz to your projects, so why not give them a go?
Here’s a look at some cool WebGL and Web Audio API demos that I’ve seen over the past couple weeks.
EVE Online ship viewer is a great-looking online ship viewer app built with WebGL. Very nice way to showcase the artwork in the game universe.
Web Audio API samples page has several compelling examples on how to do audio processing using it. WebGL City is one of the demos linked from the samples page. It’s a small demo of a helicopter flying around a night cityscape. The helicopter (disable music by pressing 'm’, enable helicopter sound by pressing 'n’) uses Web Audio APIs spatial audio features to pan the helicopter audio from one speaker to the other.
Some enterprising soul implemented a snake game using nothing but a WebGL fragment shader on the GLSL Sandbox. I’m flabbergasted.
The Big Bang may look like any other WebGL particle animation, but the particle simulation is actually run on the GPU. The simulator is a fragment shader that reads the previous particle positions from a texture and writes the new particle positions into a FBO texture.
Blocky Earth takes Google Earth data and MineCrafts it. It communicates differences in height well. For example, I was looking at Australia and the Antarctic ice sheet, and you can see how the continental ice is several kilometers thick.
The Midem Music Machine is a fun music demo by Mr.doob and Paul Lamere. It’s a sort of a ball-driven music box with balls bouncing off bits 'n’ bops. CreativeJS has a good writeup on it, check it out.
Continuing on the computer music visualization theme, I recently ran across this page about bytebeat, a form of music generated by minimalistic code formula. The page links to one cool WebGL visualization of the music. Gregg Tavares took to the idea and built a bytebeat sandbox for making and sharing your own bytebeat tunes directly from the browser.
SwiftShader is a software 3D renderer used in Chrome 18 that lets you use CSS 3D and WebGL even on blacklisted GPUs. SwiftShader is available only on Windows and kicks in when you visit a site that uses 3D graphics features.
The first time you run Chrome, it checks if your GPU is blacklisted. In the unfortunate case that it is, Chrome downloads and installs the SwiftShader component in the background. After the component is installed, you can view 3D content. If you visit a 3D site before the component has finished installing, you may need to close and re-open the tab to view the site.
The performance of SwiftShader should be good enough to view simple 3D content.
To force-enable SwiftShader for testing purposes, start Chrome from the command line with the —blacklist-accelerated-compositing and —blacklist-webgl flags.
You can read more about Chrome 18’s new graphics features (including GPU-accelerated 2D canvas) at the Chromium Blog
Heads-up from our friends at Opera, who have been testing WebGL on actual OpenGL ES 2.0 hardware: many demos and applications use highp precision in fragment shaders when it’s not really warranted.
Highp in fragment shaders is an optional part of the OpenGL ES 2.0 spec, so not all hardware supports it (and even when they do, there may be a performance hit). Using mediump will usually be good enough and it will ensure that your applications will work on mobile devices as well.
In practice, if your fragment shader previously started with
precision highp float;precision mediump float; // or lowp
Developers keep pushing the boundaries of what's possible to do in the browser. Here are some awesome new WebGL demos from around the web to showcase what your browser can really do. First off, have a gander at this impressive three.js dynamic terrain rendering demo from AlteredQualia. The second demo is Nouvelle Vague from the French web design agency Ultranoir, and it shows a very particular way to read tweets. Mr.doob comes back with another three.js demo, this time using data captured by a Kinect. Also using three.js is One Millionth Tower, a documentary funded by the Canadian National Film Board. And last, these guys got WebGL running on an iPad 2 through a WebView hack.
In other news, the Google Data Arts Team launched a workshop page to share the technology they've used to build some amazing demos. The page has a great tutorial on using dat.GUI, go check it out!
If your browser supports WebGL try this awesome demo so you can drive a car in 3D with no plugins at all. We are seeing more and more developers coming up with new 3D demos whether they were OpenGL experts already or new adventurers that started playing with it.
To make developer’s life easier there is the well known Three.js library, that abstracts the native methods to increase your productivity. The aforementioned demo is obviously using it too.
Our in-house expert, Ilmari Heikkinen, has built some interactive slides to guide you through the creation process step by step. Enjoy!
[Screenshot from creativeJS]
WebGL specification has an important update on how to request images, cross-domain. The feature has already been implemented in Chrome 13 and is coming soon to Firefox 5.
Just use image.crossOrigin method on the client side and if you can edit the server just add support to it.
Read all the details in Using Cross-domain images in WebGL and Chrome 13.