racer home

Shader engine - tutorial

 

Home Go for the GT5 look using shaders!


Dolphinity Organiser - free planning, project management and organizing software for all your action lists

 

Introduction

Older Racer versions used a couple of hard-defined materials for you to use; some colors, specular settings and whether reflections were on or off. For a lot of materials, this is enough for a nice look.

However, if you want to add better looks to your models, you will have to go beyond the regular 3D modeling and use shader (.shd) files. These give more precise control over how materials look (a material and a shader are really the same thing). With shaders, you can use multiple textures to give your materials just the look you need. This gives you the opportunity to define how YOU want to do reflections, and to add special effects that hadn't come up in my mind yet!

Racer v0.9 goes one step further - it requires shaders to be defined for all used materials. Also, it requires GPU shaders (in Cg) to be defined for each shader. Cg shaders give much improved lighting, as well as provide the basis for better atmosphere support, shadowmapping and such. Without Cg, this is not possible. Learn more about GPU shaders here. The shaders below are more targeted at the older, non-Cg usage. With Cg shaders, you mostly just define a few layers with textures and a vertex and fragment shader.

There is a reference guide for Racer shaders here, which contains more detailed descriptions of the features described below.

Shading essentials - first steps

Shaders try to mimick materials that exist in real-life. The Cg shader files behind them (which are in data/renderer/shaders) model light to get a balance between performance and realistic lighting. Shaders use these Cg files and define parameters for the Cg shaders to vary the looks a bit. A shader consists of:

Lighting models

Simulating lighting in Cg is a never-ending struggle to keep high-performance, but still to give realistic lighting. In the real world, lighting can be divided up in 2 types:

In the above list, note a conversion of energy rule; there are only so many photons coming in from the source light. What is being sent back can never be more than what came in. This means that the more diffuse light you have, the less specular you will have. A perfectly specular billiard ball cannot have any diffuse color (although such a thing doesn't exist in real life). The formula is roughly: incoming_light = diffuse_light+specular_light+heat. Heat is generated in real life as photons don't get reflected (not something that is simulated in Racer). But remember this when definining materials; if you define diffuse=1 1 1 and specular=1 1 1, you're really generating light energy. This violates the laws of physics and your shader will look 'off'.

To help with these issues, a console command 'analyse shaders' is available that searches for anomalies in the shader definitions. It reports back with any issues found.

For more information on energy conservation in lighting, see also:

 

Older non-Cg shaders

For non-Cg shaders, keep on reading... Note that v0.8.10 still has a non-Cg racer.exe for older cards that load different .shd files to support the older methods for old cards. For the best experience, you will need a nice graphics card though. More information on the non-Cg version here.

My First Shader - a transparent fence

Here's an example shader that blends a texture with the contents in the framebuffer. The 'cull=none' makes sure that the fence will be drawn no matter at which side of the fence the viewer is. The 'blend' blendfunction mixes the texture with that in the framebuffer ('blend' is a shortcut for 'src_alpha one_minus_src_alpha'). The 'alphafunc' avoids pixels being rendered (and updating the Z-buffer) that are mostly transparent. Otherwise (settings depthwrite=0 and leaving alphafunc out) you probably get bad Z-ordering (some far away trees appearing before closer trees).

shader_fence
{
  cull=none
  layer0
  {
map=fence.tga
blendfunc=blend alphafunc=gequal 150 depthwrite=1
} }

My Second Shader - a transparent fence part 2

The above shader can look a bit crooked further away (aliasing pixels), so an alternative is to omit the alphafunc filter, so that more pixels get drawn (actually, using 'alphafunc=gequal 1' might speed up rendering a bit, since blending pixels with alpha=0 is a waste). Also, depthwrite is set to 0, to avoid pixels written BEHIND the fence being clipped out due to the Z-buffer.

shader_fence
{
  cull=none
  layer0
  {
map=fence.tga
blendfunc=blend depthwrite=0
} }

A sky shader

Here's an example skybox shader; it avoids Z-buffer testing (depthfunc=always) & writing (depthwrite=0). Notice that this improves rendering performance. Also, the 'sky=1' line indicates this is a sky object, which pushes the far Z clipping coordinate quite a bit further, so even with low visibility (say 300 meters) you are still able to render the sky object (which uses multiple kilometers as the clipping point).

Last point is the 'fog=0' line. As fog costs time to render and process, it's better to turn it off completely and do any fog you want directly in the sky object's textures.

shader_sky
{
  sky=1
fog=0
layer0 { map=sky.tga depthwrite=0 depthfunc=always } }

A cubic environment mapped car body shader

For this shader, a cubic environment map is used. The specific map is $trackenvmap, which is a cube map that is loaded statically or overruled for a track (static: data/images/track_*.tga, per-track: data/tracks/<mytrack>/track_*.tga). If envmap.live_track is set to 1 in racer.ini, this environment map will be updated to match the actual surroundings.

The $trackenvmap cubic map can be updated live, at quite some rendering cost though, with gfx.ini's envmap.live_track (set it to 1 to enable live updates).

The body.tga image contains the actual colors, and its alpha channel decides where reflectivity is highest (alpha=0) and regular color prevails (alpha=255). Intermediate values (1..254) are mixes between full reflection and the regular texture.

shader_body
{
layer0
{
; Cubic envmap of track
map=$trackenvmap
texgen_s=reflection_map
texgen_t=reflection_map
texgen_r=reflection_map
}
layer1
{
; Real body colors
map=body.tga
blendfunc=src_alpha one_minus_src_alpha
}
}

Advanced shaders

Although the above examples can be used, it's all based on a fixed rendering path. These days, graphics cards allow vertex & fragment programs where you use a small piece of source code to define a program that's run at every vertex or fragment (pixel). Racer has embraced Cg (created by nVidia) to support more advanced shading methods. It supports generating HDR (high dynamic range) rendering for example, which is much, much better looking when put to good use.

To begin with vertex & fragment shaders for Racer, start the GPU Shader Tutorial.

More information

It's easier from here on to just use the shader reference page.


Dolphinity Organiser - free planning, project management and organizing software for all your action lists

(last updated November 13, 2012 )