Tutorial 20 HLSL Basic post

September 15th, 2010

No Comments

HLSL Basic Post effects

Everyone will have seen black and white films/pictures, sepia pictures, pictures or movies with film grain etc. To get such an effect in the 3D world, we normally use a technique called post-effects. This means we apply some kind of filter after (post) rendering. This means we don’t have to do the effect on every object that’s hidden, we only apply the filter on the pixels that are actually visible. This can be really usefull and good, especially when adding some more calculation heavy stuff.


What do we need?

- Of course we will need a shader, just as in the previous tutorial.

- We also need to grab a picture of everything that just has been rendered. There are multiple ways of doing this, but right now we’ll use the easiest way of doing it: A render surface aka rendertarget. This is basically a texture that you can render to, when you tell it to be the texture to render to of course.

How do we set things up?

- We will create the render surface first. This is pretty much the same as creating a mesh, but there are some more parameters this time.

screen = Scene.CreateRenderSurface(1024, 1024, true, CONST_TV_RENDERSURFACEFORMAT.TV_TEXTUREFORMAT_A8R8G8B8, "useless_string_name_as_you_really_should_keep_track_of_classes_like_this_yourself");

First of all there are the width and height of the texture. You can set those to the screen size, but a lot of older graphics cards can only do power of 2 textures. That’s why we chose to round up to that any way, so it’s 1024×1024.
The second thing is the format of the render surface. You can choose to not have an alpha channel or just a red channel, or anything else, based on what the enum contains. In this case we choose an A8R8G8B8 format, which means every channel (alpha, red, green blue) has 8 bits to store its information in. That means 256 possibilities per channel. This is pretty much the default format, except for HDR scenarios, which we will not cover now.
The last parameter is a string to name it, which is once again not recommended, you should just keep track of such things yourself.

- We also need to tell our shader that the texture to read from should be the render surface’ texture.

Shader.SetEffectParamTexture("SceneTexture", screen.GetTexture());

It’s as easy as that. The first parameter is the name of the variable in the shader, the second parameter is the ID to the texture.

How do we render this?

- The rendering is moved from within the TV.Clear() and TV.RenderToScreen() to another spot:

screen.SetNewCamera(Cam);
screen.StartRender(false);
Cube.Render();//Render the cube.
screen.EndRender();

First we need to tell the render surface which camera we are using.
Then we’ll tell it to start rendering, clearing both the depth as well as the color buffer (if we’d set true, it would only clear the depth buffer and keep the color of the texture that was already there).
Now we render the cube and end rendering with the render surface. This is all fairly easy.

- Now we’ll somehow need to draw the shader fullscreen with the just rendered texture. To do this, we call:

Screen2D.Action_Begin2D();
Screen2D.Draw_FullscreenQuadWithShader(Shader, 0, 0, 1, 1, 0, 0);
Screen2D.Action_End2D();

The most important thing is the Draw_FullscreenQuadWithShader method. This method will draw the shader full screen (should be named full viewport actually).
The first parameter is the shader which the fullscreeen quad (or plane) should be rendered with.
The next 4 parameters are the texture coordinates of the fullscreen plane. In this case, we’ll use the complete texture.
The last 2 parameters are 2 optional texture channels. You could pass texture id’s with it, to supply the textures, but in this case we already did this ourselves.

- Now what does the shader look like?
The vertex program is the same as the one from the object, but we don’t transform it to viewspace, because the information will be passed on correctly by the Draw_FullscreenQuadWithShader:

void vs( in a2v IN, out v2p OUT )
{
    OUT.Position    = IN.Position;
    OUT.UV          = IN.UV;
}

The fragment program (or pixel shader) is not much different either:

void ps( in v2p IN, out p2s OUT )
{
    float4  Scene     = tex2D(ScreenSampler, IN.UV);

    OUT.Colour        = Scene;
    OUT.Colour.a      = 1;
}

Note that we’ve set the colour (tutorials were written by a brit :) ) alpha to 1, which means it will be 100% opaque.

Things you can add/change yourself

- Try to find a way to make a sepia or black white shader, it will help you understand HLSL and fullscreen drawing a bit more.

Reply