在 3D 游戏中渲染具有固定纹理的天空时,人们通常会先在一个立方体贴图中创建 6 个纹理,然后在相机周围渲染一个立方体。在 GLSL 中,您可以使用法线而不是纹理坐标来访问纹理中的像素,并且您可以通过标准化片段相对于相机的位置来轻松获得此法线。然而,这个过程可以用围绕相机的任何形状来完成,因为当你标准化每个位置时,它总是会产生一个球体。现在我想知道:为什么它总是一个立方体而不是一个四面体?渲染一个立方体需要 12 个三角形,一个四面体只有 4 个。正如我已经说过的,任何围绕相机的形状都可以。所以四面体占用更少的 VRAM 并且渲染速度更快,没有任何缺点?为什么不使用它们?
2 回答
你根本不需要一些环境几何。您需要做的就是绘制一个全屏四边形,并为其计算正确的纹理坐标。现在有了现代 GL,我们甚至不需要为此提供顶点数据,我们可以使用无属性渲染:
顶点着色器:
#version 330 core
out vec3 dir;
uniform mat4 invPV;
void main()
{
vec2 pos = vec2( (gl_VertexID & 2)>>1, 1 - (gl_VertexID & 1)) * 2.0 - 1.0;
vec4 front= invPV * vec4(pos, -1.0, 1.0);
vec4 back = invPV * vec4(pos, 1.0, 1.0);
dir=back.xyz / back.w - front.xyz / front.w;
gl_Position = vec4(pos,1.0,1.0);
}
在哪里invPV
,inverse(Projection*View)
因此它将考虑您的相机方向以及投影。原则上,这可以进一步简化,具体取决于您可以对投影矩阵施加多少约束。
片段着色器:
#version 330 core
in vec3 dir;
out color;
uniform samplerCube uTexEnv;
void main()
{
color=texture(uTexEnv, dir);
}
要使用它,您只需绑定一个空的 VAO 和您的纹理,上传您的invPV
矩阵并调用glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
.
这种方法当然可以用于球形纹理映射而不是立方体贴图
这是视图深度和形状的问题
最好的天空盒形状是(半)球体,因为它的渲染表面几乎没有变形地投影到相机空间。如果您使用任何其他形状,则会出现投影伪影,尤其是在角落,例如大多数应用程序/游戏使用立方体天空盒。以有限的半径(不仅仅是单个点)查看太阳并旋转视图,使太阳从视图的中间到侧面。然后通常太阳会从圆形/圆盘形扭曲为椭圆形/椭圆形:
这是由于天空盒和相机之间的距离发生了变化。如果将其与直接渲染的星星进行比较:
然后你就可以看到区别了。第一张图片是谷歌找到的第一张相关图片(来自某些游戏),第二张是我认为 Space Engineers 的屏幕截图,最后一张是由我的 astro 应用程序渲染的,请参见
因此,远离球体的形状越多,您得到的扭曲就越多。
使用 4 面金字塔比立方体更糟糕,因为边之间的角度更糟,会产生更大的伪影。另一个问题是您需要更大尺寸的金字塔来覆盖相同的空间。如果您在天空盒渲染期间出于某种目的使用深度缓冲区,您可能会通过增加Z_far平面显着影响精度。
高架
6 和 4 个多边形之间的差异并没有那么大,因为天空盒很大(覆盖整个视图),速度主要取决于提交给屏幕的像素/纹素的数量而不是顶点数量。所以金字塔可能比立方体更慢,因为它需要更大的面(需要更多的插值器迭代)。但是,如果您想使用球形天空盒,那么您还需要球形纹理,因为如果您使用标准立方体纹理,失真仍然存在,并且这些更难以维持创建......这就是为什么更多使用立方体的原因。
球形天空盒
他们需要不同类型的纹理。半球形纹理如下所示: