Skybox without a box, Skysphere without a sphere
This is a technique for rendering a sky without using a box or (hemi)sphere model to do so. Instead it uses a full screen quad and the bounding frustum corners to fire a frustum ray into a cube map texture. A handy trick with this method is the moveToFarPlane parameter on the vertex shader, when set to true and using depth-stencil states set how you would normally set them up for rendering 3d objects, the sky rendering can come after all opaque objects are rendered, saving a good amount of pixel processing time since it's not often the entire sky is visible in a game.
Generating required data about the frustum:
- assumes you have a view and projection matrix.
- also assumes you have camera orientation stored in a quaternion, if you store it in a matrix simply replace xform with that orientation matrix.
// update the bounding frustum Matrix matViewProjection; Matrix.Multiply(ref matView, ref matProjection, out matViewProjection); boundingFrustum.Matrix = matViewProjection; // create a reverse rotation matrix for camera frustum corners Matrix xform; Matrix.CreateFromQuaternion(ref camWorldOrientation, out xform); Matrix.Multiply(ref matView, ref xform, out xform); // fetch the frustum corners and reverse the camera rotation Vector3[] frustumCorners = new Vector3[8]; boundingFrustum.GetCorners(frustumCorners); Vector3.Transform(frustumCorners, 4, ref xform, frustumCorners, 0, 4); // switch the bottom two corners for left to right ordering Vector3 temp = frustumCorners[3]; frustumCorners[3] = frustumCorners[2]; frustumCorners[2] = temp;
That's all there is for supporting code, now to the sky portion. The below assumes you have a way to generate a full screen quad, if you do not refer to the Shape Generation page for a function that will do that for you.
Shader for rendering the sky:
// =============================================================================================
// Parameters
// =============================================================================================
float3 FrustumCorners[4];
// =============================================================================================
// Functions
// =============================================================================================
// interpolates the frustum corners to generate a ray that starts at the camera and ends at
// the far clip plane of the bounding frustum
#define GetFrustumRay(texCoord) \
lerp(lerp(FrustumCorners[0], FrustumCorners[1], ##texCoord.x), \
lerp(FrustumCorners[2], FrustumCorners[3], ##texCoord.x), \
##texCoord.y)
// =============================================================================================
// Textures & Samplers
// =============================================================================================
texture CubeMap;
sampler CubeMapSampler = sampler_state { Texture = <CubeMap>; }
// =============================================================================================
// Vertex Shaders
// =============================================================================================
void VS_Passive(inout float4 position : POSITION0,
inout float2 texCoord : TEXCOORD0,
uniform bool moveToFarPlane)
{
if (moveToFarPlane)
position.zw = 1;
}
// =============================================================================================
// Pixel Shaders
// =============================================================================================
float4 PS_Sky(float2 texCoord : TEXCOORD0) : COLOR0
{
float3 frustumRay = GetFrustumRay(texCoord);
return texCUBE(CubeMapSampler, frustumRay);
}
// =============================================================================================
// Techniques
// =============================================================================================
technique Sky
{
pass Sky
{
VertexShader = compile vs_2_0 VS_Passive(true);
PixelShader = compile ps_2_0 PS_Sky();
}
}