TorqueShaderEngine/LightingSystem/Custom Material Lighting

From TDN

When using procedural shaders (using the standard Material object) all dynamic lighting shaders are automatically set up. However, when using a custom shader and material (with the CustomMaterial object) the dynamic lighting shaders must be manually set up. This article explains how.


Creating and Linking Custom Lighting Materials

The lighting system works with materials by linking multiple lighting materials together, then using the material necessary for a particular lighting task - similar to how fallbacks work. For procedural materials everything is generated and setup automatically, custom materials need to be setup manually.


Here's the basics of linking:

  • the main/root material (the one bound to a texture and applied to an object) only processes directional dynamic lighting, static lighting (light maps), or, for objects that ignore lighting, process no lighting.
  • the dynamic lighting material, which is linked off of the root materials "dynamicLightingMaterial" property, only processes point and spot dynamic lighting.
  • the dynamic lighting mask material ("dynamicLightingMaskMaterial") processes dynamic point and spot lighting that is using a mask cubemap.


Here's a quick example of the material definitions from atlas:

new CustomMaterial(AtlasDynamicLightingMaskMaterial)
{
   texture[0] = "$dynamiclight";
   texture[1] = "$dynamiclightmask";
   shader = AtlasDynamicLightingMaskShader;
   version = 1.1;
   preload = true;
};

new CustomMaterial(AtlasDynamicLightingMaterial)
{
   texture[0] = "$dynamiclight";
   shader = AtlasDynamicLightingShader;
   version = 1.1;
   preload = true;
};

new CustomMaterial(AtlasMaterial)
{
   shader = AtlasShader;
   version = 1.1;

   dynamicLightingMaterial = AtlasDynamicLightingMaterial;
   dynamicLightingMaskMaterial = AtlasDynamicLightingMaskMaterial;
   preload = true;
};


The constants "$dynamiclight" and "$dynamiclightmask" are built into the engine and tell TGEA that your shader expects the dynamic lighting or mask texture respectively in the assigned texture unit.


Creating Dynamic Point and Spot Lighting Shaders

The lighting system has several built-in shader functions that help simplify dynamic lighting and ensure that it's similar to other in-game lighting - just include the the shader header lightingSystem/shdrLib.h to access them.


The header includes the following functions:

  • getDynamicLightingCoord - used by vertex shaders to get the coords used for sampling the lighting texture. Expects the vertex and light positions in object space, the current object to world transform, and the lighting transform - all are supplied in the expected format by the engine, so just pass them in. Returns the float3 coords used to lookup the lighting texture - just pass this on to the pixel shader.
  • getDynamicLighting - used to sample the dynamic lighting texture. Expects the dynamic lighting texture, the lighting texture coords supplied by the vertex shader, and the light color - all supplied in the correct format by the engine or vertex shader.


The atlas custom shaders are very good examples of minimal dynamic lighting shaders.


The atlas dynamic lighting vertex shader "atlasSurfaceDynamicLightingV.hlsl":

#define IN_HLSL
#include "../shdrConsts.h"
#include "atlas.h"
#include "../lightingSystem/shdrLib.h"

struct VertLightConnectData
{
   float4 hpos : POSITION;
   float3 lightingCoord   : TEXCOORD0;
};

//-----------------------------------------------------------------------------
// Main
//-----------------------------------------------------------------------------
VertLightConnectData main( VertData IN,
                  uniform float4x4 modelView : register(C0),
                  uniform float3   eyePos    : register(VC_EYE_POS),
                  uniform float    morphT    : register(C49),
                  uniform float4x4 objTrans        : register(VC_OBJ_TRANS),              
                  uniform float4   lightPos        : register(VC_LIGHT_POS1),
                  uniform float4x4 lightingMatrix  : register(VC_LIGHT_TRANS)
                  )
{
   VertLightConnectData OUT;

   // Apply morph calculations.
   float4 realPos = IN.position + (IN.morphCoord * morphT);
   realPos.w = 1;

   // Transform and return.
   OUT.hpos = mul(modelView, realPos);

   // Let's get some lighting calcs in here.
   OUT.lightingCoord = getDynamicLightingCoord(IN.position, lightPos, objTrans, lightingMatrix);

   return OUT;
}


The atlas dynamic lighting pixel shader "atlasSurfaceDynamicLightingP.hlsl":

//-----------------------------------------------------------------------------
// Structures                                                                  
//-----------------------------------------------------------------------------
#define IN_HLSL
#include "../shdrConsts.h"
#include "../lightingSystem/shdrLib.h"

struct ConnectData
{
   float3 lightingCoord   : TEXCOORD0;
};

struct Fragout
{
   float4 col : COLOR0;
};


//-----------------------------------------------------------------------------
// Main                                                                        
//-----------------------------------------------------------------------------
Fragout main( ConnectData IN,			  
              uniform sampler3D dlightMap       : register(S0),
              uniform float4    lightColor      : register(PC_DIFF_COLOR)
              )
{
   Fragout OUT;
   
   OUT.col = getDynamicLighting(dlightMap, IN.lightingCoord, lightColor);
   
   // alpha is sum(r,g,b) so we can filter unlit pixels with alpha test.
   OUT.col.w = OUT.col.x + OUT.col.y + OUT.col.z;
   
   return OUT;
}


These few changes handle all of the base lighting calculations.