Difference between revisions of "GLSL : common mistakes"

From OpenGL.org
Jump to: navigation, search
m
m
Line 117: Line 117:
 
  resultRGB = colorRGB_0 * (1.0 - alpha) + colorRGB_1 * alpha;
 
  resultRGB = colorRGB_0 * (1.0 - alpha) + colorRGB_1 * alpha;
 
which can be simplified to
 
which can be simplified to
  resultRGB = colorRGB_0  - alpha * (colorRGB_0 + colorRGB_1);
+
  resultRGB = colorRGB_0  + alpha * (colorRGB_1 - colorRGB_0);
  
 
and of course, GPUs have a special instruction just for this common case. GLSL calls it mix while other languages like Cg calls it lerp.
 
and of course, GPUs have a special instruction just for this common case. GLSL calls it mix while other languages like Cg calls it lerp.

Revision as of 05:16, 7 April 2009

The following article discusses common mistakes made in the OpenGL Shading Language, GLSL.

Enable Or Not To Enable

With fixed pipeline, you needed to call glEnable(GL_TEXTURE_2D) to enable 2D texturing. You needed to call glEnable(GL_LIGHTING). Since shaders override these functionalities, you don't need to glEnable/glDisable. If you don't want texturing, you either need to write another shader that doesn't do texturing or you can attach a all white or all black texture, depending on your needs. You can also write one shader that does lighting and one that doesn't.

Things that are not overriden by shaders, like the alpha test, depth test, stencil test... calling glEnable/glDisable will have an effect.

Binding A Texture

When you compile and link your GLSL shader, the next step is to get uniform locations for your samplers (I'm talking about texture samplers) and setup the samplers. Some people do this:

glUniform1i(location, textureID)

You can't send a GL texture ID as your sampler. A sampler should be from 0 to the max number of texture image units.
Once you compile and link your shader, make sure that you setup all the samplers by calling (assuming of course your samplers are named Texture0, Texture1 and Texture2

location=glGetUniformLocation(shaderProgram, "Texture0"); 
glUniform1i(location, 0);
location=glGetUniformLocation(shaderProgram, "Texture1"); 
glUniform1i(location, 1);
location=glGetUniformLocation(shaderProgram, "Texture2"); 
glUniform1i(location, 2);

To bind a texture, always use glBindTexture.

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureID[0]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, textureID[1]);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, textureID[2]);

or

for(i=0; i<3; i++)
{
  glActiveTexture(GL_TEXTURE0+i);
  glBindTexture(GL_TEXTURE_2D, textureID[i]);
}

If you don't set the samplers properly, you might get a link failure that says

Output from shader Fragment shader(s) linked, vertex shader(s) linked.
Validation failed - samplers of different types are bound to the same texture i
mage unit.

Types

nVidia drivers are more relaxed. You could do

float myvalue = 0;

but this won't compile on other platforms. Use 0.0 instead. Don't write 0.0f. GLSL is not C or C++.

float texel = texture2D(tex, texcoord);

The above is wrong since texture2D returns a vec4 Do this instead

float texel = float(texture2D(tex, texcoord));

or

float texel = texture2D(tex, texcoord).r;

or

float texel = texture2D(tex, texcoord).x;

Functions

Functions should look like this

vec4 myfunction(inout float value1, in vec3 value2, in vec4 value3)

instead of

vec4 myfunction(float value1, vec3 value2, vec4 value3)

Not Used

In the vertex shader

gl_TexCoord[0] = gl_MultiTexCoord0;

and in the fragment shader

vec4 texel = texture2D(tex, gl_TexCoord[0].xy);

zw isn't being used in the fs.
Keep in mind that for GLSL 1.30, you should define your own vertex attribute.
This means that instead of gl_MultiTexCoord0, define AttrMultiTexCoord0.
Also, do not use gl_TexCoord[0]. Define your own varying and call it VaryingTexCoord0.

Easy Optimization

gl_TexCoord[0].x = gl_MultiTexCoord0.x;
gl_TexCoord[0].y = gl_MultiTexCoord0.y;

turns into

gl_TexCoord[0].xy = gl_MultiTexCoord0.xy;

Keep in mind that for GLSL 1.30, you should define your own vertex attribute.
This means that instead of gl_MultiTexCoord0, define AttrMultiTexCoord0.
Also, do not use gl_TexCoord[0]. Define your own varying and call it VaryingTexCoord0.

The MAD instruction

MAD is short for multiply, then add. It is a special floating point circuit. Very fast. Costs 1 GPU cycle.

vec4 result1 = (value / 2.0) + 1.0;
vec4 result2 = (value / 2.0) - 1.0;
vec4 result3 = (value / -2.0) + 1.0;

The above doesn't quite easily turn into a MAD. It might be compiled to a reciprocal, then add. That might cost 2 or more cycles. Below is GLSL code that converts to a single MAD instruction (for each line of code of course)

vec4 result1 = (value * 0.5) + 1.0;
vec4 result2 = (value * 0.5) - 1.0;
vec4 result3 = (value * -0.5) + 1.0;

More MAD

One expression might be better than the other.

result = 0.5 * (1.0 + variable);

which compiles to

ADD  temp, 1.0, variable;
MUL  result, temp, 0.5;

Compare the above with this

result = 0.5 + 0.5 * variable;

which compiles to

MAD result, variable, 0.5, 0.5;

Of course, your GLSL compiler might be smart enough and optimize the above simple example for you but code it right yourself!

Linear Interpolation, lerp, mix

This is more about being aware of built in functions of GLSL and making use of them so that your GLSL compiler easily generates the "low level hardware executable". Blending 2 values based on some factor

vec3 colorRGB_0, colorRGB_1;
float alpha;
resultRGB = colorRGB_0 * (1.0 - alpha) + colorRGB_1 * alpha;

which can be simplified to

resultRGB = colorRGB_0  + alpha * (colorRGB_1 - colorRGB_0);

and of course, GPUs have a special instruction just for this common case. GLSL calls it mix while other languages like Cg calls it lerp.

resultRGB = mix(colorRGB_0, colorRGB_1, alpha);

GL_LINE_SMOOTH, GL_POINT_SMOOTH , GL_POLYGON_SMOOTH

If you have any of these enabled, it is known that this causes software mode rendering on ATI/AMD.
You should not even need these. Enable fulscreen MSAA instead.

Compile GLSL

This should be in the FAQ but for now, we'll leave it here.
Can you compile a GLSL program using some offline compiler?
Yes, by using the Cg compiler.
The Cg compiler not only compiles Cg code, but it can also do translations from one language to another, also called the target language.
You would have to download the Cg package which contains the compiler from developer.nvidia.com
Once install, using the command line, type

cgc -oglsl -profile vp40 test.glsl_vs

That means your file test.glsl_vs contains your GLSL vertex shader and your target is GL_NV_vertex_program4 (also called vp40), so it should print out the shader on screen.
The Cg compiler also supports other targets like arbvp and arbfp and vp10, vp20, vp30, and the fp version as well.

There is no official offline compiler for OpenGL. The ARB didn't intend for this. Normally, you would just send your GLSL code to GL and the GL driver compiles the program, generates a GPU specific binary code, which eventually gets uploaded to the GPU when you decide to use it.