Saturday, December 29, 2007

Default Uniform Values

In GLSL 1.10 all uniforms are assigned a default value of 0 when a program is successfully linked.  In GLSL 1.20 all uniforms, except for samplers, can have an initializer, like so:

uniform vec3 color = vec3(0.7, 0.7, 0.2);

All uniforms without initializers are assigned a default value of 0.

References:
GLSL 1.20 specification, p. 24

Thursday, December 27, 2007

Active Shader Uniforms and Vertex Attributes

A uniform is only active if the GLSL compiler and/or linker determine that it is used in the shader, or cannot conclusively determine that it is not used in the shader.  As an example, in the following shader pair foo, rex, and gl_LightModel.ambient are the only active uniforms.

[vertex]
uniform vec4 foo, bar, baz, rex;
varying vec4 var;
void main()
{
   var = rex;
   gl_Position = foo;
}

[fragment]
uniform vec4 color, size, rex, throwaway;
varying vec4 var;
void main()
{
   gl_FragColor = (rex + var) * gl_LightModel.ambient;
}

In this more complex example posMod and baz are the only active uniforms.

[vertex]
uniform vec4 v_color, posMod, foo, bar;
varying vec4 colMod;
void main()
{
  colMod = v_color * gl_Vertex;
  gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex + posMod;
}

[fragment]
uniform vec4 f_color, rex, baz;
varying vec4 colMod;
void main()
{
   vec4 finalColor = f_color * colMod;
   finalColor += rex;
   gl_FragColor = baz;
}

Even though v_color, f_color, and rex are all used in the code as written they do not have any affect on the final vertex position or fragment color.  As such, a good GLSL compiler/linker will determine that they are unused.

Vertex attributes have similar rules.  In order to be considered active they must in some way affect the final vertex position or fragment color.

This differentiation between active and inactive can be important, especially if you are dealing with procedurally generated shaders.  Only active uniforms and attributes count towards implementation defined limits.  That is, if your implementation has a limit of 16 vertex attributes you can still declare more than 16 as long as 16 or fewer actually affect the final vertex position or fragment color.

References:
OpenGL 2.1 Specification (12/01/2006) pp. 76-79

Tuesday, December 12, 2006

Rendering to multiple textures

To render to multiple textures, you still use the glDrawBuffers function. However, you use the enums from GL_EXT_framebuffer_object.

Example:

GLenum buffers[] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT };
glDrawBuffers(2, buffers);

If a framebuffer object is bound with textures attached to GL_COLOR_ATTACHMENT0_EXT and GL_COLOR_ATTACHMENT1_EXT, they will both be rendered to now.

Monday, December 11, 2006

Rendering to multiple buffers

Rendering to multiple buffers, known as MRT to the Direct3D world, can be accomplished with the GL_ARB_draw_buffers extension. Using this extension is incredibly simple. For example, to draw to the back and AUX0 buffers simultaneously, you would use the following code:

GLenum buffers[] = { GL_BACK, GL_AUX0 };
glDrawBuffersARB(2, buffers)

And there you have it.

Of course, this is rather dull unless we can actually write different colors to different buffers. In order to do this, shaders must be used. In GLSL, you can select which buffer is written to by writing to gl_FragData[n] in place of gl_FragColor. If you are using GL_ARB_fragment_program, you can select which buffer is written to by writing to result.color[n].

References:

GL_ARB_draw_buffers specification

Sunday, December 10, 2006

Streaming texture updates

It is possible to stream updates to a texture, often skipping a costly data copy, using the GL_ARB_pixel_buffer_object extension which was promoted to core in OpenGL 2.1

To do this, you must bind a buffer to GL_PIXEL_UNPACK_BUFFER_ARB, map it, write your texture data into the mapped buffer, unmap it, and call glTexSubImage2D referencing into the buffer.

// Bind buffer
glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, myBuffer);

// Null existing data
glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, width * height * bytesPerPixel, NULL, GL_STREAM_DRAW);

// Map buffer. Returns pointer to buffer memory
void *pboMemory = glMapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY);

writeImage(pboMemory); // Writes data into pboMemory

// Unmaps buffer, indicating we are done writing data to it
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, (char *)NULL);

// Unbind buffer
glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);


Normally the driver must copy the data you pass into glTexSubImage2D. However, by storing the data in a buffer, which is memory managed by the driver, we can avoid this copy. Furthermore, by using a universally hardware accelerated texture format (see this tip) we prevent any data processing, ensuring the fastest possible download to the GPU.


References:

GL_ARB_pixel_buffer_object specification

Saturday, December 9, 2006

Setting vertex attribute locations

While you can query the location of a generic vertex attribute in a GLSL shader by calling glGetAttribLocation(GLuint program, const GLchar *name), you can also set the location of a generic vertex attribute manually using glBindAttribLocation(GLuint program, GLuint index, const GLchar *name)

NOTES: Manually set attribute locations do not take effect until glLinkProgram(GLuint program) is called.

You cannot manually assign a location to a built in vertex attribute (e.g. gl_Vertex).

It is possible to assign the same location to multiple attributes. This process is known as aliasing, and is only allowed if just one of the aliased attributes is active in the executable program. HOWEVER the implementation is not required to check for aliasing and is free to employ optimizations that only work in the abscence of aliasing.

Any vertex attribute which is not manually assigned a location will be assigned one by the linker, and this location can be queried with glGetAttribLocation(GLuint program, const GLchar *name).

References:
glBindAttribLocation man page

Thursday, December 7, 2006

Render to Texture

OpenGL supports fast crossplatform offscreen rendering through the GL_EXT_framebuffer_object extension.

To render to a texture using the framebuffer object you must

1) Create a framebuffer object
glGenFramebuffersEXT(1, &myFBO);


2) Bind the framebuffer object
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, myFBO);


3) Attach a texture to the FBO
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, myTexture, 0);


4) If you need depth testing, create and attach a depth renderbuffer

// Gen renderbuffer
glGenRenderbuffersEXT(1, &myRB);

// Bind renderbuffer
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, myRB);

// Init as a depth buffer
glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, width, height);

// Attach to the FBO for depth
glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, myRB);

5) Render just like you normally would.

6) Unbind the FBO (and renderbuffer if necessary). glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);

7) Use the texture you rendered to!

NOTES:

All textures and renderbuffers attached to the framebuffer object must have the same dimensions.

You must use the GL_EXT_packed_depth_stencil extension to use stencil testing with framebuffer objects.

You can only render to RGB, RGBA, and depth textures using framebuffer objects.


References:

Framebuffer object specification