Tag Archives: Render-to-Texture

VPOS

Starting with DirectX Pixel Shader Model 3.0 there exist an input type called VPOS. It’s the current pixels position on the screen and it’s automatically generated. This can be useful when sampling from a previously rendered texture when rendering an arbitrarily shaped mesh to the screen. To do this, we need uv-coords that represents where to sample on the texture. These coordinates can be gained by simply dividing VPOS with the screen dimensions.
When working with older hardware, that doesn’t support shader model 3.0, there is a need to manually create the VPOS in the vertex shader and pass it to the fragment shader as a TEXCOORD. This is the way to do so ( including the scaling to uv-range which manually has to be done for VPOS if you’re using it).

Vertex Shader:

float4x4 matWorldViewProjection;
float2 fInverseViewportDimensions;
struct VS_INPUT
{
   float4 Position : POSITION0;
};
struct VS_OUTPUT
{
   float4 Position : POSITION0;
   float4 calculatedVPos : TEXCOORD0;
};
float4 ConvertToVPos( float4 p )
{
   return float4( 0.5*( float2(p.x + p.w, p.w - p.y) + p.w*fInverseViewportDimensions.xy), p.zw);
}
 
VS_OUTPUT vs_main( VS_INPUT Input )
{
   VS_OUTPUT Output;
   Output.Position = mul( Input.Position, matWorldViewProjection );
   Output.calculatedVPos = ConvertToVPos(Output.Position);
   return( Output );
}

Pixel Shader:

float4 ps_main(VS_OUTPUT Input) : COLOR0
{
   Input.calculatedVPos /= Input.calculatedVPos.w;
   return float4(Input.calculatedVPos.xy,0,1); // test render it to the screen
}

The image below shows an elephant model rendered with the shader above. As can be seen, the color (red and green channels) correctly represents the uv-coords for a fullscreen quad. Since 0,0,0 = black, 1,0,0 = red, 0,1,0 = green, 1, 1,0 = yellow.

VPOS Elephant
This is how the pixel shader would have looked like if VPOS were used instead (note: no special vertex shader needed in this case).
struct PS_INPUT
{
   float2 vPos : VPOS;
};
float4 ps_main(PS_INPUT Input) : COLOR0
{
   return float4(Input.vPos*fInverseViewportDimensions + fInverseViewportDimensions*0.5,0,1); // test render it to the screen
}

The original code, more info and proof can be found here:
http://www.gamedev.net/community/forums/topic.asp?topic_id=506573

Render Thickness

In [1] they describe a clever way of rendering the thickness of an object in a single pass. The method only correctly works for convex objects but this limitation isn’t that bad, the method can often be used to get the approximated thickness of concave objects as well. For example, [1] uses it to fake the light scattering in clouds rendered as billboards. The methods works like this:

The object is rendered and the distance from the near plane is saved in a color channel R. Also, the distance to the far plane is saved in channel G. By rendering with the blend color mode MIN, one will get the minimum distance from the near plane in R, and the minimum distance to the far plane in G. By using these two distances, one can easily calculate the thickness of the rendered object with the following formula (1-G) – R (if distance is scaled so one is the the distance between the clip planes). Alpha can be saved as well in the same render pass, by outputting it to the A channel. And selecting blend alpha mode ADD (color and alpha can have different modes). This will add up the alpha.

All this is done in only one pass. Just remember to clear to white before rendering.

The image below shows the thickness of the popular Hebe mesh rendered with this method. This model is not convex, and the problem areas are for example the arm holding the bowl. As one can see, the algorithm believes that the bowl and the shoulder are connected, and therefore believes that part of the object is the thickest.

Hebe

[1] The Art and Technology of Whiteout
http://ati.amd.com/developer/gdc/2007/ArtAndTechnologyOfWhiteout(Siggraph07).pdf

Deferred Lighting

