OpenGL - 将立方体贴图的面渲染为四边形

时间:2016-07-23 15:00:39

标签: c# opengl glsl opentk

我需要将一个立方体贴图的特定面渲染到四边形以进行调试。

this类似的问题我收集你使用三维纹理坐标,但我需要一些信息来克服这个障碍。我现在得到的只是黑色,有时完全是不同的颜色。

到目前为止,我已经使用以下内容绘制了2D四边形。

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

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

我需要在这里进行调整吗?这样做的一般方法是什么?

2 个答案:

答案 0 :(得分:2)

需要进行一些更改,这些更改都非常简单。

纹理目标

您当前使用TextureTarget.Texture2D的位置,您将使用TextureTarget.TextureCubeMap。例如,假设您已经有一个给定的立方体贴图纹理:

GL.BindTexture(TextureTarget.TextureCubeMap, textureID);

着色器代码中的采样器类型

在片段着色器中,您当前定义了一个采样器变量,它看起来像这样:

uniform sampler2D Tex;

您将修改为:

uniform samplerCube Tex;

纹理坐标

最大的变化与纹理坐标有关。用于立方体贴图的纹理坐标将具有3个组件,可以将其解释为方向向量。想象与以原点为中心的立方体贴图对应的立方体。然后,纹理坐标给出的方向向量指向将要采样的纹素。

一个选项是修改客户端代码生成的顶点属性中的纹理坐标。您可以将它们扩展为3个组件而不是之前的2个组件,并选择适当的值来隔离要渲染的面。

根据您已经传递给2D纹理的着色器代码的现有纹理坐标,计算新纹理坐标可能更容易。当前纹理坐标在两个坐标方向上跨越范围[0.0,1.0]的正方形时,您需要将该范围映射到多维数据集的面,其中多维数据集以原点为中心,并且具有范围[-1.0,1.0 ]在每个坐标方向上。

要完成此操作,请使用-1.0或1.0作为与要隔离的面匹配的坐标方向,并将输入纹理坐标从范围[0.0,1.0]缩放/移位到范围[-1.0,1.0]其他两个坐标方向。

假设您在2D纹理案例的着色器代码中有以下内容:

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

然后,对于GL_TEXTURE_CUBE_MAP_POSITIVE_X面,使用1.0作为立方体纹理坐标的x坐标,并缩放/移动剩余的两个坐标:

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

等同于GL_TEXTURE_CUBE_MAP_NEGATIVE_X face:

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

对于GL_TEXTURE_CUBE_MAP_POSITIVE_Y脸:

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

对于GL_TEXTURE_CUBE_MAP_NEGATIVE_Y脸:

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

对于GL_TEXTURE_CUBE_MAP_POSITIVE_Z脸:

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

对于GL_TEXTURE_CUBE_MAP_NEGATIVE_Z脸:

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

请注意,立方体贴图面的方向有点模糊。如果您对结果输出的方向有特定的期望,则可能必须置换/镜像上面代码中的某些值。

答案 1 :(得分:0)

在上面Reto的答案的基础上,我想到了以下(伪)代码,用于将立方体贴图的给定面呈现为具有正确方向的四边形。

示例四边形生成器(C):

typedef struct {
    vec3 position;
    vec2 uv;
} shader_quads_vertex;

typedef struct {
    shader_quads_vertex verts[6];
} vert_quad;

// ...

// Don't forget to set vertex attributes correctly
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
    sizeof(shader_quads_vertex), 0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE,
    sizeof(shader_quads_vertex), (void *)sizeof(vec3));

// ...

// v3 *----* v2
//    |    |
// v0 *----* v1
//
// v0, v1, v2, v0, v2, v3

