OpenGL is a versatile graphics language with a wide variety of uses. For example, a video conferencing application might use OpenGL to display streaming video. Such applications usually involve a video codec that operates in the YUV color space, and a video display that operates in the RGB color space. At some point, the color space must be converted for this to work. With OpenGL, the color space conversion can be done using a fragment shader. A fragment shader is a program that runs every time OpenGL generates a color for a pixel. Using fragment shaders can be complicated, but the advantage is that they usually run in dedicated hardware on the GPU, leaving the CPU available for other tasks.
The following example shows a minimal OpenGL fragment shader for pixel format conversion:
/* One texture for each plane of the YUV image. Each texture is only * greyscale, and they may be different resolutions. */ uniform sampler2D tex_y; uniform sampler2D tex_u; uniform sampler2D tex_v;
/* Texture coordinate from the vertex shader */ varying vec2 tex_coord;
/* Function to convert from YUV to RGB color space. This conversion may * vary depending on the application. */ vec3 yuv_to_rgb(vec3 yuv) { // TODO return vec3(0, 0, 0); }
/* Main function for the fragment shader. This function is run for every * pixel that is drawn and returns the pixel’s color. */ void main() { vec3 yuv; yuv.x = texture2D(tex_y, tex_coord).r; yuv.y = texture2D(tex_u, tex_coord).r; yuv.z = texture2D(tex_v, tex_coord).r; gl_FragColor = vec4(yuv_to_rgb(yuv), 1.0); } |
To use the shader, its source code can be passed to OpenGL as a string through the API. OpenGL has functions for creating shader programs, setting their source code and compiling them. Once a program is compiled, the program variables (uniform in this example) can be accessed through OpenGL API functions. In this case, the uniform variables are bound to 2D textures and the normal OpenGL texture functions can be used to set their data. When a new YUV image is ready, the three texture planes are updated with the new data. To draw the image, the color conversion shader program must be made current. Then a quad with texture coordinates can be drawn somewhere in the viewport. This will cause the fragment shader to run for each viewport pixel that is covered by the quad. This has the added benefit of automatically scaling the image to fit in the OpenGL viewport.