When rendering a sky with a fixed texture in 3D games, people often create 6 textures in a cube map first, and then render a cube around the camera. In GLSL, you can access the pixels in the textures with a normal instead of a texture coordinate, and you can easily get this normal by normalizing the fragment position relative to the camera. However, this process can be done with any shape that surrounds the camera, because when you normalize each position it will always result in a sphere. Now I'm wondering: Why is it always a cube and not a tetrahedron? Rendering a cube takes 12 triangles, a tetrahedron only 4. And as I already said, any shape that surrounds the camera works. So tetrahedrons take less VRAM and are faster to render, without any downsides? Why not use them?
问题:
回答1:
You don't need some environment geometry at all. All you need to do is drawing a full screen quad, and just compute the correct texture coordinates for it. Now with modern GL, we don't even need to supply vertex data for this, we can use attributless rendering:
Vertex Shader:
#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);
}
where invPV
is inverse(Projection*View)
, so it will take your camera orientation as well as the projection into account. This can in principle be eithen further simplyfied, depending on how much constraints you can put on the projection matrix.
Fragment Shader:
#version 330 core
in vec3 dir;
out color;
uniform samplerCube uTexEnv;
void main()
{
color=texture(uTexEnv, dir);
}
To use this, you simply need to bind an empty VAO and your texture, upload your invPV
matrix and call glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
.
This approach could of course be used for spherical texture mapping instead of cube maps
回答2:
it is a question of the view depth and shape
the best skybox shape is (half)sphere because its rendered surface is projecting to camera space almost without distortions. If you use any other shape then projection artifacts will occur especially on corners for example most Apps/Games use cube skybox. Look at sun with finite radius (not just single dot) and rotate the view so sun gets from middle to side of view. Then usually the sun gets distorted from circular/disc shape to elliptic/oval shape:
this is due to changing distance between skybox and camera. If you compare it to directly rendered star:
then you can see the difference. First image is the first relevant image found by google (from some game) the second is screen shot form Space Engineers I think and the last is rendered by mine astro app see
- realistic n-body solar system
So the more is the shape far from sphere the more distortions you get.
using 4-sided pyramid is even worse then cube because the angles between sides are worse creating even bigger artifacts. Another problem is you need bigger size of pyramid to cover the same space. If you use Depth Buffer for some purpose during skybox rendering you could significantly affect precision by increasing Z_far plane.
Overhead
diference between 6 and 4 polygons is not that much because the skybox is huge (cover whole view) the speed is determined mostly by the count of pixel/texel filed to screen not the vertices count. So the pyramid could be even slower then cube because it needs to have bigger faces (more interpolator iterations are needed). But if you want to use spherical skybox then you need also spherical texture because if you would use the standard cube texture the distortion will be still present and these are harder to maintain create,... and that is why cubes are more used.
Spherical skyboxes
they need different type of texture. Hemispherical texture looks like this: