GLSL Transform Feedback: Capturing Vertex Output

Transform feedback in OpenGL and GLSL allows developers to capture primitive data generated by vertex processing stages—such as vertex, tessellation, and geometry shaders—and write it directly into buffer objects on the GPU. By disabling the rasterization stage entirely, the pipeline can completely bypass fragment rendering, effectively turning the graphics pipeline into a dedicated, high-performance general-purpose computation engine. This article explains the architectural mechanics of transform feedback, how to eliminate the fragment processing stage, and the operational steps required to record vertex output data.

The Pipeline Architecture of Transform Feedback

In a traditional graphics pipeline, processed vertices pass through primitive assembly, get rasterized into fragments, and are ultimately processed by the fragment shader before reaching the framebuffer.

Transform feedback sits between the post-processing vertex stages (geometry or vertex execution) and the primitive assembly/rasterization stage. Instead of allowing primitives to continue downstream toward the screen, the GPU intercepts the output varyings defined in your GLSL shaders and redirects them into one or more bound Buffer Objects (GL_TRANSFORM_FEEDBACK_BUFFER).

Bypassing Fragment Rendering with Rasterizer Discard

Capturing vertex data without executing fragment shaders relies on the OpenGL state toggle GL_RASTERIZER_DISCARD.

Under normal execution, the pipeline requires a fragment shader to evaluate colors and depths for every generated pixel. When you enable rasterizer discard using:

glEnable(GL_RASTERIZER_DISCARD);

the hardware completely turns off the rasterization stage. Primitives processed by vertex or geometry shaders do not generate fragments, rendering the fragment shader idle and skipping it entirely. This eliminates the performance cost of depth testing, blending, stencil tests, and fragment execution. Once the capture pass is complete, rasterization can be re-enabled using glDisable(GL_RASTERIZER_DISCARD) if rendering to a framebuffer is later required.

Configuring the GLSL Shader and Program

To record specific outputs, the OpenGL program must identify which GLSL output variables to capture before the shader program is linked. This is accomplished using glTransformFeedbackVaryings.

  1. Declare outputs in the Vertex Shader:
#version 330 core
layout (location = 0) in vec3 inPosition;
layout (location = 1) in vec3 inVelocity;

out vec3 outPosition;
out vec3 outVelocity;

uniform float deltaTime;

void main() {
    outPosition = inPosition + inVelocity * deltaTime;
    outVelocity = inVelocity; // update or apply forces
}
  1. Specify varyings before linking:
const char* varyings[] = { "outPosition", "outVelocity" };
glTransformFeedbackVaryings(shaderProgram, 2, varyings, GL_INTERLEAVED_ATTRIBS);
glLinkProgram(shaderProgram);

Outputs can be written into a single buffer using GL_INTERLEAVED_ATTRIBS or separated across distinct buffers using GL_SEPARATE_ATTRIBS.

Executing the Capture Loop

The actual recording process requires binding an output buffer and wrapping the draw call in transform feedback execution markers:

  1. Bind Buffer to Feedback Target: Bind the destination buffer to the indexed transform feedback binding point using glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, index, bufferID).
  2. Disable Rasterization: Call glEnable(GL_RASTERIZER_DISCARD).
  3. Begin Feedback: Call glBeginTransformFeedback(GL_POINTS). The primitive type must match the output topology generated by the preceding shader stages.
  4. Issue Draw Calls: Execute standard draw commands such as glDrawArrays or glDrawElements. The GPU executes the vertex shader, captures the specified output variables into the bound buffer, and immediately drops the primitives before rasterization.
  5. End Feedback: Call glEndTransformFeedback().
  6. Restore Pipeline State: Call glDisable(GL_RASTERIZER_DISCARD) if subsequent render passes need to draw to the screen.

Practical Applications

By eliminating the rasterization and fragment stages, transform feedback creates an efficient GPU-to-GPU data pipeline. The captured buffer can immediately serve as a vertex buffer object (VBO) for a subsequent render pass or feed into another transform feedback pass in a ping-pong buffer configuration. This approach is widely used for particle systems, cloth physics simulations, and iterative computational algorithms where keeping data entirely in GPU memory delivers maximum performance.