Shader Programming, Volume 12
Vertex and Fragment Shaders
Now that we’ve learned enough about Vertex functions to implement a snow shader, we’ll be moving on to Vertex and Fragment shaders. As compared to Surface Shaders, Vertex and Fragment shaders have access to very little information about the physical properties that determine how light reflects on surfaces, but they are perfect for non-photorealistic effects.
Fragment Shaders and Grab Passes, Understanding Vertex and Fragment Shaders
The first Vertex and Fragment shader we’ll make will just apply a given texture to a model and multiply it by a given color. It’s a simple shader that’ll provide a good base for future Vertex and Fragment shaders, and it’ll give us the opportunity to get familiar with how they’re structured. Here’s what the code looks like:
Vertex and Fragment shaders have two steps, essentially: first the model is passed through the vertex function, then the result is inputted into a fragment function. Both of these functions are assigned using pragma directives on lines 14 and 15.
The vertex function receives the input data defined as vertInput on line 20 of the shader.
You could name the struct anything, but each field of the struct has to be decorated with a binding semantic. This feature of Cg allows us to mark variables so that they’ll be initialized with certain data.
Input Binding Semantics:
- POSITION, SV_POSITION: the position of a vertex in world coordinates (object space).
- NORMAL: the normal of a vertex, relative to the world.
- COLOR, COLOR0, DIFFUSE, SV_TARGET: The color information stored in the vertex.
- COLOR1, SPECULAR: the secondary color information stored in the vertex (usually the specular).
- TEXCOORD, TEXCOORD1, etc.: the i-th UV data stored in the vertex.
In this case, pos will contain the position of the current vertex. The position is in model coordinates, relative to the 3D object, so we have to convert it to view coordinates manually using the model-view-projection matrix in line 35.
The other variable in the vertInput struct, texcoord, is initialized using the TEXCOORD0 binding semantics to get the UV data of the first texture. This is passed directly to the fragment function in line 36.
For the frag function, the vertOutput struct is needed. Much like the vertInput struct it follows similar binding semantics.
Output Binding Semantics:
- POSITION, SV_POSITION, HPOS: the position of a vertex in camera coordinates (clip space, from zero to one for each dimension).
- COLOR, COLOR0, COL0, COL, SV_TARGET: the front primary color.
- COLOR1, COL1: the front secondary color.
- TEXCOORD0 / TEX0, TEXCOORD1 / TEX1, etc: the i-th UV data stored in the vertex.
- WPOS: the position, in pixels, in the window (origin in the lower left corner).
The vertOutput struct is passed to the fragment function, which samples the main texture of the model and multiplies it by the color provided on line 43.
Here’s how it looks when it all comes together:
Okay, that was a pretty painless introduction to Vertex and Fragment shaders. I’m hoping the next shader will be a little more intriguing though. In the next article we’ll learn how to use the grab pass technique to implement a glass shader.