2
\$\begingroup\$

I've looked at this technique, but it strongly sounds like he has to do some ugly hacks involving branching in his lighting shaders just to correctly draw a skybox. This seem awfully wasteful.

How can I most efficiently implement a skybox with a renderer that uses deferred shading?

\$\endgroup\$
  • \$\begingroup\$ mhm... I just had an idea... since the skybox is only ever visible in areas where no objects are drawn, could I mask those areas out of the gbuffer using the stencil buffer and then later only light pixels where the stencil test fails and just draw the skybox to pixels where the stencil test succeeds? \$\endgroup\$ – TravisG Aug 29 '13 at 16:51
  • \$\begingroup\$ oh, come to think of it, this probably improves lighting performance a lot as well. brb, implementing \$\endgroup\$ – TravisG Aug 29 '13 at 16:53
  • \$\begingroup\$ Possible duplicate gamedev.stackexchange.com/questions/55462/… \$\endgroup\$ – danijar Aug 29 '13 at 18:57
7
\$\begingroup\$

The skybox should be drawn after all lighting has been done. It's done exactly the same way as in a forward-shaded renderer; it shouldn't go into the G-buffer at all, and the lighting shaders shouldn't need to know anything about it.

I don't think there's any need to fiddle around with the viewport depth range or stencil either. If you use a projection matrix with an infinite far plane, you can just draw it after everything else, using a vertex shader that sets w = 0 before transforming by the view and projection matrices.

Alternatively, if it's just a texture and you don't need to draw actual geometry, you could do it as a postprocess - a full-screen pass, where the vertex shader sets z = w = 1 to draw the full-screen pass at the far plane. Here's the skybox code I use, which takes this approach:

TextureCube<float3> g_texSkybox;

void vsMain(in Vertex i_vtx, out float3 o_vecView : VIEW, out float4 o_posClip : SV_Position)
{
    // Set z = 1 to draw skybox at the back of the depth range
    o_posClip = float4(i_vtx.m_pos.xy, 1.0, 1.0);

    float4 vecView = mul(o_posClip, g_matClipToWorldNoTranslation);
    o_vecView = vecView.xyz / vecView.w;
}

float3 psMain(in float3 i_vecView : VIEW) : SV_Target
{
    return g_texSkybox.Sample(g_ssTrilinearRepeat, i_vecView);
}

Here g_matClipToWorldNoTranslation is the inverse projection matrix, multiplied by the inverse of the rotation part of the view matrix. It converts a point in clip space to the corresponding direction in world space.

\$\endgroup\$
  • \$\begingroup\$ Thanks, this probably works, but I'm sticking with the stencil buffer solution for now. I'm going to use it mainly for a space game where the lighting geometry often overlaps empty areas of space. The stencil buffer solution (as a side effect to drawing the sky box perfectly) enables me to discard a pretty large number of pixels and thus save a lot of performance. \$\endgroup\$ – TravisG Aug 29 '13 at 20:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.