S&box Wiki

Revision Difference

ShadingModel#547910

<cat>Code.Shader</cat>⤶ <cat>Material.ShaderBasic</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; } }; ``` ## Light Data | Type | Name | Description | |------|------|-------------| | float3 | Color | The color is an RGB value in the linear sRGB color space. | | float3 | LightDir | The normalized light vector, in world space (direction from the current fragment's position to the light). | | float | NdotL | The dot product of the shading normal (with normal mapping applied) and the light vector. This value is equal to the result of `saturate(dot(getWorldSpaceNormal(), lightData.l))`. This value is always between 0.0 and 1.0. When the value is <= 0.0, the current fragment is not visible from the light and lighting computations can be skipped. | | float3 | PositionWs | The position of the light in world space. | | float | Attenuation | Attenuation of the light based on the distance from the current fragment to the light in world space. This value between 0.0 and 1.0 is computed differently for each type of light (it's always 1.0 for directional lights). | | float | Visibility | Visibility factor computed from shadow maps or other occlusion data specific to the light being evaluated. This value is between 0.0 and 1.0. | ## 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 ); } } ```