vert_quad quad = { 0 };
quad.verts[0].position.x = x0;  // v0 (0,0)
quad.verts[0].position.y = y0;
quad.verts[0].uv.u = 0.0f;
quad.verts[0].uv.v = 0.0f;
quad.verts[1].position.x = x1;  // v1 (1,0)
quad.verts[1].position.y = y0;
quad.verts[1].uv.u = 1.0f;
quad.verts[1].uv.v = 0.0f;
quad.verts[2].position.x = x1;  // v2 (1,1)
quad.verts[2].position.y = y1;
quad.verts[2].uv.u = 1.0f;
quad.verts[2].uv.v = 1.0f;
quad.verts[3].position.x = x0;  // v0 (0,0)
quad.verts[3].position.y = y0;
quad.verts[3].uv.u = 0.0f;
quad.verts[3].uv.v = 0.0f;
quad.verts[4].position.x = x1;  // v2 (1,1)
quad.verts[4].position.y = y1;
quad.verts[4].uv.u = 1.0f;
quad.verts[4].uv.v = 1.0f;
quad.verts[5].position.x = x0;  // v3 (0,1)
quad.verts[5].position.y = y1;
quad.verts[5].uv.u = 0.0f;
quad.verts[5].uv.v = 1.0f;

// TODO: Bind shader, set uniforms, push quad into your vertex buffer
//glBindBuffer(GL_ARRAY_BUFFER, ...);
//glBufferData(...)

// TODO: Draw 1 face, or 6 faces, or whatever you like. E.g. draw 6 faces:
//for (int face = 0; face < 6; face++) {
//    TODO: Set "u_face" to face index via uniform via glUniform1i()
//    glDrawArrays(GL_TRIANGLES, 0, 6);
//}

顶点着色器(GLSL):

#version 330 core

layout(location = 0) in vec3 attr_position;
layout(location = 1) in vec2 attr_uv;

uniform int u_face;

out vs_out {
    vec3 uvw;
} vertex;

void main()
{
    gl_Position = vec4(attr_position, 1.0);

    vec2 uv_cube = attr_uv.xy * 2.0 - 1.0;

    switch (u_face) {
    case 0:
        vertex.uvw = vec3(1.0, uv_cube.y, uv_cube.x);
        break;
    case 1:
        vertex.uvw = vec3(-1.0, uv_cube.y, -uv_cube.x);
        break;
    case 2:
        vertex.uvw = vec3(uv_cube.x, 1.0, uv_cube.y);
        break;
    case 3:
        vertex.uvw = vec3(uv_cube.x, -1.0, -uv_cube.y);
        break;
    case 4:
        vertex.uvw = vec3(-uv_cube.x, uv_cube.y, 1.0);
        break;
    case 5:
        vertex.uvw = vec3(uv_cube.x, uv_cube.y, -1.0);
        break;
    };
}

碎片着色器(GLSL):

#version 330 core

in vs_out {
    vec3 uvw;
} vertex;

uniform samplerCube u_tex;

out vec4 final_color;

void main()
{
    vec4 tex_color = texture(u_tex, vertex.uvw);
    final_color = vec4(vec3(tex_color.r), 1.0);
}

注意:上面的代码对您的坐标系做出了以下假设:

  • 从四边形的左下角开始,四边形的顶点位置/ UV始终为CCW。
  • 右手坐标(向右+ X,向上+ Y和+ Z从屏幕向观看者显示)
  • u_face被解释为与GL_TEXTURE_CUBE_MAP_POSITIVE_X的偏移量(0:+ X,1:-X,2:+ Y,3:-Y,4:+ Z,5:-Z)
  • + Y呈现为好像向下凝视-Z(向前),然后向后倾斜90度以直视向上(即,对于u_face = 2,“向上”向量为+ Z)
  • -Y呈现为好像向下凝视-Z(向前),然后将头向下倾斜90度以直视向下(即,对于u_face = 3,“向上”向量为-Z)
  • 根据假设2,其他4张脸的向上向量为+ Y

我确实测试了此代码,但是为了简洁起见,我省略了许多样板代码GL代码。希望对您有所帮助。