Revision Difference
ShadingModel#547707
<cat>Code.Shader</cat>⤶
<title>Shading Model</title>⤶
⤶
# What is a shading model⤶
⤶
A shading model is what controls the lighting & shading of your surface. By default, Two different shading models are provided, `ShadingModelStandard` which is the default, and `ShadingModelValveStandard`. Not only can you use one of these pre-defined shading models, but you can also create your own shading model to shade your surface the way you want.⤶
⤶
# Using built-in shading models⤶
⤶
Invoking and using your shading model is done by calling the `FinalizePixelMaterial` function, which is defined automatically when you include the `common/pixel.hlsl` file within your pixel shader.⤶
⤶
By default, if you do not provide a shading model to the `FinalizePixelMaterial` call, it will use the `ShadingModelStandard`. For example:⤶
```⤶
// PixelInput, Material⤶
return FinalizePixelMaterial( i, m );⤶
```⤶
⤶
If you wish to use the `ShadingModelValveStandard`, it will need to be passed in as the third argument to `FinalizePixelMaterial` like so.⤶
```⤶
ShadingModelValveStandard sm;⤶
// PixelInput, Material, Shading Model⤶
return FinalizePixelMaterial( i, m, sm );⤶
```⤶
⤶
# Creating a shading model⤶
⤶
## Structure⤶
⤶
All shading models contain 4 basic methods which need to be implemented. `Init`, which is called once the shading model is initialized for the first time. `Direct` is called for every single direct light which is currently visible, `Indirect` is called once to calculate indirect lighting & `PostProcess` which allows tweaking of the shading after everything is done, for example adding fog. To begin implementing any of these methods, you will need to create a class & inherit from `ShadingModel`. An empty shading model can be seen below:⤶
```⤶
class ShadingModelExample : ShadingModel⤶
{⤶
//⤶
// Execute before anything⤶
//⤶
void Init( const PixelInput pixelInput, const Material material )⤶
{⤶
}⤶
⤶
//⤶
// Executed for every direct light⤶
//⤶
LightShade Direct( const LightData light )⤶
{⤶
LightShade shade;⤶
shade.Diffuse = float3( 0.0f, 0.0f, 0.0f );⤶
shade.Specular = float3( 0.0f, 0.0f, 0.0f );⤶
return shade;⤶
}⤶
⤶
//⤶
// Executed for indirect lighting, combine ambient occlusion, etc.⤶
//⤶
LightShade Indirect()⤶
{⤶
LightShade shade;⤶
shade.Diffuse = float3( 0.0f, 0.0f, 0.0f );⤶
shade.Specular = float3( 0.0f, 0.0f, 0.0f );⤶
return shade;⤶
}⤶
⤶
//⤶
// Applying any post-processing effects after all lighting is complete⤶
//⤶
float4 PostProcess( float4 vColor )⤶
{⤶
return vColor;⤶
}⤶
};⤶
```⤶
⤶
## Usage⤶
Once the shading model is created, it can be used like any other shading model. It's first declared and then passed into `FinalizePixelMaterial` like so:⤶
```⤶
float4 MainPs( PixelInput i ) : SV_Target0⤶
{⤶
Material m = GatherMaterial( i );⤶
ShadingModelExample sm;⤶
return FinalizePixelMaterial( i, m, sm );⤶
}⤶
```⤶
⤶
## Finished Example⤶
⤶
<upload src="653cb/8daa51bce5c2fd1.png" size="147268" name="image.png" />⤶
⤶
```⤶
PS⤶
{⤶
#include "common/pixel.hlsl"⤶
//⤶
// Main⤶
//⤶
⤶
class ShadingModelExample : ShadingModel⤶
{⤶
float3 Albedo;⤶
float3 NormalWs;⤶
float3 ViewRayWs;⤶
⤶
//⤶
// Consumes a material and converts it to the internal shading parameters,⤶
// That is more easily consumed by the shader.⤶
//⤶
// Inherited classes can expand this to whichever shading model they want.⤶
//⤶
void Init( const PixelInput pixelInput, const Material material )⤶
{⤶
// Keep track of our albedo & normal⤶
Albedo = material.Albedo;⤶
NormalWs = material.Normal;⤶
⤶
float3 PositionWithOffsetWs = pixelInput.vPositionWithOffsetWs.xyz;⤶
float3 PositionWs = PositionWithOffsetWs + g_vCameraPositionWs;⤶
⤶
// View ray in World Space⤶
ViewRayWs = CalculatePositionToCameraDirWs( PositionWs );⤶
}⤶
⤶
//⤶
// Executed for every direct light⤶
//⤶
LightShade Direct( const LightData light )⤶
{⤶
// Shading output⤶
LightShade shade;⤶
⤶
// Compute our shadow mask⤶
float flShadow = light.NdotL * light.Visibility * light.Attenuation;⤶
⤶
// Make it hard instead of a soft transition⤶
flShadow = step(0.001f, flShadow);⤶
⤶
// Calculate everything we need for specular⤶
float3 vHalfAngleDirWs = normalize(ViewRayWs + light.LightDir);⤶
float flNdotH = dot( vHalfAngleDirWs.xyz, NormalWs );⤶
⤶
// Sharpen our specular⤶
float flSpecular = pow(flNdotH * flShadow, 100.0f);⤶
// Smooth it out a little bit⤶
flSpecular = smoothstep(0.005, 0.01, flSpecular);⤶
⤶
// Diffuse lighting for the current light⤶
shade.Diffuse = saturate(flShadow * light.Color) * g_flTintColor;⤶
⤶
// Calculate our specular for the current light⤶
shade.Specular = flSpecular * Albedo * g_flTintColor;⤶
⤶
return shade;⤶
}⤶
⤶
//⤶
// Executed for indirect lighting, combine ambient occlusion, etc.⤶
//⤶
LightShade Indirect()⤶
{⤶
LightShade shade;⤶
⤶
// Get a flat average ambient⤶
float3 vAmbientCube[6];⤶
SampleLightProbeVolume( vAmbientCube, float3(0,0,0) );⤶
⤶
// Light with our ambient color⤶
float3 flColor = 0.0f;⤶
for(int i = 0; i < 6; i++)⤶
flColor += vAmbientCube[i] * (1.0f / 6.0f); ⤶
shade.Diffuse = flColor * Albedo;⤶
⤶
// No specular⤶
shade.Specular = 0.0f;⤶
⤶
return shade;⤶
}⤶
⤶
float4 PostProcess( float4 vColor )⤶
{⤶
// We don't need any post processing!⤶
return vColor;⤶
}⤶
};⤶
⤶
⤶
float4 MainPs( PixelInput i ) : SV_Target0⤶
{⤶
Material m = GatherMaterial( i );⤶
ShadingModelExample sm;⤶
return FinalizePixelMaterial( i, m, sm );⤶
}⤶
}⤶
```