Deferred Rendering

(revisited)

 

DevTricks

Tyler Howard

What is Deferred Rendering?

  • A technique used to optimize lighting in 3D graphics!

"Revisited?"

  • I had a previous DevTricks presentation that went over a brief history of lighting algorithms. Each technique was only given a few minutes' summary, at best. This is a closer look at one.

Why pick Deferred Rendering?

  • Even though it's an old technique (paper in 1988, first shipped game with technique in 2001), I think it's the biggest leap that made real-time lighting feasible for consumer use.

Why use Deferred Rendering?

  • Deferred Rendering is (generally speaking) a performance improvement over its predecessor, Forward Rendering.
  • Forward Rendering takes the naive approach and lights everything -- while it's "correct," this has overdraw -- a lit pixel can be overwritten by another pixel, wasting work.
  • Deferred Rendering builds a composite screen and only lights one pixel per pixel, with no wasted work.

Comparison

Forward:

Each light is applied directly to each objects' first and only render to screen

Cost is roughly m x n lighting calculations

m objects

n lights

Comparison

Deferred:

m objects

n lights

Composite step

(merge objects into single object, kinda)

Cost is roughly m + n lighting calculations

What does that imply?

Deferred rendering scales much better as you add more objects or lights, by orders of magnitude.

 

It's possible to have many more objects and lights in a scene and still be able to meet target framerates between 24-60 fps.

What was that composite step?

  • We're taking an object and rendering information about it into various buffers, saving ("deferring") the lighting for the last step.
  • These buffers are usually:
    • diffuse (color inherent to the object)
    • depth (distance from camera)
    • normal (direction that pixel is facing)
  • All these buffers combined form a new 3D object that light calculations can be applied to, like the push-pin art analogy from earlier

Final render of scene:

Working backwards:

Depth

Working backwards:

Normal

Working backwards:

Color

<script id="pass_fs" type="x-shader/x-fragment">
    #extension GL_EXT_draw_buffers : require
    precision highp float;
    uniform vec3 u_Color;
    //in
    varying vec3 fs_Normal;
    varying vec4 fs_Position;
    varying vec2 fs_Texcoord;
    varying float fs_Depth;
    uniform sampler2D u_Texture;
    void main(void)
    {
        //normal, position, depth, color
        gl_FragData[0] = vec4(vec3(fs_Depth), 1.0);
        gl_FragData[1] = vec4(normalize(fs_Normal.xyz), 1.0);
        gl_FragData[2] = fs_Position;
        gl_FragData[3] = vec4(texture2D(u_Texture, fs_Texcoord).xyz, 1.0);
    }
</script>

Making the composite -- rendering to multiple 2D textures at once

=

240 lights applied to composite image/geometry buffer

~58 fps

Applying lights to scene -- for each light

uniform sampler2D u_Depthtex;
uniform sampler2D u_Normaltex;
uniform sampler2D u_Positiontex;
uniform sampler2D u_Colortex;
uniform vec4 u_Light;
uniform vec3 u_LightColor;
in vec2 fs_Texcoord;
out vec4 fragmentColor;

void main()
{
    vec3 normal = texture(u_Normaltex, fs_Texcoord).xyz;
    vec3 position = texture(u_Positiontex, fs_Texcoord).xyz;
    vec3 color = texture(u_Colortex, fs_Texcoord).xyz;
    vec3 light = u_Light.xyz;
    float lightRadius = u_Light.w;

    if(distance(light, position) < lightRadius)
    {
        float dist = distance(light, position);
        float lightR = lightRadius;
        float attenuation = dist/(1.0 - (dist/lightR) * (dist/lightR));
        attenuation = attenuation / lightR + 1.0;
        attenuation = 1.0 / (attenuation * attenuation);
        float diffuse = abs(dot(normal, normalize(light - position)));
        fragmentColor = vec4(diffuse * u_LightColor * color * attenuation, 1.0);
    }
    else
        fragmentColor = vec4(0.0,0.0,0.0,1.0);
}

Live Demo & Code

 

(someone else's implementation, so we're clear)

 

http://tiansijie.github.io/Tile_Based_WebGL_DeferredShader/

 

 https://github.com/tiansijie/Tile_Based_WebGL_DeferredShader

 

(pertinent shader code lives in index.html <script type="x-shader/(x-fragment or x-vertex)"> tags)

Questions?

Deferred Rendering

By tdhoward

Deferred Rendering

  • 568