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\$
3
  • \$\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\$ Commented Aug 29, 2013 at 16:51
  • \$\begingroup\$ oh, come to think of it, this probably improves lighting performance a lot as well. brb, implementing \$\endgroup\$ Commented Aug 29, 2013 at 16:53
  • \$\begingroup\$ Possible duplicate gamedev.stackexchange.com/questions/55462/… \$\endgroup\$ Commented Aug 29, 2013 at 18:57

1 Answer 1

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\$
4
  • \$\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\$ Commented Aug 29, 2013 at 20:51
  • \$\begingroup\$ If you draw a skybox after lighting, is it still possible to do environment mapping? \$\endgroup\$ Commented Nov 4, 2024 at 9:46
  • 1
    \$\begingroup\$ @MarkIngram Yes, as long as you can sample the skybox texture from your lighting shader, you can do environment mapping. That's independent of when the skybox is drawn to the main framebuffer. \$\endgroup\$ Commented Nov 5, 2024 at 19:02
  • \$\begingroup\$ Thank you @NathanReed \$\endgroup\$ Commented Nov 9, 2024 at 9:10

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.