为什么人们不使用四面体作为天空盒?
Why don't people use tetrahedrons for skyboxes?
在 3D 游戏中渲染固定纹理的天空时,人们通常会先在立方体贴图中创建 6 个纹理,然后在相机周围渲染一个立方体。在 GLSL 中,您可以使用法线而不是纹理坐标访问纹理中的像素,并且您可以通过标准化片段相对于相机的位置来轻松获得此法线。然而,这个过程可以用相机周围的任何形状来完成,因为当你标准化每个位置时,它总是会产生一个球体。 现在我想知道:为什么它总是立方体而不是四面体?渲染一个立方体需要 12 个三角形,一个四面体只需要 4 个。正如我已经说过的,任何围绕相机的形状都可以。所以四面体占用更少的 VRAM 并且渲染速度更快,没有任何缺点?为什么不使用它们?
是视野深度和形状的问题
最好的 天空盒 形状是(半)球体,因为它的渲染表面投影到相机 space 几乎没有扭曲。如果您使用任何其他形状,那么投影伪像会出现,尤其是在角上,例如大多数 Apps/Games 使用立方体 天空盒 。查看具有有限半径(不仅仅是单个点)的太阳并旋转视图,使太阳从视图的中间到侧面。然后通常太阳会从 circular/disc 形状变形为 elliptic/oval 形状:
这是由于 skybox 和 camera 之间的距离变化所致。如果将其与直接渲染的星星进行比较:
那你就可以看出区别了。第一张图片是 google 找到的第一张相关图片(来自某些游戏),第二张是我认为 Space 工程师的屏幕截图,最后一张是我的 astro 应用程序渲染的
所以形状离球体越远,变形就越多。
使用 4 面金字塔比立方体更糟糕,因为边之间的角度更差,会产生更大的伪影。另一个问题是你需要更大尺寸的金字塔来覆盖相同的 space。如果在 skybox 渲染期间出于某种目的使用 Depth Buffer,则可以通过增加 Z_far[= 来显着影响精度59=]平面.
开销
6 和 4 个多边形之间的差异并不大,因为 skybox 很大(覆盖整个视图)速度主要由 pixel/texel 字段的数量决定筛选不是顶点数。所以金字塔可能比立方体更慢,因为它需要有更大的面(需要更多的插值器迭代)。但是如果你想使用球形 skybox 那么你还需要球形纹理,因为如果你使用标准立方体纹理,失真仍然存在并且这些更难维护创建,...这就是立方体被更多使用的原因。
球形天空盒
他们需要不同类型的纹理。半球形纹理看起来像这样:
您根本不需要一些环境几何。您需要做的就是绘制一个 全屏四边形 ,然后为其计算正确的纹理坐标。现在有了现代 GL,我们甚至不需要为此提供顶点数据,我们可以使用 attributless 渲染:
顶点着色器:
#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)
.
这种方法当然可以用于球形纹理贴图而不是立方体贴图
在 3D 游戏中渲染固定纹理的天空时,人们通常会先在立方体贴图中创建 6 个纹理,然后在相机周围渲染一个立方体。在 GLSL 中,您可以使用法线而不是纹理坐标访问纹理中的像素,并且您可以通过标准化片段相对于相机的位置来轻松获得此法线。然而,这个过程可以用相机周围的任何形状来完成,因为当你标准化每个位置时,它总是会产生一个球体。 现在我想知道:为什么它总是立方体而不是四面体?渲染一个立方体需要 12 个三角形,一个四面体只需要 4 个。正如我已经说过的,任何围绕相机的形状都可以。所以四面体占用更少的 VRAM 并且渲染速度更快,没有任何缺点?为什么不使用它们?
是视野深度和形状的问题
最好的 天空盒 形状是(半)球体,因为它的渲染表面投影到相机 space 几乎没有扭曲。如果您使用任何其他形状,那么投影伪像会出现,尤其是在角上,例如大多数 Apps/Games 使用立方体 天空盒 。查看具有有限半径(不仅仅是单个点)的太阳并旋转视图,使太阳从视图的中间到侧面。然后通常太阳会从 circular/disc 形状变形为 elliptic/oval 形状:
这是由于 skybox 和 camera 之间的距离变化所致。如果将其与直接渲染的星星进行比较:
那你就可以看出区别了。第一张图片是 google 找到的第一张相关图片(来自某些游戏),第二张是我认为 Space 工程师的屏幕截图,最后一张是我的 astro 应用程序渲染的
所以形状离球体越远,变形就越多。
使用 4 面金字塔比立方体更糟糕,因为边之间的角度更差,会产生更大的伪影。另一个问题是你需要更大尺寸的金字塔来覆盖相同的 space。如果在 skybox 渲染期间出于某种目的使用 Depth Buffer,则可以通过增加 Z_far[= 来显着影响精度59=]平面.
开销
6 和 4 个多边形之间的差异并不大,因为 skybox 很大(覆盖整个视图)速度主要由 pixel/texel 字段的数量决定筛选不是顶点数。所以金字塔可能比立方体更慢,因为它需要有更大的面(需要更多的插值器迭代)。但是如果你想使用球形 skybox 那么你还需要球形纹理,因为如果你使用标准立方体纹理,失真仍然存在并且这些更难维护创建,...这就是立方体被更多使用的原因。
球形天空盒
他们需要不同类型的纹理。半球形纹理看起来像这样:
您根本不需要一些环境几何。您需要做的就是绘制一个 全屏四边形 ,然后为其计算正确的纹理坐标。现在有了现代 GL,我们甚至不需要为此提供顶点数据,我们可以使用 attributless 渲染:
顶点着色器:
#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)
.
这种方法当然可以用于球形纹理贴图而不是立方体贴图