Articles
GLSL: AN INTRODUCTION
What Is GLSL? GLSL (GLslang) is a short term for the official OpenGL Shading Language. GLSL is a C/C++ similar high level programming language for several parts of the graphic card. With GLSL you can code (right up to) short programs, called shaders, which are executed on the GPU. Why Shaders? Until DirectX 8 hardware (GeForce 2 and lower, Radoen 7000 and lower) the graphic pipeline could only be configured, but not be programmed. For example there is the OpenGL lighting model with ambient, diffuse, specular and emissive lighting. This model is mainly used but there are many other models for lighting. In fixed-function OpenGL only this lighting model could be used, no other. With Shaders you are able to write your own lighting model. But thatÂ’s only one feature of shaders. There are thousands of other really nice possibilities: Shadows, Environment Mapping, Per-Pixel Lighting, Bump Mapping, Parallax Bump Mapping, HDR, and much more! Why GLSL? Shaders are available in OpenGL till 2002 through ARB_vertex_program and ARB_fragment_program extension. But with those extensions you are only able to use assembly shaders. Because of the growing complexity of lighting and shading models assembly shaders are hard to use. GLSL is a high-level shading language, which means that you can write your shader in C/C++ style. This makes shader development much easier! What Is The Difference Between Fixed Function Pipeline And GLSL? There are two types of shaders in GLSL: vertex shaders and fragment shaders. Vertex Shader A vertex shader operates on every vertex. So if you call glVertex* (or glDrawArrays, Â…) the vertex shader is executed for each vertex. If you use a vertex shader you have nearly full control over what is happening with each vertex. But if you use a vertex shader ALL Per-Vertex operations of the fixed function OpenGL pipeline are replaced (see Figure 1):
For a full overview what a vertex shader replaces and what it does not replace please see reference [1], page 41. So if you want to use a vertex shader you HAVE to do all these things above on your own (of course, only if you need it :-) Fragment Shader A fragment shader operates on every fragment which is produced by rasterization. With fragment shader you have nearly full control over what is happening with each fragment. But just like a vertex shader, a fragment shader replaces ALL Per-Fragment operations of the fixed function OpenGL pipeline (see Figure 1):
For a full overview what a fragment shader replaces and what it does not replace please see reference [1], page 43. Using a fragment shader is just like using a vertex shader, because you HAVE to do all these things above on your own (of course, only if you need it). ![]() Figure 1, OpenGL 1.5 fixed function pipeline [1] What Does GLSL Look Like? As mentioned above there are 2 types of shaders, a vertex shader and a fragment shader. Each shader type has other inputs and outputs. Data Types In GLSL There are four main types: float, int, bool and sampler. For the first three types, vector types are available: vec2, vec3, vec4 2D, 3D and 4D floating point vector ivec2, ivec3, ivec4 2D, 3D and 4D integer vector bvec2, bvec3, bvec4 2D, 3D and 4D boolean vectors For floats here are also matrix types: mat2, mat3, mat4 2x2, 3x3, 4x4 floating point matrix Samplers are types representing textures. They are used for texture sampling. Sampler types have to be uniform. They are not allowed to be declared as a non-uniform type. Here are the different sampler types: sampler1D, sampler2D, sampler3D 1D, 2D and 3D texture samplerCube Cube Map texture sampler1Dshadow, sampler2Dshadow 1D and 2D depth-component texture About Attributes, Uniforms And Varyings There are three types of inputs and outputs in a shader: uniforms, attributes and varyings. Uniforms are values which do not change during a rendering, for example the light position or the light color. Uniforms are available in vertex and fragment shaders. Uniforms are read-only. Attributes are only available in vertex shader and they are input values which change every vertex, for example the vertex position or normals. Attributes are read-only. Varyings are used for passing data from a vertex shader to a fragment shader. Varyings are (perspective correct) interpolated across the primitive. Varyings are read-only in fragment shader but are read- and writeable in vertex shader (but be careful, reading a varying type before writing to it will return an undefined value). If you want to use varyings you have to declare the same varying in your vertex shader and in your fragment shader. All uniform, attribute and varying types HAVE to be global. You are not allowed to specify a uniform/attribute/varying type in a function or a void. Built-In Types GLSL has some built-in attributes in a vertex shader: gl_Vertex 4D vector representing the vertex position gl_Normal 3D vector representing the vertex normal gl_Color 4D vector representing the vertex color gl_MultiTexCoordX 4D vector representing the texture coordinate of texture unit X There are some other built-in attributes, see reference [2], page 41 for a full list. GLSL also has some built-in uniforms: gl_ModelViewMatrix 4x4 Matrix representing the model-view matrix. gl_ModelViewProjectionMatrix 4x4 Matrix representing the model-view-projection matrix. gl_NormalMatrix 3x3 Matrix representing the inverse transpose model-view matrix. This matrix is used for normal transformation. There are some other built-in uniforms, like lighting states. See reference [2], page 42 for a full list. GLSL Built-In Varyings: gl_FrontColor 4D vector representing the primitives front color gl_BackColor 4D vector representing the primitives back color gl_TexCoord[X] 4D vector representing the Xth texture coordinate There are some other built-in varyings. See reference [2], page 44 for a full list. And last but not least there are some built-in types which are used for shader output: gl_Position 4D vector representing the final processed vertex position. Only available in vertex shader. gl_FragColor 4D vector representing the final color which is written in the frame buffer. Only available in fragment shader. gl_FragDepth float representing the depth which is written in the depth buffer. Only available in fragment shader. The importance of built-in types is that they are mapped to the OpenGL states. For example if you call glLightfv(GL_LIGHT0, GL_POSITION, my_light_position) this value is available as a uniform using gl_LightSource[0].position in a vertex and/or fragment shader. Generic Types You are also able to specify your own attributes, uniforms and varyings. For example if you want to pass a 3D tangent vector for each vertex from your application to the vertex shader you can specify a “Tangent” attribute: attribute vec3 Tangent; Here are some other examples:
Language Details GLSL is quite similar to C/C++ but there are some minor differences. See reference [1], page 57 for ANSI C features which are not supported in GLSL. Additionally GLSL has the following features/restrictions: GLSL is 100% type safe. You are not allowed to assign an integer to a float without casting (by constructor):
Casts have to be done using constructors. For example this wonÂ’t work:
Vectors and matrices can be only be filled with user-data using constructors:
Vector multiplication is component-wise:
Vector with matrix multiplication is also available.
This will simply transform the vertex position by the model-view-projection matrix. There are also many built-in function which can (and should) be used: dot a simple dot product cross a simple cross product texture2D used for sampling a texture normalize normalize a vector clamp clamping a vector to a minimum and a maximum For a full list of built-in functions see reference [2], page 46. Each shader must have a main() void. This void is called if the shader is executed. Shader Examples So, up to now we have heard a lot concerning GLSL. But how does a shader look like. Here are some simple examples: Ambient Shader The ambient shader surely is the simplest shader available. Each rendered pixel has one specific color: Vertex Shader:
Fragment Shader:
Diffuse Shader The diffuse lighting model is one common used lighting model. ItÂ’s a little bit harder to implement: Vertex Shader:
Fragment Shader:
Texture Mapping Shader This is a simple shader for texture mapping. Vertex Shader varying vec2 texture_coordinate;
Fragment Shader varying vec2 texture_coordinate; uniform sampler2D my_color_texture;
GLSL API – How To Use GLSL In Your OpenGL Application All right, we heard how to write shaders and what they are good for. But how do we use them in our OpenGL application? GLSL is available right now through 4 extensions: GL_ARB_shader_objects GL_ARB_shading_language_100 GL_ARB_vertex_shader GL_ARB_fragment_shader All extensions documents are available in the OpenGL extension registry, see reference [3]. GLSL is very similar to C/C++. So if you want to use a GLSL shader you have to do the following steps:
A shader object represents your source code. You are able to pass your source code to a shader object and compile the shader object. A program object represents a useable part of render pipeline. How To Create Those Objects? A Program object is created with the command A Shader object is created with the commend So for example: if you want to create a vertex shader object you have to do the following:
How To Pass Your Shader Source Code To A Shader Object? This can be done by using
With this command you are able to pass more than one string to one shader object.
Here an example on how to pass a shader source to a vertex shader:
How To Compile A Shader Object? This can simply be done by using
shader is our shader object How to link my shader objects to a program object? First of all we have to create a program object. Then we attach all shader objects which we want to use to this program object using
program is our program object and shader is our shader object Then we link our program object with
program is our program object. How To Use A Program Object? Program objects can be used with
program is our program object If program is 0 then standard fixed function OpenGL is used Putting All Together Here is a code example for loading, compiling, linking and using shaders:
If all shaders were compiled successfully and the program object was linked successfully all renderings after glUseProgramObjectARB will be done using our shader. Some Other Important Functions Of course you also can delete (shader and program) objects with
Another VERY important command is
object is a shader or program object. This function gives you information about shader and program objects. For example: if you are calling this function after a failed compiling you will get information why the compiling failed. Uniforms Uniforms can be passed to the GL using void glUniform{1|2|3|4}{f|i}ARB(GLint location, TYPE val) void glUniform{1|2|3|4}{f|i}vARB(GLint location, GLuint count, const TYPE * vals) void glUniformMatrix{2|3|4|}fvARB(GLint location, GLuint count, GLboolean transpose, const GLfloat * vals) location is the location of the uniform. Getting the uniform location can easily be done with
program is our program object. So hereÂ’s an example of how to use uniforms:
Note that you are not able to pass built-in uniform via glUniform! Generic Attributes Generic Attributes are just like Uniforms: void glVertexAttrib{1|2|3|4}{s|f|d}ARB(GLuint index, TYPE val) void glVertexAttrib{1|2|3|4}{s|f|d}vARB(GLuint index, const TYPE * vals) index is the location if the attribute. Getting the attribute location can also easily be done with
program is our program object. An example:
Using Textures With GLSL Because many GLSL beginners have problems with texturing I want to say something about this now. As I said before... textures are available in GLSL via sampler, which have to be uniform. But how do we tell GLSL which texture image should be used for which sampler? Textures are not directly passed to GLSL, but we pass the texture unit where our texture is bound to OpenGL. This works in the following way:
Here an example:
i is the texture unit where I want to bind my texture. There are many other useful functions for using GLSL. See the extension specification or reference [4] for more information. Shader Hardware/Driver Overview Alright, we have heard many about shaders and GLSL. But which hardware is able to run shaders? GLSL is very similar to DirectX Shader Model 3.0 Right now GLSL is available on the following graphic cards: ATI Radeon 9500, 9600, 9700 and 9800 using Catalyst 4.5 driver (which is the most bug-free ATI GLSL driver now). nVidia GeForce FX 5200, 5600, 5700, 5800, 5900, 5950 using forceware 60 series (you can get these drivers at http://www.3dchipset.com/drivers/beta/nvidia/nt5/6111.php ) Because of the fact that these cards only support Shader Model 2.0 some features of GLSL are not available, like loops, vertex shader sampler … I don't think anything older than these cards will support GLSL fragment shader. However I think there might be vertex shader emulation in driver software for these cards. GLSL Shader Development Tools There are also some nice shader development tools for GLSL: Typhoon Labs has a really nice shader development tool called “Shader Designer”. You can get it at http://www.typhoonlabs.com ATI and 3Dlabs are also working together on a GLSL version of RenderMonkey. This tool is not available yet but should come out very soon! GLSL References Finally, I would like to list some nice GLSL references: http://www.clockworkcoders.com/oglsl http://www.3dlabs.com/support/developer/ogl2/index.htm http://www.ati.com/developer/sdk/RadeonSDK/Html/Samples/OpenGL/GLSLSimpleShader.html http://developer.nvidia.com/object/sdk_home.html#NV40_Video_Clips http://esprit.campus.luth.se/~humus Thanks for reading! "If you want to contact my please make sure that it doesn't look like spam! I get a lot of spam mail/ICQ requests... Everything suspicious will be deleted" References: [1] OpenGL Shading Language Master Class Notes, 3DLabs, March/April 2004
|
|