c++ - OpenGL反射未正确映射天空盒
问题描述
我试图在不使用 LearnOpenGL 方法的情况下学习 OpenGL 上的反射,但我无法弄清楚反射有什么问题。在相机视图的某些部分似乎发生了某种扭曲和翻转。到目前为止,我只是使用视图矩阵来简化代码。
这就是球体反射的样子。
我也尝试使用立方体。即使我的鼠标继续向右移动,反射也会扭曲,然后最终显示扭曲之前存在的图像。
立方体反射
天空盒图像
这是天空盒顶点着色器
#version 330 core
layout (location = 0) in vec3 aPos;
out vec3 o_pos;
void main()
{
o_pos = aPos;
gl_Position = vec4(aPos, 1.0);
}
这是天空盒片段着色器。我将视图矩阵直接传递给片段着色器。
#version 330 core
out vec4 FragColor;
in vec3 o_pos;
uniform vec3 view_up;
uniform vec3 view_right;
uniform vec3 view_forward;
uniform samplerCube cubeTex;
void main()
{
vec3 pv = mat3(view_right, view_up, view_forward) * vec3(o_pos.xy, 1.0);
FragColor = vec4(texture(cubeTex, pv).rgb, 1.0);
}
这是对象顶点着色器
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 normal;
out vec3 o_normal;
out vec3 o_pos;
void main()
{
o_pos = aPos;
o_normal = normal;
gl_Position = vec4(o_pos, 1.0f);
}
这是对象片段着色器。它类似于 learnopengl 着色器。
#version 330 core
in vec3 o_pos;
in vec3 o_normal;
out vec4 fragColor;
uniform vec3 eye_pos;
uniform samplerCube cubeTex;
void main()
{
vec3 incident = normalize(o_pos - eye_pos);
vec3 reflection = reflect(incident, normalize(o_normal));
fragColor = vec4(texture(cubeTex, reflection).rgb, 1.0);
}
这是渲染循环
while (!glfwWindowShouldClose(window))
{
// input
processInput(window);
// render
glClearColor(0.9f, 0.8f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// change view using mouse input
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
ypos = (ypos/SCR_HEIGHT) * PI * PI * 0.5;
xpos = (xpos/SCR_WIDTH) * 2 * PI - PI;
if (ypos < PI/2) ypos = 1.6f;
if (ypos > 3*PI/2) ypos = 4.7f;
eye_forward = normalize(glm::vec3{(GLfloat)(cos(ypos) * cos(xpos)),
(GLfloat)sin(ypos),
(GLfloat)(cos(ypos) * sin(xpos))});
eye_right = glm::normalize(glm::cross(eye_forward, glm::vec3{0.0, 1.0, 0.0}));
eye_up = glm::cross(eye_right, eye_forward);
// draw skybox
glUseProgram(shaderProgram);
glUniform1i(u_cubeTex, 0);
glUniform3fv(u_view_up, 1, (GLfloat*)&eye_up);
glUniform3fv(u_view_right, 1, (GLfloat*)&eye_right);
glUniform3fv(u_view_forward, 1, (GLfloat*)&eye_forward);
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLE_STRIP, 0, cube.size());
// draw UV sphere
glUseProgram(sphereProgram);
glUniform1i(u_cubeTex2, 0);
glUniform3fv(u_eye_pos, 1, (GLfloat*)&eye_forward);
glBindVertexArray(sphereVAO);
glDrawArrays(GL_TRIANGLE, 0, sphere.size());
glfwSwapBuffers(window);
glfwPollEvents();
}
解决方案
eye_pos
是世界空间中的一个位置。
o_pos
是视图空间、剪辑空间和规范化设备空间中的位置。这在您的情况下都是一样的,因为您没有投影矩阵,并且顶点坐标属性直接分配给顶点着色器中的剪辑空间位置:
gl_Position = vec4(aPos, 1.0);
无论如何,计算从世界空间位置到视图空间位置的向量没有任何意义,就像你这样做:
vec3 incident = normalize(o_pos - eye_pos);
视空间中的眼睛位置为 (0, 0, 0)。所以视图空间中的事件视图向量为:
vec3 incident = normalize(o_pos - vec3(0.0, 0.0, 0.0));
这个向量必须从视图空间转换到世界空间,这可以通过视图 ( mat3(view_right, view_up, view_forward)
) 的方向矩阵来实现,就像在天空盒片段着色器中一样:
vec3 incident = mat3(view_right, view_up, view_forward) * normalize(o_pos);
法线向量o_normal
也是视图空间向量。所以它必须转化为世界空间:
vec3 wordlNormal = mat3(view_right, view_up, view_forward) * normalize(o_normal);
除了变换入射矢量和法线矢量,还可以变换反射后的结果矢量。
您必须更改对象片段着色器才能解决您的问题:
#version 330 core
in vec3 o_pos;
in vec3 o_normal;
out vec4 fragColor;
uniform vec3 view_up;
uniform vec3 view_right;
uniform vec3 view_forward;
uniform samplerCube cubeTex;
void main()
{
mat3 invNormalM = mat3(view_right, view_up, view_forward);
vec3 viewIncident = normalize(o_pos);
vec3 viewNormal = normalize(o_normal);
vec3 worldReflection = invNormalM * reflect(viewIncident, viewNormal);
fragColor = vec4(texture(cubeTex, worldReflection).rgb, 1.0);
}
我将视图矩阵直接传递给片段着色器。
mat3(view_right, view_up, view_forward)
不是视图矩阵。它是相机的方向矩阵。所以它是逆视图(正常)矩阵。
推荐阅读
- cmake - 有没有办法复制现有库但能够修改新库?
- kubernetes - 如何通过在golang中传递像kubectl这样的yaml文件来创建kubernetes对象
- python - Python在Outlook中发送带有签名的电子邮件
- java - 提供文件时未正确获取文件扩展名
- python - 从数据框中创建具有条件逻辑的变量
- javascript - 如何将多个文件选择放入数组中以输出
- javascript - 指定属性返回的js递归函数
- python - NotImplementedError:不支持板
- java - 在tomcat服务器中运行war文件时出错
- react-native - 如何将 iOS 本地 URI 转换为路径?