This is a lighting technique that lately has increased a lot in popularity. The normal way of shading is to perform the lighting calculations on a fragment when it is rasterized to the screen. This is often good but requires a lot of calculations if there are many lights. And the bad thing is that this fragment might later on be overwritten by some other fragment so the calculations might be a waste.

In deferred lighting (or deferred shading, or deferred rendering), you save the information about the fragment that is necessary to perform the shading (lighting) by rendering them to textures instead of doing the actual lighting calculation. When all geometry is rendered, the lighting will now be calculated only once per pixel on the screen. So no calculations will be wasted. You can perhaps say that it is some sort of a lazy evaluator.

The information saved per fragment is often:

  • position ( or just depth )
  • albedo ( the diffuse texture )
  • normal
  • specular

And these are sometimes also used:

  • shininess
  • material ID (for selecting material behaviour)

When all geometry has been rendered and it’s time to perform the lighting, the lights needs to be represented as something when sent to rasterization. Point lights can be drawn either as spheres or just square billboards. Directional light should be drawn as a full screen rectangle. And spotlights will be cones. Note that this shading technique allows for lights shaped in any form, not just these traditional ones.

The big reason for using deferred rendering is how well it scales with more lights. Another reason that it has increased in popularity lately is how nice it works with new rendering methods like SSAO and depth of field. The problem areas with deferred lighting is transparent objects and multisampling (antialiasing). If the original scene didn’t have per pixel lighting (but instead maybe vertex lightning) on the whole scene then the deferred rendering might be slower than traditional rendering.

Deferred Lightning example

Deferred Lighting example

Explanation of deferred lighting (and source code)
http://www.beyond3d.com/content/articles/19/

Deferred Rendering in S.T.A.L.K.E.R.

Deferred Rendering in S.T.A.L.K.E.R.

Explanation of how deferred lighting was used in the game S.T.A.L.K.E.R.
http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter09.html 

The result of the deferred rendering XNA tutorial

The result of the deferred rendering XNA tutorial

A very good tutorial of how to implement deferred lighting in XNA 2.0. This is good reading even when you are rendering in an other API.
http://www.ziggyware.com/readarticle.php?article_id=155

A long discussion on the gamedev.net forum of pros and cons of using deferred rendering compared to traditional forward rendering.
http://www.gamedev.net/community/forums/topic.asp?topic_id=424979

DirectX9 implementation if deferred shading, and some optimization talk
http://www.gamedev.net/reference/programming/features/shaderx2/Tips_and_Tricks_with_ DirectX_9.pdf

Deferred Lightning in Leadwerk Engine

Deferred Lightning in Leadwerk Engine

Info about the implementation of deferred shading in the Leadwerks Engine.
http://www.leadwerks.com/files/Deferred_Rendering_in_Leadwerks_Engine.pdf

Deferred Lighting in Killzone 2

Deferred Lighting in Killzone 2

A presentation about deferred lighting in the game Killzone 2:
http://www.guerrilla-games.com/publications/dr_kz2_rsx_dev07.pdf

Gaussian Blur Filter Shader

There are different ways to perform blur and this is one of the most common way to do it in a shader. It’s a two step method with first a horizontal blur and then a vertical blur. By splitting the work in two directions (two passes) you can save a lot of computation.

The method can be divided in the following parts:

  1. Render the scene you want to blur to a texture (could be downsampled)
  2. Render a screen aligned quad with the horizontal blur shader to a texture
  3. Render a screen aligned quad with the vertical blur shader to the screen or texture depending on what you want to use it for

The following image shows how the blur works when splitted up in two directions.

Separable blur filter

Here’s the horizontal blur shader.

Vertex Shader (GLSL) . This shader screen align a quad with width 1. Any method to render a screen aligned quad will work. So you’re free to use other shaders.

varying vec2 vTexCoord;
 
// remember that you should draw a screen aligned quad
void main(void)
{
   gl_Position = ftransform();;
  
   // Clean up inaccuracies
   vec2 Pos;
   Pos = sign(gl_Vertex.xy);
 
   gl_Position = vec4(Pos, 0.0, 1.0);
   // Image-space
   vTexCoord = Pos * 0.5 + 0.5;
}

