S&box Wiki

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 );⤶ }⤶ }⤶ ```