Apply varying colors per-vertex to WebGL triangles

Keith Peters
InstructorKeith Peters
Share this video with your friends

Social Share Links

Send Tweet

In this lesson we will apply a single color per vertex and see how those colors are interpolated across an entire triangle, making use of a new kind of shader variable. We will also see how enabling gl.DEPTH_TEST will give our model proper depth by having webGL pay attention to z-values. Note: This lesson assumes prior knowledge in WebGL. Check out Create 3D Graphics in JavaScript Using WebGL for an in depth introduction to WebGL if some of these concepts are feeling unfamiliar.

[00:00] Thus far we've been using a single color for every fragment that's drawn to the screen. We're able to change that color via JavaScript, but we need to have a way to have different objects or different parts of objects have different colors. We'll do that now.

[00:14] So far we've only seen color used in the fragment shader, but WebGL allows us to assign a color to each vertex in the vertex shader as well. We can then use this special mechanism to pass that color data to the fragment shader where those vertex colors will be interpolated across the full triangular surface.

[00:34] If we have a triangle with red, green, and blue vertices like so, the fragment shader will interpolate those colors and fill the rectangle like this. First of all we need to define the colors. We'll work from the end of the previous lesson where we have the rotating triangles like so.

[00:53] We're going to assign a random color to every single vertex, so we'll need a new array to hold all those color values. I'll make that right up at the top of the create vertices function. As we loop through and create the X, Y, and Z values for each vertex, we'll also get a random red, green and blue value along with one for alpha. We'll push those four values onto the array's separate elements.

[01:18] We'll need to pass this color data over to the vertex shader. It's going to be the exact same mechanism that we're using to pass the vertex data over. I'll just copy that whole block, paste it back in. For buffer I'll change that to color buffer, and instead of vertices I'll use the colors array. Then, instead of getting the coords location, I'll get the location of an attribute called colors. We'll have to create that attribute in the shader. We'll do that in a moment.

[01:47] I'll pass in that colors pointer to glVertexAttribPointer, and we'll make sure that it knows that there are four values per element. I'll enable that pointer and unbind the buffer. Just below that I'm going to comment out this code that sets the color in the fragment shader as we'll be doing something very different now. That's all we need to do on the JavaScript, for now anyway.

[02:15] Let's jump over the shaders. As you might guess we're going to add a new vec4 attribute called colors. That should be familiar now. But how do we get this per vertex color data over to the fragment shader as per fragment color data?

[02:32] There's another kind of shader variable that's used just for this purpose. It's called a varying. We'll say varying vec4, and we'll call this one varying colors. This will be the link between the shaders.

[02:49] We need to assign the current value of colors to varying colors each time the shader is executed on a vertex. Inside the shader we simply say varying colors = colors. Then we jump down to the fragment shader. We'll create the exact same varying variable here.

[03:10] This is the link, but it's a magical link. We set a single color per vertex, and we get back a different color for every single fragment, which translates to different colors for each pixel, all nicely smoothly interpolated.

[03:27] We can get rid of this original color uniform and instead apply varying colors to gl_FragColor. Run it and there we go. Look at all the pretty colors.

[03:38] As cool as this looks there's obviously something wrong here. The shapes are rotating around, but they're not really arranging themselves in space correctly. We can fix that really quickly.

[03:50] By default WebGL doesn't pay attention to Z values or depths of objects when drawing them to the screen. So sometimes things that are farther away will get drawn on top of things that are closer. All we have to do is tell it to test depths before drawing. We'll do that back in the initGL method by saying glEnable(GL_DEPTH_TEST).

[04:14] Hey, now that's way better. What we have now really does look like 3D. We're still lacking some of the finer details like accurate perspective and lighting, but there's no denying that this looks three-dimensional.

[04:28] Play around with this one. You're actually at the point now where you could start building out some real models by arranging the vertices into sensible shapes and applying specific colors to each one rather than randomly, like we're doing here.

[04:39] You might want to try capturing some keyboard or mouse events and transforming the vertices based on those rather than the set rotation values we're doing now.