diff --git a/manul/shaders/.gitignore b/manul/shaders/.gitignore new file mode 100644 index 00000000..a4949df6 --- /dev/null +++ b/manul/shaders/.gitignore @@ -0,0 +1,4 @@ +* + +!*.hlsl* +!project.manul \ No newline at end of file diff --git a/manul/shaders/contact_shadows.hlsl b/manul/shaders/contact_shadows.hlsl new file mode 100644 index 00000000..49eeb93a --- /dev/null +++ b/manul/shaders/contact_shadows.hlsl @@ -0,0 +1,149 @@ +Texture2D g_DepthTexture : register(t0); +RWTexture2D g_Output : register(u0); +sampler g_SamplerLinearClamp : register(s0); + +// From https://www.shadertoy.com/view/3tB3z3 - except we're using R2 here +#define XE_HILBERT_LEVEL 6U +#define XE_HILBERT_WIDTH ( (1U << XE_HILBERT_LEVEL) ) +#define XE_HILBERT_AREA ( XE_HILBERT_WIDTH * XE_HILBERT_WIDTH ) +inline uint HilbertIndex( uint posX, uint posY ) +{ + uint index = 0U; + for( uint curLevel = XE_HILBERT_WIDTH/2U; curLevel > 0U; curLevel /= 2U ) + { + uint regionX = ( posX & curLevel ) > 0U; + uint regionY = ( posY & curLevel ) > 0U; + index += curLevel * curLevel * ( (3U * regionX) ^ regionY); + if( regionY == 0U ) + { + if( regionX == 1U ) + { + posX = uint( (XE_HILBERT_WIDTH - 1U) ) - posX; + posY = uint( (XE_HILBERT_WIDTH - 1U) ) - posY; + } + + uint temp = posX; + posX = posY; + posY = temp; + } + } + return index; +} + +// Engine-specific screen & temporal noise loader +float2 SpatioTemporalNoise( uint2 pixCoord, uint temporalIndex ) // without TAA, temporalIndex is always 0 +{ + float2 noise; +#if 1 // Hilbert curve driving R2 (see https://www.shadertoy.com/view/3tB3z3) + #ifdef XE_GTAO_HILBERT_LUT_AVAILABLE // load from lookup texture... + uint index = g_srcHilbertLUT.Load( uint3( pixCoord % 64, 0 ) ).x; + #else // ...or generate in-place? + uint index = HilbertIndex( pixCoord.x, pixCoord.y ); + #endif + index += 288*(temporalIndex%64); // why 288? tried out a few and that's the best so far (with XE_HILBERT_LEVEL 6U) - but there's probably better :) + // R2 sequence - see http://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/ + return float2( frac( 0.5 + index * float2(0.75487766624669276005, 0.5698402909980532659114) ) ); +#else // Pseudo-random (fastest but looks bad - not a good choice) + uint baseHash = Hash32( pixCoord.x + (pixCoord.y << 15) ); + baseHash = Hash32Combine( baseHash, temporalIndex ); + return float2( Hash32ToFloat( baseHash ), Hash32ToFloat( Hash32( baseHash ) ) ); +#endif +} + +cbuffer DrawConstants : register(b0) { + float4x4 g_Projection; + float4x4 g_InverseProjection; + float3 g_LightDirView; + float g_NumSamples; + float g_SampleRange; + float g_Thickness; + uint g_FrameIndex; +} + +uint2 NdcToPixel(in float2 size, in float4 ndc); +float2 NdcToUv(in float4 ndc); +float4 PixelToNdc(in float2 size, in uint2 pixel, in float depth); +float4 UvToNdc(in float2 uv, in float depth); +uint2 ViewToPixel(in float2 size, in float3 view); +float2 ViewToUv(in float3 view); +float GetPixelDepth(in float2 size, in uint2 pixel); +float GetUvDepth(in float2 uv); +float3 PixelToViewWithDepth(in float2 size, in uint2 pixel); +float3 UvToViewWithDepth(in float2 uv); + +[numthreads(8, 8, 1)] +void main(uint3 PixCoord : SV_DispatchThreadID) { + float2 size; + g_DepthTexture.GetDimensions(size.x, size.y); + uint2 pixel = PixCoord.xy; + + float occlusion = 0; + g_Output[pixel] = 1.; + + float2 noise = SpatioTemporalNoise(PixCoord.xy, g_FrameIndex); + float3 viewPosition = PixelToViewWithDepth(size, pixel); + float3 sample = viewPosition; + float3 step = g_LightDirView * g_SampleRange / g_NumSamples; + sample += noise.x * step; + for(int i = 0; i < g_NumSamples; ++i) { + sample += step; + float2 sample_uv = ViewToUv(sample); + if(all(and(sample_uv > 0, sample_uv < 1.))) { + float depthDelta = sample.z * .998 - GetUvDepth(sample_uv); + if(depthDelta < 0. && depthDelta > -g_Thickness) { + occlusion = 1.; + break; + } + } + } + g_Output[pixel] = 1. - occlusion; +} + +uint2 NdcToPixel(in float2 size, in float4 ndc) { + return (uint2)floor(NdcToUv(ndc) * size); +} + +float2 NdcToUv(in float4 ndc) { + return ndc.xy * float2(.5, -.5) + .5; +} + +float4 PixelToNdc(in float2 size, in uint2 pixel, in float ndc_depth) { + return float4(float2(2., -2.) * ((((float2)pixel + .5) / size) - .5), ndc_depth, 1.); +} + +float4 UvToNdc(in float2 uv, in float ndc_depth) { + return float4(float2(2., -2.) * (uv - .5), ndc_depth, 1.); +} + +uint2 ViewToPixel(in float2 size, in float3 view) { + float4 ndc = mul(g_Projection, float4(view, 1.)); + ndc /= ndc.w; + return NdcToPixel(size, ndc); +} + +float2 ViewToUv(in float3 view) { + float4 ndc = mul(g_Projection, float4(view, 1.)); + ndc /= ndc.w; + return NdcToUv(ndc); +} + +float GetPixelDepth(in float2 size, in uint2 pixel) { + return PixelToViewWithDepth(size, pixel).z; +} + +float GetUvDepth(in float2 uv) { + return UvToViewWithDepth(uv).z; +} + +float3 PixelToViewWithDepth(in float2 size, in uint2 pixel) { + float4 ndc = PixelToNdc(size, pixel, g_DepthTexture[pixel]); + ndc = mul(g_InverseProjection, ndc); + return ndc.xyz / ndc.w; +} + +float3 UvToViewWithDepth(in float2 uv) { + float4 ndc = UvToNdc(uv, g_DepthTexture.SampleLevel(g_SamplerLinearClamp, uv, 0.)); + ndc = mul(g_InverseProjection, ndc); + return ndc.xyz / ndc.w; +} + diff --git a/manul/shaders/copy.hlsl b/manul/shaders/copy.hlsl new file mode 100644 index 00000000..ab9b9c1d --- /dev/null +++ b/manul/shaders/copy.hlsl @@ -0,0 +1,18 @@ + +struct VertexOutput { + float2 m_Position : Position; + float4 m_PositionSV : SV_Position; +}; + +struct PixelOutput { + float4 m_Output : SV_Target0; +}; + +sampler source_sampler : register(s0); +Texture2D source : register(t0); + +PixelOutput main(VertexOutput ps_in) { + PixelOutput result; + result.m_Output = source.Sample(source_sampler, ps_in.m_Position); + return result; +} diff --git a/manul/shaders/cubemap_utils.hlsli b/manul/shaders/cubemap_utils.hlsli new file mode 100644 index 00000000..1484b5b0 --- /dev/null +++ b/manul/shaders/cubemap_utils.hlsli @@ -0,0 +1,52 @@ +#define PI 3.1415926535897932384626433832795 +#define TWO_PI 6.283185307179586476925286766559 +#define ONE_OVER_PI 0.31830988618379067153776752674503 +#define TWO_OVER_PI 0.63661977236758134307553505349006 +#define ONE_OVER_TWO_PI 0.15915494309189533576888376337251 + +float3 CalcNormal(in uint3 PixCoord); + +float2 EquirectFromNormal(in float3 Normal); + +float2 PixCoordToFloat(in uint2 Coord, in uint2 Size); + +float2 PixCoordToFloat(in uint2 Coord, in uint2 Size) { + return ((float2)Coord + .5.xx) / (float2)Size; +} + +float3 CalcNormal(in uint3 PixCoord) { + static const float3x3 FaceTransform[6] = { + // +X + float3x3( 0., 0., -1., + 0., -1., 0., + 1., 0., 0. ), + // -X + float3x3( 0., 0., 1., + 0., -1., 0., + -1., 0., 0. ), + // +Y + float3x3( 1., 0., 0., + 0., 0., 1., + 0., 1., 0. ), + // -Y + float3x3( 1., 0., 0., + 0., 0., -1., + 0., -1., 0. ), + // +Z + float3x3( 1., 0., 0., + 0., -1., 0., + 0., 0., 1. ), + // -Z + float3x3( -1., 0., 0., + 0., -1., 0., + 0., 0., -1. ) + }; + uint2 FaceSize; + uint Elements; + g_OutCubemap.GetDimensions(FaceSize.x, FaceSize.y, Elements); + return normalize(mul(float3(PixCoordToFloat(PixCoord.xy, FaceSize) * 2. - 1., 1.), FaceTransform[PixCoord.z])); +} + +float2 EquirectFromNormal(in float3 Normal) { + return float2(atan2(Normal.x, Normal.z) * ONE_OVER_TWO_PI, -asin(Normal.y) * ONE_OVER_PI) + .5.xx; +} \ No newline at end of file diff --git a/manul/shaders/cubemap_vertex.hlsl b/manul/shaders/cubemap_vertex.hlsl new file mode 100644 index 00000000..5c5ff2a8 --- /dev/null +++ b/manul/shaders/cubemap_vertex.hlsl @@ -0,0 +1,39 @@ +#pragma pack_matrix(column_major) + +struct VertexInput { + float3 m_Position : Position; + float3 m_Normal : Normal; + float2 m_TexCoord : TexCoord; + float4 m_Tangent : Tangent; +}; + +struct VertexOutput { + float3 m_Position : Position; + float3 m_Normal : Normal; + float2 m_TexCoord : TexCoord; + float4 m_Tangent : Tangent; + float4 m_PositionSV : SV_Position; + float4 m_PositionCS : PositionCS; + float4 m_HistoryPositionCS : HistoryPositionCS; +}; + +cbuffer VertexConstants : register(b0) { + float4x4 g_FaceProjection; +}; + +#include "manul/draw_constants.hlsli" + +VertexOutput main(in VertexInput vs_in) { + VertexOutput result; + float4x3 model_view = GetModelView(); + result.m_Position = mul(float4(vs_in.m_Position, 1.), model_view).xyz; + result.m_Normal = mul(float4(vs_in.m_Normal, 0.), model_view).xyz; + result.m_Normal = vs_in.m_Normal; + result.m_TexCoord = vs_in.m_TexCoord; + result.m_Tangent.xyz = vs_in.m_Tangent.xyz; + result.m_Tangent.w = vs_in.m_Tangent.w; + result.m_PositionSV = mul(g_FaceProjection, float4(result.m_Position, 1.)); + result.m_PositionCS = result.m_PositionSV; + result.m_HistoryPositionCS = result.m_PositionSV; + return result; +} diff --git a/manul/shaders/culling.hlsl b/manul/shaders/culling.hlsl new file mode 100644 index 00000000..47e31575 --- /dev/null +++ b/manul/shaders/culling.hlsl @@ -0,0 +1,43 @@ + +StructuredBuffer g_InTransforms : register(t0); +RWStructuredBuffer g_OutCulledTransforms : register(u0); + +struct CommandBuffer { + uint g_IndexCountPerInstance; + uint g_InstanceCount; + uint g_StartIndexLocation; + int g_BaseVertexLocation; + uint g_StartInstanceLocation; +}; + +RWStructuredBuffer g_CommandBuffer : register(u1); + +cbuffer ViewData : register(b0) { + float4 g_FrustumPlanes[6]; + float4x4 g_Projection; +} + +cbuffer PushConstants : register(b1) { + float3 g_Origin; + float g_InstanceRadius; + float g_MinRadius; + float g_MaxRadius; + uint g_NumInstances; +} + +[numthreads(64, 1, 1)] +void main(uint3 DispatchId : SV_DispatchThreadID) { + if(DispatchId.x >= g_NumInstances) return; + float4x3 instance = g_InTransforms[DispatchId.x]; + //float3 position = mul(float4(0., 0., 0., 1.), instance) + g_Origin; + //for(int i = 0; i < 6; ++i) { + // if(dot(float4(position, 1.), g_FrustumPlanes[i]) < -g_InstanceRadius) return; + //} + //float4 ndc = mul(g_Projection, float4(0., g_InstanceRadius, -length(position), 1.)); + //float radius = ndc.y / ndc.w; + //if(radius > g_MinRadius && radius <= g_MaxRadius) { + uint index; + InterlockedAdd(g_CommandBuffer[0].g_InstanceCount, 1, index); + g_OutCulledTransforms[index] = instance; + //} +} diff --git a/manul/shaders/default_vertex.hlsl b/manul/shaders/default_vertex.hlsl new file mode 100644 index 00000000..b7db5b0b --- /dev/null +++ b/manul/shaders/default_vertex.hlsl @@ -0,0 +1,42 @@ +#pragma pack_matrix(column_major) + +struct VertexInput { + float3 m_Position : Position; + float3 m_Normal : Normal; + float2 m_TexCoord : TexCoord; + float4 m_Tangent : Tangent; +}; + +struct VertexOutput { + float3 m_Position : Position; + float3 m_Normal : Normal; + float2 m_TexCoord : TexCoord; + float4 m_Tangent : Tangent; + float4 m_PositionSV : SV_Position; + float4 m_PositionCS : PositionCS; + float4 m_HistoryPositionCS : HistoryPositionCS; +}; + +cbuffer VertexConstants : register(b0) { + float4x4 g_JitteredProjection; + float4x4 g_Projection; + float4x4 g_ProjectionHistory; +}; + +#include "manul/draw_constants.hlsli" + +VertexOutput main(in VertexInput vs_in) { + VertexOutput result; + float4x3 model_view = GetModelView(); + float4x3 model_view_history = GetModelViewHistory(); + result.m_Position = mul(float4(vs_in.m_Position, 1.), model_view).xyz; + result.m_Normal = mul(float4(vs_in.m_Normal, 0.), model_view).xyz; + result.m_TexCoord = vs_in.m_TexCoord; + result.m_Tangent.xyz = mul(float4(vs_in.m_Tangent.xyz, 0.), model_view).xyz; + result.m_Tangent.w = vs_in.m_Tangent.w; + result.m_PositionSV = mul(g_JitteredProjection, float4(result.m_Position, 1.)); + result.m_PositionCS = mul(g_Projection, float4(result.m_Position, 1.)); + float3 history_position = mul(float4(vs_in.m_Position, 1.), model_view_history); + result.m_HistoryPositionCS = mul(g_ProjectionHistory, float4(history_position, 1.)); + return result; +} diff --git a/manul/shaders/envmap.hlsl b/manul/shaders/envmap.hlsl new file mode 100644 index 00000000..e9364ff9 --- /dev/null +++ b/manul/shaders/envmap.hlsl @@ -0,0 +1,213 @@ + +RWTexture2DArray g_OutCubemap : register(u0); +RWTexture2D g_OutBRDF : register(u1); + +Texture2D g_SourceEquirect : register(t0); +TextureCube g_SourceCube : register(t1); +Texture1D g_SampleKernel : register(t2); +SamplerState g_SamplerLinearClampV : register(s0); +SamplerState g_SamplerLinearClamp : register(s1); + +#include "cubemap_utils.hlsli" + +float DistributionGGX(float NdotH, float roughness); +float GeometrySchlickGGX(float NdotV, float roughness); +float GeometrySmith(float roughness, float NdotV, float NdotL); +float3 ImportanceSampleGGX(in float2 Xi,in float Roughness , in float3 N); +void ImportanceSampleCosDir(in float2 u, in float3 N, out float3 L, out float NdotL, out float pdf); + +float2 GetSample(uint sample, uint sampleCount); + +struct FilterParameters { + float m_PreExposureMul; + float m_Roughness; + float m_SampleCount; + float m_SourceWidth; + float m_MipBias; + uint3 m_Offset; +}; + +#ifdef SPIRV + +[[vk::push_constant]] ConstantBuffer g_FilterParams; + +#else + +cbuffer g_Const : register(b0) { FilterParameters g_FilterParams; } + +#endif + +#include "manul/sky.hlsli" + +[numthreads(32, 32, 1)] +void CSSampleEquirectangular(uint3 PixCoord : SV_DispatchThreadID) { + //Sky(g_OutCubemap[PixCoord + g_FilterParams.m_Offset], CalcNormal(PixCoord + g_FilterParams.m_Offset), normalize(float3(0., .1, 1.)), 0.); + g_OutCubemap[PixCoord + g_FilterParams.m_Offset].rgb = clamp(g_SourceEquirect.SampleLevel(g_SamplerLinearClampV, EquirectFromNormal(CalcNormal(PixCoord)), 0.) * g_FilterParams.m_PreExposureMul, 0., 65535.); +} + +[numthreads(32, 32, 1)] +void CSDiffuseIBL(uint3 PixCoord : SV_DispatchThreadID) { + float3 N = CalcNormal(PixCoord + g_FilterParams.m_Offset); + + float sampleCount = g_FilterParams.m_SampleCount; + float width = g_FilterParams.m_SourceWidth; + float mipBias = g_FilterParams.m_MipBias; + + float3 accBrdf = 0; + for ( uint i =0; i < sampleCount ; ++ i ) { + float2 eta = GetSample (i , sampleCount ) ; + float3 L ; + float NdotL ; + float pdf ; + ImportanceSampleCosDir ( eta , N , L , NdotL , pdf ) ; + if ( NdotL >0) { + float omegaS = 1. / ( sampleCount * pdf ); + float omegaP = 4. * PI / (6. * width * width ); + float mipLevel = max(0., mipBias + .5 * log2 ( omegaS / omegaP )); + accBrdf += g_SourceCube . SampleLevel ( g_SamplerLinearClamp , L, mipLevel ) ; + } + } + g_OutCubemap[PixCoord + g_FilterParams.m_Offset].rgb = accBrdf * (1. / sampleCount); +} + +[numthreads(32, 32, 1)] +void CSSpecularIBL(uint3 PixCoord : SV_DispatchThreadID) { + float3 N = CalcNormal(PixCoord + g_FilterParams.m_Offset); + + float roughness = g_FilterParams.m_Roughness; + float width = g_FilterParams.m_SourceWidth; + float sampleCount = g_FilterParams.m_SampleCount; + float mipBias = g_FilterParams.m_MipBias; + + float3 accBrdf = 0; + float accWeight = 0; + for ( uint i =0; i < sampleCount ; ++ i ) { + float2 eta = GetSample (i , sampleCount ) ; + float pdf ; + float3 H = ImportanceSampleGGX(eta, roughness, N); + float3 L = normalize(2 * dot( N, H ) * H - N); + float NdotL = dot(N, L); + float NdotH = dot(N, H); + if(NdotL > 0) { + float D = DistributionGGX(NdotH, roughness); + float pdf = D / 4.; + float omegaS = 1. / ( sampleCount * pdf ); + float omegaP = 4. * PI / (6. * width * width ); + float mipLevel = roughness == 0. ? mipBias : max(0., mipBias + .5 * log2 ( omegaS / omegaP )); + accBrdf += g_SourceCube . SampleLevel ( g_SamplerLinearClamp , L, mipLevel ) * NdotL; + accWeight += NdotL; + } + } + g_OutCubemap[PixCoord + g_FilterParams.m_Offset].rgb = accBrdf * (1. / accWeight); +} + +[numthreads(32, 32, 1)] +void CSIntegrateBRDF(uint3 PixCoord : SV_DispatchThreadID) { + uint2 FaceSize; + g_OutBRDF.GetDimensions(FaceSize.x, FaceSize.y); + float NoV = ((float)PixCoord.x + .5) / (float)FaceSize.x; + float Roughness = ((float)PixCoord.y + .5) / (float)FaceSize.y; + float3 N = float3(0., 0., 1.); + + float3 V; + V.x = sqrt( 1. - NoV * NoV ); // sin + V.y = 0.; + V.z = NoV; // cos + float A = 0.; + float B = 0.; + float sampleCount = 1024.; + for( uint i = 0; i < sampleCount; i++ ) + { + float2 Xi = GetSample (i , sampleCount ); + float3 H = ImportanceSampleGGX(Xi, Roughness, N); + float3 L = normalize(2. * dot( V, H ) * H - V); + float NoL = saturate( L.z ); + float NoH = saturate( H.z ); + float VoH = saturate( dot( V, H ) ); + if( NoL > 0. ) + { + float G = GeometrySmith( Roughness, NoV, NoL ); + float G_Vis = G * VoH / (NoH * NoV); + float Fc = pow( 1. - VoH, 5. ); + A += (1. - Fc) * G_Vis; + B += Fc * G_Vis; + } + } + g_OutBRDF[PixCoord.xy] = float2( A, B ) / sampleCount; +} + +[numthreads(32, 32, 1)] +void CSGenerateCubeMip(uint3 PixCoord : SV_DispatchThreadID) { + g_OutCubemap[PixCoord + g_FilterParams.m_Offset].rgb = g_SourceCube.SampleLevel(g_SamplerLinearClamp, CalcNormal(PixCoord + g_FilterParams.m_Offset), 0.); +} + +float GeometrySchlickGGX(float NdotV, float roughness) +{ + // note that we use a different k for IBL + float a = roughness; + float k = (a * a) / 2.0; + + float nom = NdotV; + float denom = NdotV * (1.0 - k) + k; + + return nom / denom; +} + +float GeometrySmith(float roughness, float NdotV, float NdotL) +{ + float ggx2 = GeometrySchlickGGX(NdotV, roughness); + float ggx1 = GeometrySchlickGGX(NdotL, roughness); + + return ggx1 * ggx2; +} + +float DistributionGGX(float NdotH, float roughness) +{ + float a = roughness*roughness; + float a2 = a*a; + + float nom = a2; + float denom = (NdotH*NdotH * (a2 - 1.0) + 1.0); + denom = PI * denom * denom; + + return nom / denom; +} + +void ImportanceSampleCosDir (in float2 Xi, in float3 N, out float3 L, out float NdotL, out float pdf) { + // Local referencial + float3 upVector = abs (N.z) < .999 ? float3 (0., 0., 1.) : float3 (1., 0., 0.) ; + float3 tangentX = normalize ( cross ( upVector , N ) ) ; + float3 tangentY = cross ( N , tangentX ) ; + + float r = sqrt ( Xi.x ) ; + float phi = Xi.y * TWO_PI; + + L = float3 ( r * cos ( phi ) , r * sin ( phi ) , sqrt ( max (0. ,1. - Xi.x ) ) ) ; + L = normalize ( tangentX * L . y + tangentY * L . x + N * L . z ) ; + + NdotL = dot (L , N ) ; + pdf = NdotL * ONE_OVER_PI ; +} + +float3 ImportanceSampleGGX(in float2 Xi,in float Roughness , in float3 N) +{ + double a = Roughness * Roughness; + float Phi = TWO_PI * Xi.x; + float CosTheta = sqrt( (float)((1. - Xi.y) / ( 1. + (a*a - 1.) * Xi.y ) )); + float SinTheta = sqrt( 1. - CosTheta * CosTheta ); + float3 H = float3( SinTheta * cos( Phi ), SinTheta * sin( Phi ), CosTheta); + float3 UpVector = abs(N.z) < .999 ? float3(0.,0.,1.) : float3(1.,0.,0.); + float3 TangentX = normalize( cross( UpVector , N ) ); + float3 TangentY = cross( N, TangentX ); + // Tangent to world space + return normalize( TangentX * H.x + TangentY * H.y + N * H.z ); +} + +float RadicalInverse_VdC(uint bits) +{ + return float(reversebits(bits)) * 2.3283064365386963e-10; // / 0x100000000 +} + +float2 GetSample(uint i, uint N) { + return float2((float)i/(float)N, RadicalInverse_VdC(i)); +} \ No newline at end of file diff --git a/manul/shaders/fx_vertex.hlsl b/manul/shaders/fx_vertex.hlsl new file mode 100644 index 00000000..65f97b4b --- /dev/null +++ b/manul/shaders/fx_vertex.hlsl @@ -0,0 +1,16 @@ + +struct VertexInput { + float2 m_Position : Position; +}; + +struct VertexOutput { + float2 m_Position : Position; + float4 m_PositionSV : SV_Position; +}; + +VertexOutput main(in VertexInput vs_in) { + VertexOutput result; + result.m_Position = vs_in.m_Position; + result.m_PositionSV = float4(vs_in.m_Position * float2(2., -2.) + float2(-1., 1.), 0., 1.); + return result; +} diff --git a/manul/shaders/gbuffer_cube_lighting.hlsl b/manul/shaders/gbuffer_cube_lighting.hlsl new file mode 100644 index 00000000..1f37604e --- /dev/null +++ b/manul/shaders/gbuffer_cube_lighting.hlsl @@ -0,0 +1,69 @@ +#define PI 3.1415926535897932384626433832795 +#define TWO_PI 6.283185307179586476925286766559 +#define ONE_OVER_PI 0.31830988618379067153776752674503 +#define TWO_OVER_PI 0.63661977236758134307553505349006 +#define ONE_OVER_TWO_PI 0.15915494309189533576888376337251 + +RWTexture2DArray g_OutCubemap : register(u0); + +TextureCube g_Skybox : register(t0); +TextureCube g_Diffuse : register(t1); +TextureCube g_Normal : register(t2); +TextureCube g_Params : register(t3); +TextureCube g_Depth : register(t4); +TextureCube g_DiffuseIBL : register(t5); + +SamplerState g_SamplerLinearClamp : register(s0); +SamplerState g_SamplerPointClamp : register(s1); + +#include "cubemap_utils.hlsli" +#include "manul/sky.hlsli" + +struct FilterParameters { + uint3 m_Offset; + uint unused; + float3 m_LightVector; + float m_Altitude; + float3 m_LightColor; +}; + +#ifdef SPIRV + +[[vk::push_constant]] ConstantBuffer g_FilterParams; + +#else + +cbuffer g_Const : register(b0) { FilterParameters g_FilterParams; } + +#endif + +cbuffer FilterConstants : register(b1) { + float4x4 g_InverseProjection; +}; + +[numthreads(32, 32, 1)] +void main(uint3 PixCoord : SV_DispatchThreadID) { + float3 normal = CalcNormal(PixCoord + g_FilterParams.m_Offset); + float3 size; + g_OutCubemap.GetDimensions(size.x, size.y, size.z); + + //g_OutCubemap[PixCoord + g_Offset] = g_Skybox.SampleLevel(g_SamplerLinearClamp, normal, 0.); + float3 color = 1.e-7; + CalcAtmosphere(color, normal, g_FilterParams.m_LightVector); + //CalcAtmosphere(g_OutCubemap[PixCoord + g_Offset], 1., normal, g_LightVector, g_Altitude, SKY_INF, g_LightColor.rgb, 10); + float3 normal_flipped = normal * float3(-1., 1., 1.); + float depth = g_Depth.SampleLevel(g_SamplerPointClamp, normal_flipped, 0.); + float linear_depth = 2500.; + if(depth > 0.) { + float3 material_albedo = g_Diffuse.SampleLevel(g_SamplerPointClamp, normal_flipped, 0.); + float4 material_params = g_Params.SampleLevel(g_SamplerPointClamp, normal_flipped, 0.); + float3 material_normal = (g_Normal.SampleLevel(g_SamplerPointClamp, normal_flipped, 0.) - .5) * 2.; + float NdotL = max(dot(material_normal, g_FilterParams.m_LightVector), 0.); + color = material_albedo * (g_DiffuseIBL.SampleLevel(g_SamplerPointClamp, material_normal, 0.) + ONE_OVER_PI * NdotL * g_FilterParams.m_LightColor.rgb) * material_params.b; + float4 position_ndc = mul(g_InverseProjection, float4(PixCoordToFloat(PixCoord.xy + g_FilterParams.m_Offset.xy, size.xy) * 2. - 1., depth, 1.)); + position_ndc /= position_ndc.w; + linear_depth = length(position_ndc.xyz); + } + ApplyAerialPerspective(color, 1., normal, g_FilterParams.m_LightVector, linear_depth/2500.); + g_OutCubemap[PixCoord + g_FilterParams.m_Offset].rgb = color; +} diff --git a/manul/shaders/gbufferblit.hlsl b/manul/shaders/gbufferblit.hlsl new file mode 100644 index 00000000..2791d950 --- /dev/null +++ b/manul/shaders/gbufferblit.hlsl @@ -0,0 +1,118 @@ +#include "manul/math.hlsli" + +struct VertexOutput { + float2 m_Position : Position; + float4 m_PositionSV : SV_Position; +}; + +struct PixelOutput { + float4 m_Color : SV_Target0; +}; + +Texture2D gbuffer_diffuse : register(t0); +Texture2D gbuffer_emission : register(t1); +Texture2D gbuffer_params : register(t2); +Texture2D gbuffer_normal : register(t3); +Texture2D gbuffer_depth : register(t4); + +RWTexture2D output : register(u0); + +#define DEFERRED_LIGHTING_PASS + +#include "manul/gbuffer_ssao.hlsli" +//#include "manul/gbuffer_contact_shadows.hlsli" +#include "manul/shadow.hlsli" +#include "manul/lighting.hlsli" +#include "manul/sky.hlsli" + +#define BLOCK_SIZE 8 +#define TILE_BORDER 1 +#define TILE_SIZE (BLOCK_SIZE + 2 * TILE_BORDER) + +groupshared float2 tile_XY[TILE_SIZE*TILE_SIZE]; +groupshared float tile_Z[TILE_SIZE*TILE_SIZE]; + +uint2 unflatten2D(uint idx, uint2 dim) +{ + return uint2(idx % dim.x, idx / dim.x); +} + +uint flatten2D(uint2 coord, uint2 dim) +{ + return coord.x + coord.y * dim.x; +} + +[numthreads(BLOCK_SIZE, BLOCK_SIZE, 1)] +void main(uint3 PixCoord : SV_DispatchThreadID, uint3 GroupID : SV_GroupID, uint GroupIndex : SV_GroupIndex) { + uint2 gbuffer_dimensions; + gbuffer_depth.GetDimensions(gbuffer_dimensions.x, gbuffer_dimensions.y); + + const int2 tile_upperleft = GroupID.xy * BLOCK_SIZE - TILE_BORDER; + for (uint t = GroupIndex; t < TILE_SIZE * TILE_SIZE; t += BLOCK_SIZE * BLOCK_SIZE) + { + const uint2 pixel = tile_upperleft + unflatten2D(t, TILE_SIZE); + const float depth = gbuffer_depth[pixel]; + const float3 position = ReconstructPos(PixelToCS(pixel, gbuffer_dimensions), depth); + tile_XY[t] = position.xy; + tile_Z[t] = position.z; + } + GroupMemoryBarrierWithGroupSync(); +// Decode material data + MaterialData material; + + material.m_PixelCoord = PixCoord.xy; + float2 uv = ( material.m_PixelCoord + .5) / gbuffer_dimensions; + + uint2 tile_co = material.m_PixelCoord - tile_upperleft; + + //float depth = gbuffer_depth[ material.m_PixelCoord]; + uint co = flatten2D(tile_co, TILE_SIZE); + + uint co_px = flatten2D(tile_co + int2(1, 0), TILE_SIZE); + uint co_nx = flatten2D(tile_co + int2(-1, 0), TILE_SIZE); + uint co_py = flatten2D(tile_co + int2(0, 1), TILE_SIZE); + uint co_ny = flatten2D(tile_co + int2(0, -1), TILE_SIZE); + + float depth = tile_Z[co]; + float depth_px = tile_Z[co_px]; + float depth_nx = tile_Z[co_nx]; + float depth_py = tile_Z[co_py]; + float depth_ny = tile_Z[co_ny]; + + material.m_Position = float3(tile_XY[co], depth); + if(abs(depth_px - depth) < abs(depth_nx - depth)) { + material.m_PositionDDX.xy = tile_XY[co_px]; + material.m_PositionDDX.z = depth_px; + } + else{ + material.m_PositionDDX.xy = tile_XY[co_nx]; + material.m_PositionDDX.z = depth_nx; + } + if(abs(depth_py - depth) < abs(depth_ny - depth)) { + material.m_PositionDDY.xy = tile_XY[co_py]; + material.m_PositionDDY.z = depth_py; + } + else{ + material.m_PositionDDY.xy = tile_XY[co_ny]; + material.m_PositionDDY.z = depth_ny; + } + + material.m_MaterialAlbedoAlpha.rgb = gbuffer_diffuse[ material.m_PixelCoord]; + material.m_MaterialAlbedoAlpha.a = 1.; + material.m_MaterialEmission = gbuffer_emission[ material.m_PixelCoord]; + material.m_MaterialParams = gbuffer_params[ material.m_PixelCoord]; + material.m_MaterialNormal = UnpackNormalXYZ(gbuffer_normal[ material.m_PixelCoord]); + + PixelOutput ps_out; +#if LIGHTING_NEEDS_PIXELPOSITION + ApplyMaterialLighting(output[material.m_PixelCoord], material, material.m_PixelCoord); +#else + ApplyMaterialLighting(output[material.m_PixelCoord], material); +#endif + float3 view_world = mul((float3x3)g_InverseModelView, material.m_Position); + ApplyAerialPerspective(output[material.m_PixelCoord].rgb, 1., normalize(view_world), g_LightDir, length(view_world)/2500.); + //if(depth < 1.){ + // CalcAtmosphere(ps_out.m_Color.rgb, 1., normalize(mul((float3x3) g_InverseModelView, material.m_Position)), g_LightDir.xyz, g_Altitude, length(view.xyz), g_LightColor.rgb, 10); + //} + //return ps_out; +} diff --git a/manul/shaders/gs_cubemap_viewports.hlsl b/manul/shaders/gs_cubemap_viewports.hlsl new file mode 100644 index 00000000..5e4c0048 --- /dev/null +++ b/manul/shaders/gs_cubemap_viewports.hlsl @@ -0,0 +1,46 @@ +#pragma pack_matrix(column_major) + +struct VertexInput { + float3 m_Position : Position; + float4 m_PositionCS : PositionCS; + float4 m_HistoryPositionCS : HistoryPositionCS; + float3 m_Normal : Normal; + float2 m_TexCoord : TexCoord; + float4 m_Tangent : Tangent; + float4 m_PositionSV : SV_Position; +}; + +struct VertexOutput { + float3 m_Position : Position; + float4 m_PositionCS : PositionCS; + float4 m_HistoryPositionCS : HistoryPositionCS; + float3 m_Normal : Normal; + float2 m_TexCoord : TexCoord; + float4 m_Tangent : Tangent; + float4 m_PositionSV : SV_Position; + uint m_ViewportIndex : SV_RenderTargetArrayIndex; +}; + +cbuffer CubeDrawConstants : register(b0) { + float4x4 g_FaceProjections[6]; +}; + +[maxvertexcount(18)] +void main(triangle in VertexInput gs_in[3], inout TriangleStream gs_out) { + for(uint vp = 0; vp < 6; ++vp) { + for(uint vertex = 0; vertex < 3; ++vertex) { + VertexOutput output; + VertexInput input = gs_in[2 - vertex]; + output.m_Position = input.m_Position; + output.m_PositionCS = mul(g_FaceProjections[vp], float4(output.m_Position * float3(-1., 1., 1.), 1.)); + output.m_HistoryPositionCS = output.m_PositionCS; + output.m_PositionSV = output.m_PositionCS; + output.m_Normal = input.m_Normal; + output.m_TexCoord = input.m_TexCoord; + output.m_Tangent = input.m_Tangent; + output.m_ViewportIndex = vp; + gs_out.Append(output); + } + gs_out.RestartStrip(); + } +} diff --git a/manul/shaders/gs_shadowcascade_viewports.hlsl b/manul/shaders/gs_shadowcascade_viewports.hlsl new file mode 100644 index 00000000..d9754ed2 --- /dev/null +++ b/manul/shaders/gs_shadowcascade_viewports.hlsl @@ -0,0 +1,48 @@ +#pragma pack_matrix(column_major) + +struct VertexInput { + float3 m_Position : Position; + float4 m_PositionCS : PositionCS; + float4 m_HistoryPositionCS : HistoryPositionCS; + float3 m_Normal : Normal; + float2 m_TexCoord : TexCoord; + float4 m_Tangent : Tangent; + float4 m_PositionSV : SV_Position; +}; + +struct VertexOutput { + float3 m_Position : Position; + float4 m_PositionCS : PositionCS; + float4 m_HistoryPositionCS : HistoryPositionCS; + float3 m_Normal : Normal; + float2 m_TexCoord : TexCoord; + float4 m_Tangent : Tangent; + float4 m_PositionSV : SV_Position; + uint m_ViewportIndex : SV_RenderTargetArrayIndex; +}; + +#define CASCADE_COUNT 4 + +cbuffer CubeDrawConstants : register(b0) { + float4x4 g_CascadeProjections[CASCADE_COUNT]; +}; + +[maxvertexcount(CASCADE_COUNT*3)] +void main(triangle in VertexInput gs_in[3], inout TriangleStream gs_out) { + for(uint vp = 0; vp < CASCADE_COUNT; ++vp) { + for(uint vertex = 0; vertex < 3; ++vertex) { + VertexOutput output; + VertexInput input = gs_in[vertex]; + output.m_Position = input.m_Position; + output.m_PositionCS = mul(g_CascadeProjections[vp], float4(output.m_Position, 1.)); + output.m_HistoryPositionCS = output.m_PositionCS; + output.m_PositionSV = output.m_PositionCS; + output.m_Normal = input.m_Normal; + output.m_TexCoord = input.m_TexCoord; + output.m_Tangent = input.m_Tangent; + output.m_ViewportIndex = vp; + gs_out.Append(output); + } + gs_out.RestartStrip(); + } +} diff --git a/manul/shaders/imgui_pixel.hlsl b/manul/shaders/imgui_pixel.hlsl new file mode 100644 index 00000000..8cc407f8 --- /dev/null +++ b/manul/shaders/imgui_pixel.hlsl @@ -0,0 +1,109 @@ +/* +* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#define MA_TONEMAP_SRGB_REC709 0 +#define MA_TONEMAP_LINEAR_REC2020 1 + +struct Constants +{ + float2 invDisplaySize; + float outputNits; + int m_TonemapFunction; +}; + +#ifdef SPIRV + +[[vk::push_constant]] ConstantBuffer g_Const; + +#else + +cbuffer g_Const : register(b0) { Constants g_Const; } + +#endif + +struct PS_INPUT +{ + float4 pos : SV_POSITION; + float4 col : COLOR0; + float2 uv : TEXCOORD0; +}; + +float3 LinearToSRGB(float3 color) +{ + // Approximately pow(color, 1.0 / 2.2) + return select(color < 0.0031308, 12.92 * color, 1.055 * pow(abs(color), 1.0 / 2.4) - 0.055); +} + +float3 SRGBToLinear(float3 color) +{ + // Approximately pow(color, 2.2) + return select(color < 0.04045, color / 12.92, pow(abs(color + 0.055) / 1.055, 2.4)); +} + +float3 Rec709ToRec2020(float3 color) +{ + static const float3x3 conversion = + { + 0.627402, 0.329292, 0.043306, + 0.069095, 0.919544, 0.011360, + 0.016394, 0.088028, 0.895578 + }; + return mul(conversion, color); +} + +float3 LinearToST2084(float3 color) +{ + float m1 = 2610.0 / 4096.0 / 4; + float m2 = 2523.0 / 4096.0 * 128; + float c1 = 3424.0 / 4096.0; + float c2 = 2413.0 / 4096.0 * 32; + float c3 = 2392.0 / 4096.0 * 32; + float3 cp = pow(abs(color), m1); + return pow((c1 + c2 * cp) / (1 + c3 * cp), m2); +} + +sampler sampler0 : register(s0); +Texture2D texture0 : register(t0); + +float4 main(PS_INPUT input) : SV_Target +{ + float4 tex_col = texture0.Sample(sampler0, input.uv); + float4 input_col = input.col; + switch(g_Const.m_TonemapFunction) { + case MA_TONEMAP_LINEAR_REC2020: + { + input_col.rgb = SRGBToLinear(input_col.rgb); + float4 out_col = input_col * tex_col; + const float st2084max = 10000.; + const float hdrScalar = g_Const.outputNits / st2084max; + //out_col.rgb = Rec709ToRec2020(out_col.rgb); + out_col.rgb *= hdrScalar; + out_col.rgb = LinearToST2084(out_col.rgb); + return out_col; + } + default: + { + tex_col.rgb = LinearToSRGB(tex_col.rgb); + return input_col * tex_col; + } + } +} diff --git a/manul/shaders/imgui_vertex.hlsl b/manul/shaders/imgui_vertex.hlsl new file mode 100644 index 00000000..42a7fad1 --- /dev/null +++ b/manul/shaders/imgui_vertex.hlsl @@ -0,0 +1,62 @@ +/* +* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +struct Constants +{ + float2 invDisplaySize; + float outputNits; +}; + +#ifdef SPIRV + +[[vk::push_constant]] ConstantBuffer g_Const; + +#else + +cbuffer g_Const : register(b0) { Constants g_Const; } + +#endif + +struct VS_INPUT +{ + float2 pos : POSITION; + float2 uv : TEXCOORD0; + float4 col : COLOR0; +}; + +struct PS_INPUT +{ + float4 out_pos : SV_POSITION; + float4 out_col : COLOR0; + float2 out_uv : TEXCOORD0; +}; + +PS_INPUT main(VS_INPUT input) +{ + PS_INPUT output; + output.out_pos.xy = input.pos.xy * g_Const.invDisplaySize * float2(2.0, -2.0) + float2(-1.0, 1.0); + output.out_pos.zw = float2(0, 1); + output.out_col = input.col; + output.out_uv = input.uv; + return output; +} + diff --git a/manul/shaders/instanced_cubemap_vertex.hlsl b/manul/shaders/instanced_cubemap_vertex.hlsl new file mode 100644 index 00000000..8ba45d7c --- /dev/null +++ b/manul/shaders/instanced_cubemap_vertex.hlsl @@ -0,0 +1,41 @@ +#pragma pack_matrix(column_major) + +struct VertexInput { + float3 m_Position : Position; + float3 m_Normal : Normal; + float2 m_TexCoord : TexCoord; + float4 m_Tangent : Tangent; + float4 m_InstanceTransform[3] : InstanceTransform; +}; + +struct VertexOutput { + float3 m_Position : Position; + float3 m_Normal : Normal; + float2 m_TexCoord : TexCoord; + float4 m_Tangent : Tangent; + float4 m_PositionSV : SV_Position; + float4 m_PositionCS : PositionCS; + float4 m_HistoryPositionCS : HistoryPositionCS; +}; + +cbuffer VertexConstants : register(b0) { + float4x4 g_FaceProjection; +}; + +#include "manul/draw_constants.hlsli" + +VertexOutput main(in VertexInput vs_in) { + VertexOutput result; + float4x3 model_view = GetModelView(); + float4x4 instance_transform = transpose(float4x4(vs_in.m_InstanceTransform[0], vs_in.m_InstanceTransform[1], vs_in.m_InstanceTransform[2], float4(0., 0., 0., 1.))); + float3 position = mul(float4(vs_in.m_Position, 1.), instance_transform).xyz; + result.m_Position = mul(float4(position, 1.), model_view).xyz; + result.m_Normal = mul(float4(mul(float4(vs_in.m_Normal, 0.), instance_transform)), model_view).xyz; + result.m_TexCoord = vs_in.m_TexCoord; + result.m_Tangent.xyz = mul(float4(mul(float4(vs_in.m_Tangent.xyz, 0.), instance_transform)), model_view).xyz; + result.m_Tangent.w = vs_in.m_Tangent.w; + result.m_PositionSV = mul(g_FaceProjection, float4(result.m_Position, 1.)); + result.m_PositionCS = result.m_PositionSV; + result.m_HistoryPositionCS = result.m_PositionSV; + return result; +} diff --git a/manul/shaders/instanced_shadow_vertex.hlsl b/manul/shaders/instanced_shadow_vertex.hlsl new file mode 100644 index 00000000..400d0492 --- /dev/null +++ b/manul/shaders/instanced_shadow_vertex.hlsl @@ -0,0 +1,25 @@ +#pragma pack_matrix(column_major) + +struct VertexInput { + float3 m_Position : Position; + float3 m_Normal : Normal; + float2 m_TexCoord : TexCoord; + float4 m_Tangent : Tangent; + float4 m_InstanceTransform[3] : InstanceTransform; +}; + +struct VertexOutput { + float2 m_TexCoord : TexCoord; + float4 m_PositionSV : SV_Position; +}; + +#include "manul/draw_constants_shadow.hlsli" + +VertexOutput main(in VertexInput vs_in) { + VertexOutput result; + float4x4 instance_transform = transpose(float4x4(vs_in.m_InstanceTransform[0], vs_in.m_InstanceTransform[1], vs_in.m_InstanceTransform[2], float4(0., 0., 0., 1.))); + float3 position = mul(float4(vs_in.m_Position, 1.), instance_transform); + result.m_TexCoord = vs_in.m_TexCoord; + result.m_PositionSV = mul(g_DrawConstants.m_ModelViewProjection, float4(position, 1.)); + return result; +} diff --git a/manul/shaders/instanced_vertex.hlsl b/manul/shaders/instanced_vertex.hlsl new file mode 100644 index 00000000..9b29e67d --- /dev/null +++ b/manul/shaders/instanced_vertex.hlsl @@ -0,0 +1,45 @@ +#pragma pack_matrix(column_major) + +struct VertexInput { + float3 m_Position : Position; + float3 m_Normal : Normal; + float2 m_TexCoord : TexCoord; + float4 m_Tangent : Tangent; + float4 m_InstanceTransform[3] : InstanceTransform; +}; + +struct VertexOutput { + float3 m_Position : Position; + float3 m_Normal : Normal; + float2 m_TexCoord : TexCoord; + float4 m_Tangent : Tangent; + float4 m_PositionSV : SV_Position; + float4 m_PositionCS : PositionCS; + float4 m_HistoryPositionCS : HistoryPositionCS; +}; + +cbuffer VertexConstants : register(b0) { + float4x4 g_JitteredProjection; + float4x4 g_Projection; + float4x4 g_ProjectionHistory; +}; + +#include "manul/draw_constants.hlsli" + +VertexOutput main(in VertexInput vs_in) { + VertexOutput result; + float4x3 model_view = GetModelView(); + float4x3 model_view_history = GetModelViewHistory(); + float4x4 instance_transform = transpose(float4x4(vs_in.m_InstanceTransform[0], vs_in.m_InstanceTransform[1], vs_in.m_InstanceTransform[2], float4(0., 0., 0., 1.))); + float3 position = mul(float4(vs_in.m_Position, 1.), instance_transform).xyz; + result.m_Position = mul(float4(position, 1.), model_view).xyz; + result.m_Normal = mul(mul(float4(vs_in.m_Normal, 0.), instance_transform), model_view); + result.m_TexCoord = vs_in.m_TexCoord; + result.m_Tangent.xyz = mul(mul(float4(vs_in.m_Tangent.xyz, 0.), instance_transform), model_view); + result.m_Tangent.w = vs_in.m_Tangent.w; + result.m_PositionSV = mul(g_JitteredProjection, float4(result.m_Position, 1.)); + result.m_PositionCS = mul(g_Projection, float4(result.m_Position, 1.)); + float3 history_position = mul(float4(position, 1.), model_view_history).xyz; + result.m_HistoryPositionCS = mul(g_ProjectionHistory, float4(history_position, 1.)); + return result; +} diff --git a/manul/shaders/project.manul b/manul/shaders/project.manul new file mode 100644 index 00000000..1a8a2ba2 --- /dev/null +++ b/manul/shaders/project.manul @@ -0,0 +1,309 @@ +templates: +# Partially pre-filled containers to avoid excessive copypasta + XeGTAO: + source: XeGTAO/GTAO + target: compute + definitions: # TODO (mostly) move to shader file I guess + VA_COMPILED_AS_SHADER_CODE: 1 + VA_DIRECTX: 12 + VA_DXC: 1 + XE_GTAO_USE_DEFAULT_CONSTANTS: 1 + XE_GTAO_FP32_DEPTHS: 1 + XE_GTAO_USE_HALF_FLOAT_PRECISION: 0 + VA_COMPILED_AS_SHADER_CODE: 1 + VA_SATURATE: saturate + envmap: + source: envmap + target: compute + precomputed_sky: + source: manul/sky_aerial_lut + target: compute + bloom: + source: manul/bloom + target: compute + gbuffer_lights: + source: manul/gbuffer_lights + target: compute +shaders: + materials: +# Material shaders +# are compiled for all the rendering passes at once +# Binding layout will also be defined here (though ignored by compiler for now) + default_lit: + textures: + albedo: + binding: 0 + hint: linear # so that texture will be marked as sRGB + default: white + # We split the normal map into two textures to drastically improve compression quality when compressed using standard of "Paczka całościowa" + # (so that we use the best of two 5.6.5 RGB compression blocks for hopefully less gradient-like parameters and two interpolated alphas for normal) + params: # Metalness.Roughness.Occlusion.Specular + binding: 1 + hint: linear + default: legacy_params + normal: + binding: 2 + hint: normalmap + default: normalmap + # paramx: # Metalness.Roughness.[UNUSED].Normal[X] + # binding: 1 + # hint: linear + # default: legacy_params + # paramy: # Specular.Occlusion.[UNUSED].Normal[Y] + # binding: 2 + # hint: linear + # default: legacy_params + # selfillum: + # binding: 3 + # hint: color + # default: black + masked_shadow_texture: albedo + source: ps_default_lit + legacy: + textures: + diffuse: + binding: 0 + hint: color # so that texture will be marked as sRGB + default: white + masked_shadow_texture: diffuse + source: ps_legacy + legacy_reflection: + textures: + diffuse: + binding: 0 + hint: color # so that texture will be marked as sRGB + default: white + normal: + binding: 1 + hint: normalmap + default: normalmap + masked_shadow_texture: diffuse + source: ps_legacy_reflection + legacy_normalmap: + textures: + diffuse: + binding: 0 + hint: color # so that texture will be marked as sRGB + default: white + normal: + binding: 1 + hint: normalmap + default: normalmap + masked_shadow_texture: diffuse + source: ps_legacy_normalmap + legacy_specgloss: + textures: + diffuse: + binding: 0 + hint: color # so that texture will be marked as sRGB + default: white + specgloss: + binding: 1 + hint: linear + default: white + masked_shadow_texture: diffuse + source: ps_legacy_specgloss + legacy_normalmap_specgloss: + textures: + diffuse: + binding: 0 + hint: color # so that texture will be marked as sRGB + default: white + specgloss: + binding: 1 + hint: linear + default: white + normal: + binding: 2 + hint: normalmap + default: normalmap + masked_shadow_texture: diffuse + source: ps_legacy_normalmap_specgloss + legacy_water: + textures: + diffuse: + binding: 0 + hint: color # so that texture will be marked as sRGB + default: white + normal: + binding: 1 + hint: normalmap + default: normalmap + dudvmap: + binding: 2 + hint: normalmap + default: normalmap + masked_shadow_texture: diffuse + source: ps_legacy_water + utility: +# Everything that does not belong to scene graph rendering +# ImGui shaders + imgui_pixel: + source: imgui_pixel + target: pixel + entrypoint: main + imgui_vertex: + source: imgui_vertex + target: vertex + entrypoint: main +# Shadow material + shadow_masked: + source: ps_shadow_masked + target: pixel + entrypoint: main +# Contact shadows + contact_shadows: + source: contact_shadows + target: compute + entrypoint: main +# Gbuffer lighting + gbuffer_lighting: + source: gbufferblit + target: compute + entrypoint: main +# Vertex shaders + default_vertex: + source: default_vertex + target: vertex + entrypoint: main + shadow_vertex: + source: shadow_vertex + target: vertex + entrypoint: main + cubemap_vertex: + source: cubemap_vertex + target: vertex + entrypoint: main + instanced_vertex: + source: instanced_vertex + target: vertex + entrypoint: main + instanced_shadow_vertex: + source: instanced_shadow_vertex + target: vertex + entrypoint: main + instanced_cubemap_vertex: + source: instanced_cubemap_vertex + target: vertex + entrypoint: main +# Fullscreen Fx shaders + fx_vertex: + source: fx_vertex + target: vertex + entrypoint: main + copy: + source: copy + target: pixel + entrypoint: main + skybox: + source: skybox + target: pixel + entrypoint: main + tonemap: + source: tonemap + target: pixel + entrypoint: main +# Envmap compute shaders + gbuffer_cube_lighting: + source: gbuffer_cube_lighting + target: compute + entrypoint: main + envmap_CSSampleEquirectangular: + use_template: envmap + entrypoint: CSSampleEquirectangular + envmap_CSDiffuseIBL: + use_template: envmap + entrypoint: CSDiffuseIBL + envmap_CSSpecularIBL: + use_template: envmap + entrypoint: CSSpecularIBL + envmap_CSGenerateCubeMip: + use_template: envmap + entrypoint: CSGenerateCubeMip + envmap_CSIntegrateBRDF: + use_template: envmap + entrypoint: CSIntegrateBRDF +# XeGTAO + gtao_CSPrefilterDepths16x16: + use_template: XeGTAO + entrypoint: CSPrefilterDepths16x16 + gtao_CSGTAOLow: + use_template: XeGTAO + entrypoint: CSGTAOLow + gtao_CSGTAOMedium: + use_template: XeGTAO + entrypoint: CSGTAOMedium + gtao_CSGTAOHigh: + use_template: XeGTAO + entrypoint: CSGTAOHigh + gtao_CSGTAOUltra: + use_template: XeGTAO + entrypoint: CSGTAOUltra + gtao_CSDenoisePass: + use_template: XeGTAO + entrypoint: CSDenoisePass + gtao_CSDenoiseLastPass: + use_template: XeGTAO + entrypoint: CSDenoiseLastPass +# Atmospheric transmitance LUT + sky_transmittance: + source: manul/sky_transmittance + target: pixel + entrypoint: main + sky_aerial_lut: + use_template: precomputed_sky + entrypoint: CS_AerialLUT + sky: + use_template: precomputed_sky + entrypoint: CS_Sky +# Blóm + bloom_prefilter: + use_template: bloom + entrypoint: CS_BloomPrefilter + bloom_downsample: + use_template: bloom + entrypoint: CS_BloomDownsample + bloom_upsample: + use_template: bloom + entrypoint: CS_BloomUpsample + bloom_apply: + use_template: bloom + entrypoint: CS_BloomApply +# Wire geometry shader + vtx_line: + source: manul/line + target: vertex + entrypoint: vtx_main + geo_line: + source: manul/line + target: geometry + entrypoint: geo_main + pix_line: + source: manul/line + target: pixel + entrypoint: pix_main +# Deferred light primitives + gbuffer_light_clear: + use_template: gbuffer_lights + entrypoint: CS_Clear + gbuffer_light_spot: + use_template: gbuffer_lights + entrypoint: CS_Spotlight +# Forward+ light culling + forwardplus_compute_frustums: + source: manul/forward_plus/forward_plus + target: compute + entrypoint: CS_ComputeFrustums + forwardplus_cull_lights: + source: manul/forward_plus/forward_plus + target: compute + entrypoint: CS_CullLights +# Auto exposure + autoexp_compute_avg_luminance: + source: manul/comp_luminance + target: compute + entrypoint: CS_ComputeAvgLuminance + autoexp_apply: + source: manul/apply_auto_exposure + target: compute + entrypoint: CS_ApplyAutoExposure diff --git a/manul/shaders/ps_default_lit.hlsl b/manul/shaders/ps_default_lit.hlsl new file mode 100644 index 00000000..d1d5c5b4 --- /dev/null +++ b/manul/shaders/ps_default_lit.hlsl @@ -0,0 +1,30 @@ +#include "manul/math.hlsli" +#include "manul/color_transform.hlsli" +#include "manul/material.hlsli" + +sampler diffuse_sampler : register(s0); +Texture2D tex_albedo : register(t0); +Texture2D tex_params : register(t1); +Texture2D tex_normal : register(t2); +//Texture2D tex_paramx : register(t1); +//Texture2D tex_paramy : register(t2); + +#include "manul/alpha_mask.hlsli" + +void MaterialPass(inout MaterialData material) { + material.m_MaterialAlbedoAlpha = tex_albedo.Sample(diffuse_sampler, material.m_TexCoord); + material.m_MaterialAlbedoAlpha.rgb = saturate(ACEScg_to_XYZ(material.m_MaterialAlbedoAlpha.rgb)); + AlphaMask(material.m_MaterialAlbedoAlpha.a); + material.m_MaterialEmission = float3(0., 0., 0.); + //float4 params_nx = tex_paramx.Sample(diffuse_sampler, material.m_TexCoord); + //float4 params_ny = tex_paramy.Sample(diffuse_sampler, material.m_TexCoord); + //material.m_MaterialParams = float4(params_nx.xy, params_ny.yx); + //float3 normal = UnpackNormalXY(float2(params_nx.a, params_ny.a)); + material.m_MaterialParams = tex_params.Sample(diffuse_sampler, material.m_TexCoord); + float3 normal = UnpackNormalXY(tex_normal.Sample(diffuse_sampler, material.m_TexCoord)); + material.m_MaterialNormal = normalize(normal.x * material.m_Tangent + normal.y * material.m_Bitangent + normal.z * material.m_Normal); +//#if PASS & FORWARD_LIGHTING +// float NdotV = saturate(dot(material.m_MaterialNormal, -normalize(material.m_Position))); +// material.m_MaterialAlbedoAlpha.a = lerp(pow(1. - NdotV, 5.), 1., material.m_MaterialAlbedoAlpha.a); +//#endif +} diff --git a/manul/shaders/ps_legacy.hlsl b/manul/shaders/ps_legacy.hlsl new file mode 100644 index 00000000..51f048a3 --- /dev/null +++ b/manul/shaders/ps_legacy.hlsl @@ -0,0 +1,16 @@ +#include "manul/material.hlsli" +#include "manul/color_transform.hlsli" + +sampler diffuse_sampler : register(s0); +Texture2D diffuse : register(t0); + +#include "manul/alpha_mask.hlsli" + +void MaterialPass(inout MaterialData material) { + material.m_MaterialAlbedoAlpha = diffuse.Sample(diffuse_sampler, material.m_TexCoord); + material.m_MaterialAlbedoAlpha.rgb = saturate(REC709_to_XYZ(material.m_MaterialAlbedoAlpha.rgb * g_DrawConstants.m_Diffuse)); + AlphaMask(material.m_MaterialAlbedoAlpha.a); + material.m_MaterialEmission = g_DrawConstants.m_SelfIllum * material.m_MaterialAlbedoAlpha.rgb; + material.m_MaterialParams = float4(0., 1., 1., 0.); + material.m_MaterialNormal = material.m_Normal; +} diff --git a/manul/shaders/ps_legacy_normalmap.hlsl b/manul/shaders/ps_legacy_normalmap.hlsl new file mode 100644 index 00000000..03ea126c --- /dev/null +++ b/manul/shaders/ps_legacy_normalmap.hlsl @@ -0,0 +1,22 @@ +#include "manul/math.hlsli" +#include "manul/material.hlsli" +#include "manul/color_transform.hlsli" + +sampler diffuse_sampler : register(s0); +Texture2D diffuse : register(t0); +Texture2D normalmap : register(t1); + +#include "manul/alpha_mask.hlsli" + +void MaterialPass(inout MaterialData material) { + material.m_MaterialAlbedoAlpha = diffuse.Sample(diffuse_sampler, material.m_TexCoord); + material.m_MaterialAlbedoAlpha.rgb = saturate(REC709_to_XYZ(material.m_MaterialAlbedoAlpha.rgb * g_DrawConstants.m_Diffuse)); + AlphaMask(material.m_MaterialAlbedoAlpha.a); + material.m_MaterialEmission = g_DrawConstants.m_SelfIllum * material.m_MaterialAlbedoAlpha.rgb; + + float4 normal_refl = normalmap.Sample(diffuse_sampler, material.m_TexCoord); + material.m_MaterialParams = float4(0., (1. - normal_refl.a) * (1. - normal_refl.a), 1., 0.); + + float3 normal = UnpackNormalXY(normal_refl.xy); + material.m_MaterialNormal = normalize(normal.x * material.m_Tangent + normal.y * material.m_Bitangent + normal.z * material.m_Normal); +} diff --git a/manul/shaders/ps_legacy_normalmap_specgloss.hlsl b/manul/shaders/ps_legacy_normalmap_specgloss.hlsl new file mode 100644 index 00000000..950a4f73 --- /dev/null +++ b/manul/shaders/ps_legacy_normalmap_specgloss.hlsl @@ -0,0 +1,23 @@ +#include "manul/math.hlsli" +#include "manul/material.hlsli" +#include "manul/color_transform.hlsli" + +sampler diffuse_sampler : register(s0); +Texture2D diffuse : register(t0); +Texture2D specgloss : register(t1); +Texture2D normalmap : register(t2); + +#include "manul/alpha_mask.hlsli" + +void MaterialPass(inout MaterialData material) { + material.m_MaterialAlbedoAlpha = diffuse.Sample(diffuse_sampler, material.m_TexCoord); + material.m_MaterialAlbedoAlpha.rgb = saturate(REC709_to_XYZ(material.m_MaterialAlbedoAlpha.rgb * g_DrawConstants.m_Diffuse)); + AlphaMask(material.m_MaterialAlbedoAlpha.a); + material.m_MaterialEmission = g_DrawConstants.m_SelfIllum * material.m_MaterialAlbedoAlpha.rgb; + + float3 params = specgloss.Sample(diffuse_sampler, material.m_TexCoord); + material.m_MaterialParams = float4(params.b, 1. - params.g, 1., .5); + + float3 normal = UnpackNormalXY(normalmap.Sample(diffuse_sampler, material.m_TexCoord)); + material.m_MaterialNormal = normalize(normal.x * material.m_Tangent + normal.y * material.m_Bitangent + normal.z * material.m_Normal); +} diff --git a/manul/shaders/ps_legacy_reflection.hlsl b/manul/shaders/ps_legacy_reflection.hlsl new file mode 100644 index 00000000..e759f2f2 --- /dev/null +++ b/manul/shaders/ps_legacy_reflection.hlsl @@ -0,0 +1,22 @@ +#include "manul/math.hlsli" +#include "manul/material.hlsli" +#include "manul/color_transform.hlsli" + +sampler diffuse_sampler : register(s0); +Texture2D diffuse : register(t0); +Texture2D normalmap : register(t1); + +#include "manul/alpha_mask.hlsli" + +void MaterialPass(inout MaterialData material) { + material.m_MaterialAlbedoAlpha = diffuse.Sample(diffuse_sampler, material.m_TexCoord); + material.m_MaterialAlbedoAlpha.rgb = saturate(REC709_to_XYZ(material.m_MaterialAlbedoAlpha.rgb * g_DrawConstants.m_Diffuse)); + AlphaMask(material.m_MaterialAlbedoAlpha.a); + material.m_MaterialEmission = g_DrawConstants.m_SelfIllum * material.m_MaterialAlbedoAlpha.rgb; + + float4 normal_refl = normalmap.Sample(diffuse_sampler, material.m_TexCoord); + + material.m_MaterialParams = float4(0., (1. - normal_refl.a) * (1. - normal_refl.a), 1., 0.); + float3 normal = UnpackNormalXY(normal_refl.xy); + material.m_MaterialNormal = material.m_Normal; +} diff --git a/manul/shaders/ps_legacy_specgloss.hlsl b/manul/shaders/ps_legacy_specgloss.hlsl new file mode 100644 index 00000000..b80798bd --- /dev/null +++ b/manul/shaders/ps_legacy_specgloss.hlsl @@ -0,0 +1,21 @@ +#include "manul/math.hlsli" +#include "manul/material.hlsli" +#include "manul/color_transform.hlsli" + +sampler diffuse_sampler : register(s0); +Texture2D diffuse : register(t0); +Texture2D specgloss : register(t1); + +#include "manul/alpha_mask.hlsli" + +void MaterialPass(inout MaterialData material) { + material.m_MaterialAlbedoAlpha = diffuse.Sample(diffuse_sampler, material.m_TexCoord); + material.m_MaterialAlbedoAlpha.rgb = saturate(REC709_to_XYZ(material.m_MaterialAlbedoAlpha.rgb * g_DrawConstants.m_Diffuse)); + AlphaMask(material.m_MaterialAlbedoAlpha.a); + material.m_MaterialEmission = g_DrawConstants.m_SelfIllum * material.m_MaterialAlbedoAlpha.rgb; + + float3 params = specgloss.Sample(diffuse_sampler, material.m_TexCoord); + material.m_MaterialParams = float4(params.b, 1. - params.g, 1., .5); + + material.m_MaterialNormal = material.m_Normal; +} diff --git a/manul/shaders/ps_legacy_water.hlsl b/manul/shaders/ps_legacy_water.hlsl new file mode 100644 index 00000000..477ffff5 --- /dev/null +++ b/manul/shaders/ps_legacy_water.hlsl @@ -0,0 +1,37 @@ +#include "manul/math.hlsli" +#include "manul/material.hlsli" +#include "manul/color_transform.hlsli" + +sampler diffuse_sampler : register(s0); +Texture2D diffuse : register(t0); +Texture2D normalmap : register(t1); +Texture2D dudvmap : register(t2); + +void MaterialPass(inout MaterialData material) { + float2 tex_coord = material.m_TexCoord; + + float move_factor = 0.; +#if PASS & FORWARD_LIGHTING + move_factor += (.02 * g_Time); +#endif + move_factor %= 1.; + float2 distorted_tex_coord = dudvmap.Sample(diffuse_sampler, float2(tex_coord.x + move_factor, tex_coord.y)) * .1; + distorted_tex_coord = tex_coord + float2(distorted_tex_coord.x , distorted_tex_coord.y + move_factor); + float2 total_distorted_tex_coord = (dudvmap.Sample(diffuse_sampler, distorted_tex_coord) * 2. - 1. ) * .05; + tex_coord += total_distorted_tex_coord; + + material.m_MaterialAlbedoAlpha.rgb = diffuse.Sample(diffuse_sampler, tex_coord); + material.m_MaterialAlbedoAlpha.rgb = saturate(REC709_to_XYZ(material.m_MaterialAlbedoAlpha.rgb)); + material.m_MaterialAlbedoAlpha.a = 1.; + material.m_MaterialEmission = 0; + material.m_MaterialParams = float4(0., .04, 1., .5); +#if PASS & FORWARD_LIGHTING + float4 fragment_ndc = float4(material.m_PositionNDC.xy, g_GbufferDepth[material.m_PixelCoord], 1.); + fragment_ndc = mul(g_InverseProjection, fragment_ndc); + fragment_ndc /= fragment_ndc.w; + float depth = min(0., (fragment_ndc.z - material.m_Position.z)); + material.m_MaterialAlbedoAlpha.a = 1. - exp(depth * 2.5); +#endif + float3 normal = UnpackNormalXY(normalmap.Sample(diffuse_sampler, tex_coord)); + material.m_MaterialNormal = normalize(normal.x * material.m_Tangent + normal.y * material.m_Bitangent + normal.z * material.m_Normal); +} diff --git a/manul/shaders/ps_shadow_masked.hlsl b/manul/shaders/ps_shadow_masked.hlsl new file mode 100644 index 00000000..eb19eff4 --- /dev/null +++ b/manul/shaders/ps_shadow_masked.hlsl @@ -0,0 +1,17 @@ +#pragma pack_matrix(column_major) + +sampler diffuse_sampler : register(s0); +Texture2D diffuse : register(t0); + +#include "manul/draw_constants_shadow.hlsli" + +#include "manul/alpha_mask.hlsli" + +struct VertexOutput { + float2 m_TexCoord : TexCoord; +}; + +void main(in VertexOutput ps_in) { + float4 color = diffuse.Sample(diffuse_sampler, ps_in.m_TexCoord); + AlphaMask(color.a); +} \ No newline at end of file diff --git a/manul/shaders/shadow_vertex.hlsl b/manul/shaders/shadow_vertex.hlsl new file mode 100644 index 00000000..d1db562b --- /dev/null +++ b/manul/shaders/shadow_vertex.hlsl @@ -0,0 +1,22 @@ +#pragma pack_matrix(column_major) + +struct VertexInput { + float3 m_Position : Position; + float3 m_Normal : Normal; + float2 m_TexCoord : TexCoord; + float4 m_Tangent : Tangent; +}; + +struct VertexOutput { + float2 m_TexCoord : TexCoord; + float4 m_PositionSV : SV_Position; +}; + +#include "manul/draw_constants_shadow.hlsli" + +VertexOutput main(in VertexInput vs_in) { + VertexOutput result; + result.m_TexCoord = vs_in.m_TexCoord; + result.m_PositionSV = mul(g_DrawConstants.m_ModelViewProjection, float4(vs_in.m_Position, 1.)); + return result; +} diff --git a/manul/shaders/skybox.hlsl b/manul/shaders/skybox.hlsl new file mode 100644 index 00000000..6a19ffde --- /dev/null +++ b/manul/shaders/skybox.hlsl @@ -0,0 +1,49 @@ + +struct VertexOutput { + float2 m_Position : Position; + float4 m_PositionSV : SV_Position; +}; + +struct PixelOutput { + float3 m_Color : SV_Target0; + float3 m_Emission : SV_Target1; + float4 m_Params : SV_Target2; + float3 m_Normal : SV_Target3; + float3 m_Motion : SV_Target4; +}; + +cbuffer SkyboxPixelConstants : register(b0) { + float4x4 g_InverseViewProjection; + float4x4 g_HistoryReproject; + float3 g_SunDirection; + float g_Altitude; + float3 g_MoonDirection; +} + +sampler g_SkyboxSampler : register(s0); +TextureCube g_Skybox : register(t0); + +#include "manul/sky.hlsli" + +PixelOutput main(VertexOutput ps_in) { + PixelOutput result; + result.m_Color = 0..xxx; + result.m_Emission = 1..xxx; + result.m_Params = 0..xxxx; + result.m_Normal = 0..xxx; + result.m_Motion = 0..xxx; + float4 positionNdc = float4((ps_in.m_Position - .5.xx) * float2(2., -2.), 1., 1.); + float3 viewDir = normalize(mul(g_InverseViewProjection, positionNdc).xyz); + result.m_Emission = 1.e-7; + CalcSun(result.m_Emission, viewDir, g_SunDirection, g_Altitude); + CalcMoon(result.m_Emission, viewDir, g_MoonDirection, g_SunDirection, g_Altitude); + CalcAtmosphere(result.m_Emission, viewDir, g_SunDirection); + //result.m_Emission = g_Skybox.Sample(g_SkyboxSampler, normalize(mul(g_InverseViewProjection, positionNdc).xyz)).rgb; + //result.m_Emission = 0.; //Sky(normalize(mul(g_InverseViewProjection, positionNdc).xyz), g_SunDirection, g_Altitude); + float4 positionReproject = mul(g_HistoryReproject, positionNdc); + positionReproject.xyz /= positionReproject.w; + result.m_Motion = (positionNdc - positionReproject).xyz; + result.m_Motion.xy = result.m_Motion.xy * .5.xx; + //result.m_Output = source.Sample(source_sampler, ps_in.m_Position); + return result; +} diff --git a/manul/shaders/tonemap.hlsl b/manul/shaders/tonemap.hlsl new file mode 100644 index 00000000..67c8c1b6 --- /dev/null +++ b/manul/shaders/tonemap.hlsl @@ -0,0 +1,187 @@ +#include "manul/color_transform.hlsli" + +#define MA_TONEMAP_SRGB_REC709 0 +#define MA_TONEMAP_LINEAR_REC2020 1 + +struct TonemapConstants { + int m_TonemapFunction; + float m_SceneExposure; + float m_SceneNits; + float m_SceneGamma; +}; + +#ifdef SPIRV + +[[vk::push_constant]] ConstantBuffer g_TonemapConstants; + +#else + +cbuffer g_Const : register(b0) { TonemapConstants g_TonemapConstants; } + +#endif + +struct VertexOutput { + float2 m_Position : Position; + float4 m_PositionSV : SV_Position; +}; + +struct PixelOutput { + float4 m_Output : SV_Target0; +}; + +sampler source_sampler : register(s0); +Texture2D source : register(t0); +Texture2D noise : register(t1); + +float3 ACESFilm(float3 x) +{ + float a = 2.51f; + float b = 0.03f; + float c = 2.43f; + float d = 0.59f; + float e = 0.14f; + return (x*(a*x+b))/(x*(c*x+d)+e); +} + +float luminance(float3 v) +{ + return dot(v, float3(0.2126f, 0.7152f, 0.0722f)); +} + +float3 reinhard_jodie(float3 v) +{ + float l = luminance(v); + float3 tv = v / (1.0f + v); + return lerp(v / (1.0f + l), tv, tv); +} + +float3 reinhard_extended_luminance(float3 v, float max_white_l) +{ + float l_old = luminance(v); + float numerator = l_old * (1.0f + (l_old / (max_white_l * max_white_l))); + float l_new = numerator / (1.0f + l_old); + return v * l_new / l_old; +} + +float3 ApplySRGBCurve( float3 x ) +{ + // Approximately pow(x, 1.0 / 2.2) + return select(x < 0.0031308, 12.92 * x, 1.055 * pow(x, 1.0 / 2.4) - 0.055); +} + +float3 Rec709ToRec2020(float3 color) +{ + static const float3x3 conversion = + { + 0.627402, 0.329292, 0.043306, + 0.069095, 0.919544, 0.011360, + 0.016394, 0.088028, 0.895578 + }; + return mul(conversion, color); +} + +float3 LinearToST2084(float3 color) +{ + float m1 = 2610.0 / 4096.0 / 4; + float m2 = 2523.0 / 4096.0 * 128; + float c1 = 3424.0 / 4096.0; + float c2 = 2413.0 / 4096.0 * 32; + float c3 = 2392.0 / 4096.0 * 32; + float3 cp = pow(abs(color), m1); + return pow((c1 + c2 * cp) / (1 + c3 * cp), m2); +} + +float3 filmicF(float3 x) +{ + float A = 0.22f; + float B = 0.30f; + float C = 0.10f; + float D = 0.20f; + float E = 0.01f; + float F = 0.30f; + return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F)) - E/F; +} + +float3 filmic(float3 x) +{ + return filmicF(x) / filmicF(11.2); +} + +static const float3x3 ACESInputMat = +{ + {0.59719, 0.35458, 0.04823}, + {0.07600, 0.90834, 0.01566}, + {0.02840, 0.13383, 0.83777} +}; + +// ODT_SAT => XYZ => D60_2_D65 => sRGB +static const float3x3 ACESOutputMat = +{ + { 1.60475, -0.53108, -0.07367}, + {-0.10208, 1.10813, -0.00605}, + {-0.00327, -0.07276, 1.07602} +}; + +float3 RRTAndODTFit(float3 v) +{ + float3 a = v * (v + 0.0245786f) - 0.000090537f; + float3 b = v * (0.983729f * v + 0.4329510f) + 0.238081f; + return a / b; +} + +float3 ACESFitted(float3 color) +{ + color = mul(ACESInputMat, color); + + // Apply RRT and ODT + color = RRTAndODTFit(color); + + color = mul(ACESOutputMat, color); + + // Clamp to [0, 1] + //color = saturate(color); + + return color; +} + +PixelOutput main(VertexOutput ps_in) { + PixelOutput result; + //result.m_Output.rgb = pow(ACESFilm(source.Sample(source_sampler, ps_in.m_Position).rgb), 1./2.2); + result.m_Output.rgb = source[(uint2)ps_in.m_PositionSV.xy].rgb; + result.m_Output.rgb = pow(result.m_Output.rgb*g_TonemapConstants.m_SceneExposure, g_TonemapConstants.m_SceneGamma); + switch(g_TonemapConstants.m_TonemapFunction) { + case MA_TONEMAP_SRGB_REC709: + { + uint2 noise_size; + noise.GetDimensions(noise_size.x, noise_size.y); + float3 noise_sample = noise[ps_in.m_PositionSV.xy % noise_size]; + + result.m_Output.rgb = XYZ_to_REC709(result.m_Output.rgb); + result.m_Output.rgb = saturate(reinhard_jodie(result.m_Output.rgb)); + //result.m_Output.rgb = saturate(XYZ_to_REC2020(result.m_Output.rgb)); + ApplySRGBCurve(result.m_Output.rgb); + + result.m_Output.rgb = floor(result.m_Output.rgb * 256. + noise_sample) / 255.; + } + break; + case MA_TONEMAP_LINEAR_REC2020: + { + const float st2084max = 10000.; + const float hdrScalar = g_TonemapConstants.m_SceneNits / st2084max; + //result.m_Output.rgb = XYZ_to_REC2020(result.m_Output.rgb); + //result.m_Output.rgb = ACESFilm(result.m_Output.rgb); + //const float3 LuminanceWeights = float3(0.299,0.587,0.114); + //float luminance = dot(LuminanceWeights, result.m_Output.rgb); + //result.m_Output.rgb = lerp(luminance.xxx, result.m_Output.rgb, 2.2); + result.m_Output.rgb = XYZ_to_REC709(result.m_Output.rgb); + result.m_Output.rgb = ACESFilm(result.m_Output.rgb); + result.m_Output.rgb = Rec709ToRec2020(result.m_Output.rgb); + result.m_Output.rgb = saturate(result.m_Output.rgb); + result.m_Output.rgb *= hdrScalar; + result.m_Output.rgb = LinearToST2084(result.m_Output.rgb); + } + break; + } + result.m_Output.a = 1.; + return result; +}