mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-05-19 12:27:52 -04:00
Better foliage and bark shader (#3915)
* Improved foliage shader - Added rim lighting - Cheap transmissivenes - Grazing cards fade out - Wrapped lighting - Backface darkening - Backface shading - Distance fade of shading features https://files.facepunch.com/antopilo/1b2611b1/sbox-dev_CNoLJhw3ms.mp4 * Tweaked some stuff https://files.facepunch.com/antopilo/1b2611b1/sbox-dev_C9EcKZCojJ.mp4 * use precompueted dist * Made warp lighting ignore shadow * Improved trunk rendering - Specular Occlusion - Trunk bending animation - Syncs with foliage animation https://files.facepunch.com/antopilo/1b2711b1/sbox-dev_WoM5FlFzSn.mp4
This commit is contained in:
93
game/addons/base/Assets/shaders/bark.shader
Normal file
93
game/addons/base/Assets/shaders/bark.shader
Normal file
@@ -0,0 +1,93 @@
|
||||
HEADER
|
||||
{
|
||||
Description = "Bark shader for trees, syncs with foliage.shader";
|
||||
DevShader = true;
|
||||
}
|
||||
|
||||
FEATURES
|
||||
{
|
||||
#include "common/features.hlsl"
|
||||
Feature( F_BARK_ANIMATION, 0..1, "Bark Animation" );
|
||||
}
|
||||
|
||||
MODES
|
||||
{
|
||||
Forward();
|
||||
Depth( S_MODE_DEPTH );
|
||||
ToolsShadingComplexity( "vr_tools_shading_complexity.shader" );
|
||||
}
|
||||
|
||||
COMMON
|
||||
{
|
||||
#include "common/shared.hlsl"
|
||||
}
|
||||
|
||||
struct VertexInput
|
||||
{
|
||||
#include "common/vertexinput.hlsl"
|
||||
};
|
||||
|
||||
struct PixelInput
|
||||
{
|
||||
#include "common/pixelinput.hlsl"
|
||||
};
|
||||
|
||||
VS
|
||||
{
|
||||
#include "common/vertex.hlsl"
|
||||
#include "common/trunk_bending.hlsl"
|
||||
|
||||
StaticCombo( S_BARK_ANIMATION, F_BARK_ANIMATION, Sys( ALL ) );
|
||||
|
||||
#if S_BARK_ANIMATION
|
||||
float g_flSwayStrength < Default( 1.0 ); Range( 0.0, 25.0 ); UiGroup( "Bark Animation" ); >;
|
||||
float g_flSwaySpeed < Default( 1.0 ); Range( 0.0, 10.0 ); UiGroup( "Bark Animation" ); >;
|
||||
#endif
|
||||
|
||||
PixelInput MainVs( VertexInput i )
|
||||
{
|
||||
PixelInput o = ProcessVertex( i );
|
||||
|
||||
#if S_BARK_ANIMATION
|
||||
float3 vPositionOs = i.vPositionOs.xyz;
|
||||
float3 vWind = g_vWindDirection.xyz * g_vWindStrengthFreqMulHighStrength.x;
|
||||
|
||||
ApplyTrunkBending( vPositionOs, g_flSwayStrength, g_flSwaySpeed, vWind, g_flTime );
|
||||
|
||||
float3x4 matObjectToWorld = GetTransformMatrix( i.nInstanceTransformID );
|
||||
o.vPositionWs = mul( matObjectToWorld, float4( vPositionOs, 1.0 ) );
|
||||
o.vPositionPs = Position3WsToPs( o.vPositionWs.xyz );
|
||||
#endif
|
||||
|
||||
return FinalizeVertex( o );
|
||||
}
|
||||
}
|
||||
|
||||
PS
|
||||
{
|
||||
#include "common/utils/Material.CommonInputs.hlsl"
|
||||
#include "common/pixel.hlsl"
|
||||
|
||||
RenderState( CullMode, F_RENDER_BACKFACES ? NONE : DEFAULT );
|
||||
|
||||
float g_flSpecularOcclusion < Default( 0.5 ); Range( 0.0, 1.0 ); UiGroup( "Bark" ); >;
|
||||
|
||||
// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf (page 77)
|
||||
float ComputeSpecularOcclusion( float NdotV, float ao, float roughness )
|
||||
{
|
||||
return saturate( pow( NdotV + ao, exp2( -16.0 * roughness - 1.0 ) ) - 1.0 + ao );
|
||||
}
|
||||
|
||||
float4 MainPs( PixelInput i ) : SV_Target0
|
||||
{
|
||||
Material m = Material::From( i );
|
||||
|
||||
// Specular occlusion
|
||||
float3 viewDir = normalize( g_vCameraPositionWs - m.WorldPosition );
|
||||
float NdotV = saturate( dot( m.Normal, viewDir ) );
|
||||
float specOcc = ComputeSpecularOcclusion( NdotV, m.AmbientOcclusion, m.Roughness );
|
||||
m.AmbientOcclusion *= lerp( 1.0, specOcc, g_flSpecularOcclusion );
|
||||
|
||||
return ShadingModelStandard::Shade( i, m );
|
||||
}
|
||||
}
|
||||
22
game/addons/base/Assets/shaders/common/trunk_bending.hlsl
Normal file
22
game/addons/base/Assets/shaders/common/trunk_bending.hlsl
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef TRUNK_BENDING_HLSL
|
||||
#define TRUNK_BENDING_HLSL
|
||||
|
||||
// Trunk/branch bending for vegetation
|
||||
// Based on GPU Gems 3 Chapter 16: "Vegetation Procedural Animation and Shading in Crysis"
|
||||
// https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch16.html
|
||||
void ApplyTrunkBending( inout float3 vPositionOs, float flStrength, float flSpeed, float3 vWind, float flTime )
|
||||
{
|
||||
// Height bend factor
|
||||
float flHeight = vPositionOs.z;
|
||||
float flBendFactor = flHeight * flStrength * 0.0001;
|
||||
flBendFactor *= flBendFactor; // Quadratic falloff
|
||||
|
||||
float3 vWindDir = length( vWind ) > 0.01 ? normalize( vWind ) : float3( 1, 0, 0 );
|
||||
float flWindStrength = length( vWind );
|
||||
|
||||
float flSway = sin( flTime * flSpeed ) + sin( flTime * flSpeed * 0.7 ) * 0.5;
|
||||
|
||||
vPositionOs.xy += vWindDir.xy * flBendFactor * ( flWindStrength + flSway );
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -17,8 +17,7 @@ FEATURES
|
||||
Feature( F_ALPHA_TEST, 0..1, "Rendering" );
|
||||
Feature( F_FOLIAGE_ANIMATION, 0..1( 0 = "None", 1 = "Vertex Color Based" ), "Foliage Animation" );
|
||||
Feature( F_TRANSMISSIVE, 0..1, "Rendering" );
|
||||
Feature( F_TRANSMISSIVE_BACKFACE_NDOTL, 0..1, "Rendering" );
|
||||
FeatureRule( Requires1( F_TRANSMISSIVE_BACKFACE_NDOTL, F_TRANSMISSIVE ), "Requires transmissive" );
|
||||
Feature( F_GRAZING_FADE, 0..1, "Rendering" );
|
||||
}
|
||||
|
||||
//=========================================================================================================================
|
||||
@@ -27,7 +26,7 @@ FEATURES
|
||||
MODES
|
||||
{
|
||||
Forward(); // Indicates this shader will be used for main rendering
|
||||
Depth();
|
||||
Depth( S_MODE_DEPTH );
|
||||
ToolsShadingComplexity( "vr_tools_shading_complexity.shader" ); // Shows how expensive drawing is in debug view
|
||||
}
|
||||
|
||||
@@ -60,39 +59,41 @@ struct PixelInput
|
||||
VS
|
||||
{
|
||||
#include "common/vertex.hlsl"
|
||||
#include "common/trunk_bending.hlsl"
|
||||
|
||||
StaticCombo( S_FOLIAGE_ANIMATION, F_FOLIAGE_ANIMATION, Sys( ALL ) );
|
||||
|
||||
// Vertex Color
|
||||
#if S_FOLIAGE_ANIMATION == 1
|
||||
|
||||
float g_flEdgeFrequency < Default( 0.33 ); Range( 0.0, 1.0 ); UiGroup( "Foliage Animation" ); >;
|
||||
float g_flEdgeAmplitude < Default( 0.3 ); Range( 0.0, 1.0 ); UiGroup( "Foliage Animation" ); >;
|
||||
float g_flBranchFrequency < Default( 0.33 ); Range( 0.0, 1.0 ); UiGroup( "Foliage Animation" ); >;
|
||||
float g_flBranchAmplitude < Default( 0.3 ); Range( 0.0, 1.0 ); UiGroup( "Foliage Animation" ); >;
|
||||
float g_flTrunkDeflection < Default( 0.0 ); Range( 0.0, 1.0 ); UiGroup( "Foliage Animation" ); >;
|
||||
float g_flTrunkDeflectionStart < Default( 0.0 ); Range( 0.0, 1000.0 ); UiGroup( "Foliage Animation" ); >;
|
||||
float g_flEdgeFrequency < Default( 1.0 ); Range( 0.1, 5.0 ); UiGroup( "Foliage Animation,10/Detail" ); >;
|
||||
float g_flEdgeAmplitude < Default( 0.1 ); Range( 0.0, 1.0 ); UiGroup( "Foliage Animation,10/Detail" ); >;
|
||||
float g_flBranchFrequency < Default( 0.5 ); Range( 0.1, 5.0 ); UiGroup( "Foliage Animation,10/Detail" ); >;
|
||||
float g_flBranchAmplitude < Default( 0.1 ); Range( 0.0, 1.0 ); UiGroup( "Foliage Animation,10/Detail" ); >;
|
||||
|
||||
// Trunk bending
|
||||
float g_flSwayStrength < Default( 1.0 ); Range( 0.0, 25.0 ); UiGroup( "Foliage Animation,20/Trunk" ); >;
|
||||
float g_flSwaySpeed < Default( 1.0 ); Range( 0.0, 10.0 ); UiGroup( "Foliage Animation,20/Trunk" ); >;
|
||||
|
||||
float4 SmoothCurve( float4 x )
|
||||
{
|
||||
return x * x * ( 3.0 - 2.0 * x );
|
||||
}
|
||||
{
|
||||
return x * x * ( 3.0 - 2.0 * x );
|
||||
}
|
||||
|
||||
float4 TriangleWave( float4 x )
|
||||
{
|
||||
return abs( frac( x + 0.5 ) * 2.0 - 1.0 );
|
||||
}
|
||||
return abs( frac( x + 0.5 ) * 2.0 - 1.0 );
|
||||
}
|
||||
|
||||
float4 SmoothTriangleWave( float4 x )
|
||||
{
|
||||
return SmoothCurve( TriangleWave( x ) );
|
||||
{
|
||||
return SmoothCurve( TriangleWave( x ) );
|
||||
}
|
||||
|
||||
// High-frequency displacement used in Unity's TerrainEngine; based on "Vegetation Procedural Animation and Shading in Crysis"
|
||||
// http://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch16.html
|
||||
void FoliageDetailBending( inout float3 vPositionOs, float3 vNormalOs, float3 vVertexColor, float3x4 matObjectToWorld, float3 vWind )
|
||||
{
|
||||
// 1.975, 0.793, 0.375, 0.193 are good frequencies
|
||||
const float4 vFoliageFreqs = float4( 1.975, 0.793, 0.375, 0.193 );
|
||||
|
||||
// Attenuation and phase offset is encoded in the vertex color
|
||||
@@ -101,7 +102,6 @@ VS
|
||||
const float flDetailPhase = vVertexColor.b;
|
||||
|
||||
// Material defined frequency and amplitude
|
||||
const float2 vTime = g_flTime.xx * float2( g_flEdgeFrequency, g_flBranchFrequency );
|
||||
const float flEdgeAmp = g_flEdgeAmplitude;
|
||||
const float flBranchAmp = g_flBranchAmplitude;
|
||||
|
||||
@@ -110,50 +110,24 @@ VS
|
||||
float flBranchPhase = flDetailPhase + flObjPhase;
|
||||
float flVtxPhase = dot( vPositionOs.xyz, flDetailPhase + flBranchPhase );
|
||||
|
||||
// fmod max phase avoid imprecision at high phases
|
||||
const float maxPhase = 50000.0f;
|
||||
|
||||
// x is used for edges; y is used for branches
|
||||
float2 vWavesIn = ( vTime.xy + fmod( float2( flVtxPhase, flBranchPhase ), maxPhase ) ) * length( vWind );
|
||||
|
||||
float4 vWaves = ( frac( vWavesIn.xxyy * vFoliageFreqs ) * 2.0 - 1.0 );
|
||||
float2 vTime = g_flTime * float2( g_flEdgeFrequency, g_flBranchFrequency );
|
||||
float2 vPhaseOffset = fmod( float2( flVtxPhase, flBranchPhase ), maxPhase );
|
||||
float2 vWavesIn = vTime + vPhaseOffset;
|
||||
|
||||
float4 vWaves = frac( vWavesIn.xxyy * vFoliageFreqs ) * 2.0 - 1.0;
|
||||
vWaves = SmoothTriangleWave( vWaves );
|
||||
float2 vWavesSum = vWaves.xz + vWaves.yw;
|
||||
|
||||
// Edge (xy) and branch bending (z)
|
||||
float flBranchWindBend = 1.0f - abs( dot( normalize( vWind.xyz ), normalize( float3( vPositionOs.xy, 0.0f ) ) ) );
|
||||
float flWindIntensity = saturate( length( vWind ) * 0.2 );
|
||||
|
||||
float flBranchWindBend = 1.0f - abs( dot( normalize( vWind.xyz + 0.001 ), normalize( float3( vPositionOs.xy, 0.0f ) + 0.001 ) ) );
|
||||
flBranchWindBend *= flBranchWindBend;
|
||||
|
||||
vPositionOs.xyz += vWavesSum.x * flEdgeAtten * flEdgeAmp * vNormalOs.xyz;
|
||||
vPositionOs.xyz += vWavesSum.y * flBranchAtten * flBranchAmp * float3( 0.0f, 0.0f, 1.0f );
|
||||
vPositionOs.xyz += vWavesSum.y * flBranchAtten * flBranchAmp * flBranchWindBend * vWind.xyz;
|
||||
}
|
||||
|
||||
// Main vegetation bending - displace verticies' xy positions along the wind direction
|
||||
// using normalized height to scale the amount of deformation.
|
||||
void FoliageMainBending( inout float3 vPositionOs, float3 vWind )
|
||||
{
|
||||
if ( g_flTrunkDeflection <= 0.0 ) return;
|
||||
|
||||
float flLength = length( vPositionOs.xyz );
|
||||
// Bend factor - Wind variation is done on the CPU.
|
||||
float flBF = 0.01f * max( vPositionOs.z - g_flTrunkDeflectionStart, 0 ) * g_flTrunkDeflection;
|
||||
// Smooth bending factor and increase its nearby height limit.
|
||||
flBF += 1.0f;
|
||||
flBF *= flBF;
|
||||
flBF = flBF * flBF - flBF;
|
||||
|
||||
// Back and forth
|
||||
float flBend = pow( max( 0.0f, length( vWind ) - 1.0f ) / 4.0f, 2 ) * 4.0f;
|
||||
flBend = flBend + 0.7f * sqrt( flBend ) * sin( g_flTime );
|
||||
flBF *= flBend;
|
||||
|
||||
// Displace position
|
||||
float3 vNewPos = vPositionOs;
|
||||
vNewPos.xy += vWind.xy * flBF;
|
||||
|
||||
// Rescale (reduces stretch)
|
||||
vPositionOs.xyz = normalize( vNewPos.xyz ) * flLength;
|
||||
vPositionOs.xyz += vWavesSum.x * flEdgeAtten * flEdgeAmp * flWindIntensity * vNormalOs.xyz;
|
||||
vPositionOs.xyz += vWavesSum.y * flBranchAtten * flBranchAmp * flWindIntensity * float3( 0.0f, 0.0f, 1.0f );
|
||||
vPositionOs.xyz += vWavesSum.y * flBranchAtten * flBranchAmp * flBranchWindBend * flWindIntensity * vWind.xyz;
|
||||
}
|
||||
#endif
|
||||
//
|
||||
@@ -177,8 +151,11 @@ VS
|
||||
#if ( S_FOLIAGE_ANIMATION == 1 )
|
||||
float3 vWind = g_vWindDirection.xyz * g_vWindStrengthFreqMulHighStrength.x;
|
||||
|
||||
// trunk bending
|
||||
ApplyTrunkBending( vPositionOs, g_flSwayStrength, g_flSwaySpeed, vWind, g_flTime );
|
||||
|
||||
// detail bending on top
|
||||
FoliageDetailBending( vPositionOs, vNormalOs, i.vColor.xyz, matObjectToWorld, vWind );
|
||||
FoliageMainBending( vPositionOs, vWind );
|
||||
#endif
|
||||
|
||||
o.vPositionWs = mul( matObjectToWorld, float4( vPositionOs.xyz, 1.0 ) );
|
||||
@@ -193,36 +170,184 @@ VS
|
||||
|
||||
PS
|
||||
{
|
||||
|
||||
#include "common/utils/Material.CommonInputs.hlsl"
|
||||
#include "common/pixel.hlsl"
|
||||
|
||||
StaticCombo( S_ALPHA_TEST, F_ALPHA_TEST, Sys( ALL ) );
|
||||
StaticCombo( S_TRANSMISSIVE, F_TRANSMISSIVE, Sys( ALL ) );
|
||||
StaticCombo( S_TRANSMISSIVE_BACKFACE_NDOTL, F_TRANSMISSIVE_BACKFACE_NDOTL, Sys( ALL ) );
|
||||
#include "common/utils/Material.CommonInputs.hlsl"
|
||||
#include "common/pixel.hlsl"
|
||||
#include "common/classes/Light.hlsl"
|
||||
|
||||
#if S_ALPHA_TEST
|
||||
TextureAttribute( LightSim_Opacity_A, g_tColor );
|
||||
#endif
|
||||
StaticCombo( S_ALPHA_TEST, F_ALPHA_TEST, Sys( ALL ) );
|
||||
StaticCombo( S_TRANSMISSIVE, F_TRANSMISSIVE, Sys( ALL ) );
|
||||
StaticCombo( S_GRAZING_FADE, F_GRAZING_FADE, Sys( ALL ) );
|
||||
|
||||
#if S_TRANSMISSIVE
|
||||
CreateInputTexture2D( TextureTransmissiveColor, Srgb, 8, "", "_color", "Material,10/60", Default3( 1.0, 1.0, 1.0 ) );
|
||||
Texture2D g_tTransmissiveColor < Channel( RGB, Box( TextureTransmissiveColor ), Srgb ); OutputFormat( BC7 ); SrgbRead( true ); >;
|
||||
#endif
|
||||
RenderState( CullMode, F_RENDER_BACKFACES ? NONE : DEFAULT );
|
||||
RenderState( AlphaToCoverageEnable, false );
|
||||
|
||||
RenderState( CullMode, F_RENDER_BACKFACES ? NONE : DEFAULT );
|
||||
#if ( S_MODE_DEPTH == 0 )
|
||||
RenderState( DepthFunc, EQUAL );
|
||||
RenderState( DepthWriteEnable, false );
|
||||
#endif
|
||||
|
||||
#if S_ALPHA_TEST
|
||||
TextureAttribute( LightSim_Opacity_A, g_tColor );
|
||||
float g_flAlphaDistanceStart < Default( 500.0 ); Range( 0.0, 5000.0 ); UiGroup( "Alpha Test" ); >;
|
||||
float g_flAlphaDistanceEnd < Default( 2000.0 ); Range( 0.0, 10000.0 ); UiGroup( "Alpha Test" ); >;
|
||||
#endif
|
||||
|
||||
float g_flWrapAmount < Default( 0.5 ); Range( 0.0, 1.0 ); UiGroup( "Foliage" ); >;
|
||||
float g_flWrapStrength < Default( 0.3 ); Range( 0.0, 1.0 ); UiGroup( "Foliage" ); >;
|
||||
|
||||
float g_flRimStrength < Default( 0.0 ); Range( 0.0, 2.0 ); UiGroup( "Foliage" ); >;
|
||||
float g_flRimPower < Default( 4.0 ); Range( 1.0, 8.0 ); UiGroup( "Foliage" ); >;
|
||||
|
||||
float g_flBackfaceDarkening < Default( 0.7 ); Range( 0.0, 1.0 ); UiGroup( "Foliage" ); >;
|
||||
float g_flAmbientBoost < Default( 0.0 ); Range( 0.0, 0.5 ); UiGroup( "Foliage" ); >;
|
||||
float g_flDetailFadeDistance < Default( 500.0 ); Range( 100.0, 2000.0 ); UiGroup( "Foliage" ); >;
|
||||
float g_flMinRoughness < Default( 0.5 ); Range( 0.0, 1.0 ); UiGroup( "Foliage" ); >;
|
||||
float g_flNormalVariation < Default( 0.1 ); Range( 0.0, 0.5 ); UiGroup( "Foliage" ); >;
|
||||
float g_flGrassNormalUp < Default( 0.0 ); Range( 0.0, 1.0 ); UiGroup( "Foliage" ); >;
|
||||
|
||||
|
||||
#if S_GRAZING_FADE
|
||||
float g_flGrazingFadeStart < Default( 0.5 ); Range( 0.0, 1.0 ); UiGroup( "Grazing Fade" ); >;
|
||||
float g_flGrazingFadeEnd < Default( 0.1 ); Range( 0.0, 1.0 ); UiGroup( "Grazing Fade" ); >;
|
||||
#endif
|
||||
|
||||
#if S_TRANSMISSIVE
|
||||
CreateInputTexture2D( TextureTransmissiveColor, Srgb, 8, "", "_color", "Material,10/60", Default3( 1.0, 1.0, 1.0 ) );
|
||||
Texture2D g_tTransmissiveColor < Channel( RGB, Box( TextureTransmissiveColor ), Srgb ); OutputFormat( BC7 ); SrgbRead( true ); >;
|
||||
float g_flTransmissionScale < Default( 1.0 ); Range( 0.0, 10.0 ); UiGroup( "Transmissive" ); >;
|
||||
|
||||
void ApplyTranslucency( inout Material m, Light light, float3 viewDir, float3 transmissiveColor )
|
||||
{
|
||||
float3 lightThrough = normalize( light.Direction + m.Normal * 0.2 );
|
||||
float backlit = saturate( dot( viewDir, -lightThrough ) );
|
||||
backlit *= backlit;
|
||||
m.Emission += transmissiveColor * m.Albedo * light.Color * light.Visibility * backlit * g_flTransmissionScale * light.Attenuation;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if S_ALPHA_TEST
|
||||
void ApplyAlphaTest( inout Material m, float dist )
|
||||
{
|
||||
float distFactor = saturate( ( dist - g_flAlphaDistanceStart ) / max( g_flAlphaDistanceEnd - g_flAlphaDistanceStart, 0.001 ) );
|
||||
float alphaRef = lerp( g_flAlphaTestReference, 0.1, distFactor );
|
||||
float sharpness = saturate( ( m.Opacity - alphaRef ) / max( fwidth( m.Opacity ), 0.0001 ) + 0.5 );
|
||||
clip( sharpness - 0.5 );
|
||||
m.Opacity = 1.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if S_GRAZING_FADE
|
||||
void ApplyGrazingAngleFade( float3 positionWs, float3 viewDir, float2 screenPos )
|
||||
{
|
||||
float3 geometricNormal = normalize( cross( ddx( positionWs ), ddy( positionWs ) ) );
|
||||
float NdotV = abs( dot( geometricNormal, viewDir ) );
|
||||
float fade = saturate( ( NdotV - g_flGrazingFadeEnd ) / max( g_flGrazingFadeStart - g_flGrazingFadeEnd, 0.001 ) );
|
||||
|
||||
const float4x4 bayer = float4x4(
|
||||
0.0/16.0, 8.0/16.0, 2.0/16.0, 10.0/16.0,
|
||||
12.0/16.0, 4.0/16.0, 14.0/16.0, 6.0/16.0,
|
||||
3.0/16.0, 11.0/16.0, 1.0/16.0, 9.0/16.0,
|
||||
15.0/16.0, 7.0/16.0, 13.0/16.0, 5.0/16.0
|
||||
);
|
||||
|
||||
int2 pixel = int2( screenPos ) % 4;
|
||||
clip( fade - bayer[pixel.x][pixel.y] );
|
||||
}
|
||||
#endif
|
||||
|
||||
// https://developer.nvidia.com/gpugems/gpugems/part-iii-materials/chapter-16-real-time-approximations-subsurface-scattering
|
||||
void ApplyWrappedLighting( inout Material m, Light light, float lightScale )
|
||||
{
|
||||
if ( g_flWrapStrength <= 0.0 )
|
||||
return;
|
||||
|
||||
float NdotL = dot( m.Normal, light.Direction );
|
||||
float wrapped = saturate( ( NdotL + g_flWrapAmount ) / ( 1.0 + g_flWrapAmount ) );
|
||||
float wrapContribution = wrapped - saturate( NdotL );
|
||||
|
||||
if ( wrapContribution > 0.0 )
|
||||
m.Emission += m.Albedo * light.Color * wrapContribution * g_flWrapStrength * lightScale;
|
||||
}
|
||||
|
||||
// https://www.ronja-tutorials.com/post/012-fresnel/
|
||||
void ApplyRimLighting( inout Material m, float3 viewDir )
|
||||
{
|
||||
if ( g_flRimStrength <= 0.0 )
|
||||
return;
|
||||
|
||||
float rim = 1.0 - saturate( dot( m.Normal, viewDir ) );
|
||||
rim = pow( rim, g_flRimPower );
|
||||
m.Emission += m.Albedo * rim * g_flRimStrength;
|
||||
}
|
||||
|
||||
//
|
||||
// Main
|
||||
//
|
||||
float4 MainPs( PixelInput i ) : SV_Target0
|
||||
{
|
||||
Material m = Material::From( i );
|
||||
Material m = Material::From( i );
|
||||
|
||||
#if ( S_TRANSMISSIVE )
|
||||
m.Transmission = g_tTransmissiveColor.Sample( TextureFiltering, i.vTextureCoords.xy ).rgb;
|
||||
#endif
|
||||
|
||||
return ShadingModelStandard::Shade( i, m );
|
||||
// Specular occlusion
|
||||
m.Roughness = max( m.Roughness, g_flMinRoughness );
|
||||
|
||||
// Grass normal
|
||||
m.Normal = normalize( lerp( m.Normal, float3( 0, 0, 1 ), g_flGrassNormalUp ) );
|
||||
|
||||
// Normal variation
|
||||
if ( g_flNormalVariation > 0.0 )
|
||||
{
|
||||
float2 uv = floor( m.TextureCoords * 2.0 );
|
||||
float hash = frac( sin( dot( uv, float2( 12.9898, 78.233 ) ) ) * 43758.5453 );
|
||||
float angle = hash * 6.283;
|
||||
float2 offset = float2( cos( angle ), sin( angle ) );
|
||||
m.Normal = normalize( m.Normal + m.WorldTangentU * offset.x * g_flNormalVariation + m.WorldTangentV * offset.y * g_flNormalVariation );
|
||||
}
|
||||
|
||||
float3 viewDir = normalize( g_vCameraPositionWs - m.WorldPosition );
|
||||
float dist = length( i.vPositionWithOffsetWs.xyz );
|
||||
bool closeUp = dist < g_flDetailFadeDistance * 10.0f;
|
||||
|
||||
bool isBackface = dot( m.Normal, viewDir ) < 0.0;
|
||||
if ( closeUp && isBackface )
|
||||
{
|
||||
m.Normal = -m.Normal;
|
||||
|
||||
m.Albedo *= g_flBackfaceDarkening;
|
||||
}
|
||||
|
||||
#if S_GRAZING_FADE
|
||||
ApplyGrazingAngleFade( m.WorldPosition, viewDir, m.ScreenPosition.xy );
|
||||
#endif
|
||||
|
||||
#if S_TRANSMISSIVE
|
||||
float3 transmissiveColor = g_tTransmissiveColor.Sample( TextureFiltering, i.vTextureCoords.xy ).rgb;
|
||||
float transmission = dot( transmissiveColor, float3( 0.299, 0.587, 0.114 ) );
|
||||
#else
|
||||
float transmission = 0.0;
|
||||
#endif
|
||||
|
||||
#if S_ALPHA_TEST
|
||||
ApplyAlphaTest( m, dist );
|
||||
#endif
|
||||
|
||||
uint lightCount = Light::Count( m.WorldPosition );
|
||||
if ( lightCount > 0 )
|
||||
{
|
||||
Light light = Light::From( m.WorldPosition, 0, i.vLightmapUV );
|
||||
float wrapScale = light.Attenuation; // Wrap lighting ignores shadows
|
||||
float lightScale = light.Attenuation * light.Visibility;
|
||||
|
||||
ApplyWrappedLighting( m, light, wrapScale );
|
||||
|
||||
#if S_TRANSMISSIVE
|
||||
if ( closeUp )
|
||||
ApplyTranslucency( m, light, viewDir, transmissiveColor );
|
||||
#endif
|
||||
}
|
||||
|
||||
if ( closeUp )
|
||||
ApplyRimLighting( m, viewDir );
|
||||
|
||||
if ( g_flAmbientBoost > 0.0 )
|
||||
m.Emission += m.Albedo * g_flAmbientBoost;
|
||||
|
||||
return ShadingModelStandard::Shade( i, m );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user