在视图空间中的OpenGL立方体贴图反射不正确。

4

我在跟随这篇教程,成功在场景中添加了一个立方体贴图。接着我尝试给我的物体增加反射效果,但和教程不同的是,我在视图空间下编写了GLSL代码。然而,反射看起来有些奇怪。无论你面对什么角度,它们总是反映着同一面,就像我这里一样,你总是能在反射的物体上看到一个岩石,但是这个岩石只在我的立方体贴图的一侧。 这是一个展示效果的视频:

我尝试了其他形状的物体,例如立方体,效果都是一样的。我还发现了这本书,它展示了一个视图空间反射的例子,我似乎在做类似的事情,但仍然无法得到期望的效果。 我的顶点着色器代码:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 aTexCoord;

uniform mat4 Model;
uniform mat4 View;
uniform mat4 Projection;

out vec2 TexCoord;
out vec3 aNormal;
out vec3 FragPos;

void main()
{
    aNormal = mat3(transpose(inverse(View * Model))) * normal; 
    FragPos = vec3(View * Model * vec4(aPos,1.0));
    gl_Position = Projection * vec4(FragPos,1.0);
    TexCoord = aTexCoord;
}

我的顶点着色器代码:

#version 330 core

out vec4 FragColor;

in vec3 FragPos;
in vec3 aNormal;

uniform samplerCube skybox;


void main(){
    vec3 I = normalize(FragPos);
    vec3 R = reflect(I,normalize(aNormal));
    FragColor = vec4(texture(skybox,R).rgb,1.0);
}

"我在视图空间中编写了我的GLSL代码" - 那么教程是在世界空间中进行计算吗?这意味着您需要将反射向量(R)从视图空间转换到世界空间。 - Rabbid76
反射点到正确方向的结果向量不应该是正确的吗?即使所有计算都在视图空间中进行?如果不使用相机位置,我该如何转换回世界空间?@Rabbid76 - Hirosam
1
如果Inormal在视图空间中,则R也在视图空间中。reflect不会神奇地改变系统。相机位置被编码在视图矩阵中。但由于R是一个向量而不是一个位置,相机的位置是无关紧要的(只有方向很重要)。 - Rabbid76
2个回答

9

由于你在片段着色器中进行计算,所以反射向量 (R) 也是一个视图空间的向量。而立方体贴图 (skybox) 则代表了环境在世界空间中的映射。你需要将 R 从视图空间变换到世界空间。这可以通过逆视图矩阵来实现。逆矩阵可以通过 GLSL 内置函数 inverse 来计算:

#version 330 core

out vec4 FragColor;

in vec3 FragPos;
in vec3 aNormal;

uniform samplerCube skybox;
uniform mat4 View;

void main() {
    vec3 I      = normalize(FragPos);
    vec3 viewR  = reflect(I, normalize(aNormal));
    vec3 worldR = inverse(mat3(View)) * viewR;
    FragColor   = vec4(texture(skybox, worldR).rgb, 1.0);
}

注意,视图矩阵将物体从世界空间转换到视图空间,而逆视图矩阵将物体从视图空间转换回世界空间。详情请参见 可逆矩阵

2
这是一个晚回答,但我想提供额外的信息,解释为什么它会表现出这样的行为。
想象一下你的反射物体只是一个六面体。每个面可以看作是一个镜子。现在,因为你在视图空间中,从你的视点可见的那个镜面平面的每个坐标都具有负的z值。让我们看看正好在中心的点。这个向量看起来像 (0, 0, -z),因为立方体的侧面就像一个镜子,它将被直接反射回给你 (0, 0, +z)。所以你最终从你的立方体贴图的GL_TEXTURE_CUBE_MAP_POSITIVE_Z采样。 在着色器代码中,它看起来像:
vec3 V     = normalize(-frag_pos_view_space); // vector from fragment to view point (0,0,0) in view space
vec3 R     = reflect(-V, N);                  // invert V because reflect expects incident vector
vec3 color = texture(skybox, R).xyz;

现在,让我们移动到立方体的另一侧并查看那个镜面。在视图空间中,您正在查看的坐标仍然是(0,0,-z)位于中心,将被反射并绕法线返回给您,因此反射向量再次看起来像(0,0,+z)。这意味着即使您在立方体的另一侧,您也将在立方体贴图中采样相同的面。
所以你需要做的就是使用视图矩阵的逆矩阵回到世界空间。如果您还应用了旋转来呈现天空盒本身,您甚至需要使用用于转换天空盒的模型矩阵的逆矩阵来转换您的反射向量,否则反射仍将错误。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接