Search code examples
c#openglglslopentk

OpenGL - Render face of cube map to a quad


I need to render a particular face of a cube map to a quad for debugging purposes.

From this similar question I gather you use three dimensional texture co-ordinates, but I'll need a little information to get over this hurdle. All I'm getting is black at the moment, sometimes a different colour entirely.

Until now, I've drawn 2D quads with the following.

GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, textureID);

GL.BindVertexArray(VAO);
GL.DrawArrays(PrimitiveType.Triangles, 0, vertices.Length);

Do I need to make an adjustment here? What is the general approach for doing this?


Solution

  • There are a few changes needed, which are all quite simple.

    Texture target

    Where you currently use TextureTarget.Texture2D, you will use TextureTarget.TextureCubeMap instead. For example, assuming that you already have a given cube map texture:

    GL.BindTexture(TextureTarget.TextureCubeMap, textureID);
    

    Sampler type in shader code

    In your fragment shader, you currently have a sampler variable defined, which will look something like this:

    uniform sampler2D Tex;
    

    You will modify this to:

    uniform samplerCube Tex;
    

    Texture coordinates

    The biggest change is related to your texture coordinates. Texture coordinates used for a cube map will have 3 components, which can be interpreted as a direction vector. Picture the cube corresponding to the cube map centered at the origin. The direction vector given by the texture coordinates then points at the texel that will be sampled.

    One option is that you modify the texture coordinates in the vertex attributes produced by your client code. You can expand them to 3 components instead of previously 2, and choose the appropriate values to isolate the face you want to render.

    Instead of doing that, it may be almost easier to calculate the new texture coordinates based on the existing texture coordinates you're already passing to the shader code for 2D textures. Where the current texture coordinates span a square with extent [0.0, 1.0] in both coordinate directions, you need to map that range to the face of the cube, where the cube is centered at the origin, and has extent [-1.0, 1.0] in each coordinate direction.

    To accomplish this, you use -1.0 or 1.0 for the coordinate direction matching the face you want to isolate, and scale/shift the input texture coordinates from range [0.0, 1.0] to range [-1.0, 1.0] for the other two coordinate directions.

    Say you had the following in your shader code for the 2D texture case:

    uniform sampler2D Tex;
    in vec2 TexCoord;
    ...
        vec4 val = texture(Tex, TexCoord);
    

    Then, for the GL_TEXTURE_CUBE_MAP_POSITIVE_X face, you use 1.0 for the x-coordinate of the cube texture coordinates, and scale/shift the remaining two coordinates:

    uniform samplerCube Tex;
    in vec2 TexCoord;
    ...
        vec2 mapCoord = 2.0 * TexCoord - 1.0;
        vec4 val = texture(Tex, vec3(1.0, mapCoord.xy));
    

    Equivalent for the GL_TEXTURE_CUBE_MAP_NEGATIVE_X face:

        vec4 val = texture(Tex, vec3(-1.0, mapCoord.xy));
    

    For the GL_TEXTURE_CUBE_MAP_POSITIVE_Y face:

        vec4 val = texture(Tex, vec3(mapCoord.x, 1.0, mapCoord.y));
    

    For the GL_TEXTURE_CUBE_MAP_NEGATIVE_Y face:

        vec4 val = texture(Tex, vec3(mapCoord.x, -1.0, mapCoord.y));
    

    For the GL_TEXTURE_CUBE_MAP_POSITIVE_Z face:

        vec4 val = texture(Tex, vec3(mapCoord.xy, 1.0));
    

    For the GL_TEXTURE_CUBE_MAP_NEGATIVE_Z face:

        vec4 val = texture(Tex, vec3(mapCoord.xy, -1.0));
    

    Note that the orientation of a cube map face is kind of ambiguous. If you have specific expectations on the orientation of the resulting output, you might have to permute/mirror some of the values in the code above.