Fragment Shader (GLSL) 

uniform sampler2D RTScene; // the texture with the scene you want to blur
varying vec2 vTexCoord;
 
const float blurSize = 1.0/512.0; // I've chosen this size because this will result in that every step will be one pixel wide if the RTScene texture is of size 512x512
 
void main(void)
{
   vec4 sum = vec4(0.0);
 
   // blur in y (vertical)
   // take nine samples, with the distance blurSize between them
   sum += texture2D(RTScene, vec2(vTexCoord.x - 4.0*blurSize, vTexCoord.y)) * 0.05;
   sum += texture2D(RTScene, vec2(vTexCoord.x - 3.0*blurSize, vTexCoord.y)) * 0.09;
   sum += texture2D(RTScene, vec2(vTexCoord.x - 2.0*blurSize, vTexCoord.y)) * 0.12;
   sum += texture2D(RTScene, vec2(vTexCoord.x - blurSize, vTexCoord.y)) * 0.15;
   sum += texture2D(RTScene, vec2(vTexCoord.x, vTexCoord.y)) * 0.16;
   sum += texture2D(RTScene, vec2(vTexCoord.x + blurSize, vTexCoord.y)) * 0.15;
   sum += texture2D(RTScene, vec2(vTexCoord.x + 2.0*blurSize, vTexCoord.y)) * 0.12;
   sum += texture2D(RTScene, vec2(vTexCoord.x + 3.0*blurSize, vTexCoord.y)) * 0.09;
   sum += texture2D(RTScene, vec2(vTexCoord.x + 4.0*blurSize, vTexCoord.y)) * 0.05;
 
   gl_FragColor = sum;
}

And here’s the vertical blur shader.

Vertex Shader (GLSL) (the same as for the blur in horizontal direction)

varying vec2 vTexCoord;
 
// remember that you should draw a screen aligned quad
void main(void)
{
   gl_Position = ftransform();;
  
   // Clean up inaccuracies
   vec2 Pos;
   Pos = sign(gl_Vertex.xy);
 
   gl_Position = vec4(Pos, 0.0, 1.0);
   // Image-space
   vTexCoord = Pos * 0.5 + 0.5;
}

Fragment Shader (GLSL) 

uniform sampler2D RTBlurH; // this should hold the texture rendered by the horizontal blur pass
varying vec2 vTexCoord;
 
const float blurSize = 1.0/512.0;
 
void main(void)
{
   vec4 sum = vec4(0.0);
 
   // blur in y (vertical)
   // take nine samples, with the distance blurSize between them
   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y - 4.0*blurSize)) * 0.05;
   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y - 3.0*blurSize)) * 0.09;
   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y - 2.0*blurSize)) * 0.12;
   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y - blurSize)) * 0.15;
   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y)) * 0.16;
   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y + blurSize)) * 0.15;
   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y + 2.0*blurSize)) * 0.12;
   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y + 3.0*blurSize)) * 0.09;
   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y + 4.0*blurSize)) * 0.05;
 
   gl_FragColor = sum;
}

And this is a scene without blur.

Scene before bluring

And this is the same scene but with gaussian blur.

Blured Scene

You can tweak the blur radius to change the size of the blur and change the number of samples in each direction.

Cost for separable blur shader : 9+9 = 18 (number of texture samples)
Cost for shader if blured in one pass: 9*9 = 81 (number of texture samples)
So splitting up in two directions saves a lot.

The gaussian weights are calculated accordingly to the gaussian function with standard deviation of 2.7. These calculations were done in the excel document found [2].

Here’s a description of blur shaders and other image processing shaders in DirectX:
[1] http://ati.amd.com/developer/shaderx/ShaderX2_AdvancedImageProcessing.pdf

More info about calculating weights for separable gaussian blur:
[2] http://theinstructionlimit.com/?p=40