diff --git a/Content/Editor/MaterialTemplates/Decal.shader b/Content/Editor/MaterialTemplates/Decal.shader index e0253efd6..35446b7e2 100644 --- a/Content/Editor/MaterialTemplates/Decal.shader +++ b/Content/Editor/MaterialTemplates/Decal.shader @@ -108,6 +108,8 @@ float4 GetVertexColor(MaterialInput input) return 1; } +@8 + // Get material properties function (for pixel shader) Material GetMaterialPS(MaterialInput input) { @@ -211,3 +213,5 @@ void PS_Decal( #error "Invalid decal blending mode" #endif } + +@9 diff --git a/Content/Editor/MaterialTemplates/Features/Lightmap.hlsl b/Content/Editor/MaterialTemplates/Features/Lightmap.hlsl new file mode 100644 index 000000000..6f6a44d8b --- /dev/null +++ b/Content/Editor/MaterialTemplates/Features/Lightmap.hlsl @@ -0,0 +1,53 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +@0// Lightmap: Defines +@1// Lightmap: Includes +@2// Lightmap: Constants +float4 LightmapArea; +@3// Lightmap: Resources +#if USE_LIGHTMAP +// Irradiance and directionality prebaked lightmaps +Texture2D Lightmap0 : register(t__SRV__); +Texture2D Lightmap1 : register(t__SRV__); +Texture2D Lightmap2 : register(t__SRV__); +#endif +@4// Lightmap: Utilities +#if USE_LIGHTMAP + +// Evaluates the H-Basis coefficients in the tangent space normal direction +float3 GetHBasisIrradiance(float3 n, float3 h0, float3 h1, float3 h2, float3 h3) +{ + // Band 0 + float3 color = h0 * (1.0f / sqrt(2.0f * PI)); + + // Band 1 + color += h1 * -sqrt(1.5f / PI) * n.y; + color += h2 * sqrt(1.5f / PI) * (2 * n.z - 1.0f); + color += h3 * -sqrt(1.5f / PI) * n.x; + + return color; +} + +float3 SampleLightmap(Material material, MaterialInput materialInput) +{ + // Sample lightmaps + float4 lightmap0 = Lightmap0.Sample(SamplerLinearClamp, materialInput.LightmapUV); + float4 lightmap1 = Lightmap1.Sample(SamplerLinearClamp, materialInput.LightmapUV); + float4 lightmap2 = Lightmap2.Sample(SamplerLinearClamp, materialInput.LightmapUV); + + // Unpack H-basis + float3 h0 = float3(lightmap0.x, lightmap1.x, lightmap2.x); + float3 h1 = float3(lightmap0.y, lightmap1.y, lightmap2.y); + float3 h2 = float3(lightmap0.z, lightmap1.z, lightmap2.z); + float3 h3 = float3(lightmap0.w, lightmap1.w, lightmap2.w); + + // Sample baked diffuse irradiance from the H-basis coefficients + float3 normal = material.TangentNormal; +#if MATERIAL_SHADING_MODEL == SHADING_MODEL_FOLIAGE + normal *= material.TangentNormal; +#endif + return GetHBasisIrradiance(normal, h0, h1, h2, h3) / PI; +} + +#endif +@5// Lightmap: Shaders \ No newline at end of file diff --git a/Content/Editor/MaterialTemplates/Features/Tessellation.hlsl b/Content/Editor/MaterialTemplates/Features/Tessellation.hlsl new file mode 100644 index 000000000..c5ebbba92 --- /dev/null +++ b/Content/Editor/MaterialTemplates/Features/Tessellation.hlsl @@ -0,0 +1,294 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +@0// Tessellation: Defines +@1// Tessellation: Includes +@2// Tessellation: Constants +@3// Tessellation: Resources +@4// Tessellation: Utilities +@5// Tessellation: Shaders +#if USE_TESSELLATION + +// Interpolants passed from the hull shader to the domain shader +struct TessalationHSToDS +{ + float4 Position : SV_Position; + float3 WorldPosition : TEXCOORD0; + float2 TexCoord : TEXCOORD1; + float2 LightmapUV : TEXCOORD2; +#if USE_VERTEX_COLOR + half4 VertexColor : COLOR; +#endif + float3 WorldNormal : TEXCOORD3; + float4 WorldTangent : TEXCOORD4; +#if USE_CUSTOM_VERTEX_INTERPOLATORS + float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; +#endif + float3 InstanceOrigin : TEXCOORD6; + float2 InstanceParams : TEXCOORD7; +#if IS_MOTION_VECTORS_PASS + float3 PrevWorldPosition : TEXCOORD8; +#endif + float TessellationMultiplier : TESS; +}; + +// Interpolants passed from the domain shader and to the pixel shader +struct TessalationDSToPS +{ + float4 Position : SV_Position; + float3 WorldPosition : TEXCOORD0; + float2 TexCoord : TEXCOORD1; + float2 LightmapUV : TEXCOORD2; +#if USE_VERTEX_COLOR + half4 VertexColor : COLOR; +#endif + float3 WorldNormal : TEXCOORD3; + float4 WorldTangent : TEXCOORD4; +#if USE_CUSTOM_VERTEX_INTERPOLATORS + float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; +#endif + float3 InstanceOrigin : TEXCOORD6; + float2 InstanceParams : TEXCOORD7; +#if IS_MOTION_VECTORS_PASS + float3 PrevWorldPosition : TEXCOORD8; +#endif +}; + +MaterialInput GetMaterialInput(TessalationDSToPS input) +{ + MaterialInput result = (MaterialInput)0; + result.WorldPosition = input.WorldPosition; + result.TexCoord = input.TexCoord; +#if USE_LIGHTMAP + result.LightmapUV = input.LightmapUV; +#endif +#if USE_VERTEX_COLOR + result.VertexColor = input.VertexColor; +#endif + result.TBN = CalcTangentBasis(input.WorldNormal, input.WorldTangent); + result.TwoSidedSign = WorldDeterminantSign; + result.InstanceOrigin = input.InstanceOrigin; + result.InstanceParams = input.InstanceParams; + result.SvPosition = input.Position; +#if USE_CUSTOM_VERTEX_INTERPOLATORS + result.CustomVSToPS = input.CustomVSToPS; +#endif + return result; +} + +struct TessalationPatch +{ + float EdgeTessFactor[3] : SV_TessFactor; + float InsideTessFactor : SV_InsideTessFactor; +#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN + float3 B210 : POSITION4; + float3 B120 : POSITION5; + float3 B021 : POSITION6; + float3 B012 : POSITION7; + float3 B102 : POSITION8; + float3 B201 : POSITION9; + float3 B111 : CENTER; +#endif +}; + +TessalationPatch HS_PatchConstant(InputPatch input) +{ + TessalationPatch output; + + // Average tess factors along edges, and pick an edge tess factor for the interior tessellation + float4 tessellationMultipliers; + tessellationMultipliers.x = 0.5f * (input[1].TessellationMultiplier + input[2].TessellationMultiplier); + tessellationMultipliers.y = 0.5f * (input[2].TessellationMultiplier + input[0].TessellationMultiplier); + tessellationMultipliers.z = 0.5f * (input[0].TessellationMultiplier + input[1].TessellationMultiplier); + tessellationMultipliers.w = 0.333f * (input[0].TessellationMultiplier + input[1].TessellationMultiplier + input[2].TessellationMultiplier); + tessellationMultipliers = clamp(tessellationMultipliers, 1, MAX_TESSELLATION_FACTOR); + + output.EdgeTessFactor[0] = tessellationMultipliers.x; // 1->2 edge + output.EdgeTessFactor[1] = tessellationMultipliers.y; // 2->0 edge + output.EdgeTessFactor[2] = tessellationMultipliers.z; // 0->1 edge + output.InsideTessFactor = tessellationMultipliers.w; + +#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN + // Calculate PN Triangle control points + // Reference: [Vlachos 2001] + float3 p1 = input[0].WorldPosition; + float3 p2 = input[1].WorldPosition; + float3 p3 = input[2].WorldPosition; + float3 n1 = input[0].WorldNormal; + float3 n2 = input[1].WorldNormal; + float3 n3 = input[2].WorldNormal; + output.B210 = (2.0f * p1 + p2 - dot((p2 - p1), n1) * n1) / 3.0f; + output.B120 = (2.0f * p2 + p1 - dot((p1 - p2), n2) * n2) / 3.0f; + output.B021 = (2.0f * p2 + p3 - dot((p3 - p2), n2) * n2) / 3.0f; + output.B012 = (2.0f * p3 + p2 - dot((p2 - p3), n3) * n3) / 3.0f; + output.B102 = (2.0f * p3 + p1 - dot((p1 - p3), n3) * n3) / 3.0f; + output.B201 = (2.0f * p1 + p3 - dot((p3 - p1), n1) * n1) / 3.0f; + float3 e = (output.B210 + output.B120 + output.B021 + + output.B012 + output.B102 + output.B201) / 6.0f; + float3 v = (p1 + p2 + p3) / 3.0f; + output.B111 = e + ((e - v) / 2.0f); +#endif + + return output; +} + +META_HS(USE_TESSELLATION, FEATURE_LEVEL_SM5) +META_PERMUTATION_1(IS_MOTION_VECTORS_PASS=0) +META_PERMUTATION_1(IS_MOTION_VECTORS_PASS=1) +META_HS_PATCH(TESSELLATION_IN_CONTROL_POINTS) +[domain("tri")] +[partitioning("fractional_odd")] +[outputtopology("triangle_cw")] +[maxtessfactor(MAX_TESSELLATION_FACTOR)] +[outputcontrolpoints(3)] +[patchconstantfunc("HS_PatchConstant")] +TessalationHSToDS HS(InputPatch input, uint ControlPointID : SV_OutputControlPointID) +{ + TessalationHSToDS output; + + // Pass through shader +#define COPY(thing) output.thing = input[ControlPointID].thing; + COPY(Position); + COPY(WorldPosition); + COPY(TexCoord); + COPY(LightmapUV); +#if USE_VERTEX_COLOR + COPY(VertexColor); +#endif + COPY(WorldNormal); + COPY(WorldTangent); + COPY(InstanceOrigin); + COPY(InstanceParams); +#if IS_MOTION_VECTORS_PASS + COPY(PrevWorldPosition); +#endif + COPY(TessellationMultiplier); +#if USE_CUSTOM_VERTEX_INTERPOLATORS + COPY(CustomVSToPS); +#endif +#undef COPY + + return output; +} + +#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PHONG + +// Orthogonal projection on to plane +float3 ProjectOntoPlane(float3 planeNormal, float3 planePosition, float3 pointToProject) +{ + return pointToProject - dot(pointToProject - planePosition, planeNormal) * planeNormal; +} + +#endif + +META_DS(USE_TESSELLATION, FEATURE_LEVEL_SM5) +META_PERMUTATION_1(IS_MOTION_VECTORS_PASS=0) +META_PERMUTATION_1(IS_MOTION_VECTORS_PASS=1) +[domain("tri")] +TessalationDSToPS DS(TessalationPatch constantData, float3 barycentricCoords : SV_DomainLocation, const OutputPatch input) +{ + TessalationDSToPS output; + + // Get the barycentric coords + float U = barycentricCoords.x; + float V = barycentricCoords.y; + float W = barycentricCoords.z; + + // Interpolate patch attributes to generated vertices +#define INTERPOLATE(thing) output.thing = U * input[0].thing + V * input[1].thing + W * input[2].thing +#define COPY(thing) output.thing = input[0].thing + INTERPOLATE(Position); +#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN + float UU = U * U; + float VV = V * V; + float WW = W * W; + float UU3 = UU * 3.0f; + float VV3 = VV * 3.0f; + float WW3 = WW * 3.0f; + + // Interpolate using barycentric coordinates and PN Triangle control points + output.WorldPosition = + input[0].WorldPosition * UU * U + + input[1].WorldPosition * VV * V + + input[2].WorldPosition * WW * W + + constantData.B210 * UU3 * V + + constantData.B120 * VV3 * U + + constantData.B021 * VV3 * W + + constantData.B012 * WW3 * V + + constantData.B102 * WW3 * U + + constantData.B201 * UU3 * W + + constantData.B111 * 6.0f * W * U * V; +#if IS_MOTION_VECTORS_PASS + output.PrevWorldPosition = + input[0].PrevWorldPosition * UU * U + + input[1].PrevWorldPosition * VV * V + + input[2].PrevWorldPosition * WW * W + + constantData.B210 * UU3 * V + + constantData.B120 * VV3 * U + + constantData.B021 * VV3 * W + + constantData.B012 * WW3 * V + + constantData.B102 * WW3 * U + + constantData.B201 * UU3 * W + + constantData.B111 * 6.0f * W * U * V; +#endif +#else + INTERPOLATE(WorldPosition); +#if IS_MOTION_VECTORS_PASS + INTERPOLATE(PrevWorldPosition); +#endif +#endif + INTERPOLATE(TexCoord); + INTERPOLATE(LightmapUV); +#if USE_VERTEX_COLOR + INTERPOLATE(VertexColor); +#endif + INTERPOLATE(WorldNormal); + INTERPOLATE(WorldTangent); + COPY(InstanceOrigin); + COPY(InstanceParams); +#if USE_CUSTOM_VERTEX_INTERPOLATORS + UNROLL + for (int i = 0; i < CUSTOM_VERTEX_INTERPOLATORS_COUNT; i++) + { + INTERPOLATE(CustomVSToPS[i]); + } +#endif +#undef INTERPOLATE +#undef COPY + + // Interpolating tangents can unnormalize it, so normalize it + output.WorldNormal = normalize(output.WorldNormal); + output.WorldTangent.xyz = normalize(output.WorldTangent.xyz); + +#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PHONG + // Orthogonal projection in the tangent planes + float3 posProjectedU = ProjectOntoPlane(input[0].WorldNormal, input[0].WorldPosition, output.WorldPosition); + float3 posProjectedV = ProjectOntoPlane(input[1].WorldNormal, input[1].WorldPosition, output.WorldPosition); + float3 posProjectedW = ProjectOntoPlane(input[2].WorldNormal, input[2].WorldPosition, output.WorldPosition); + + // Interpolate the projected points + output.WorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW; +#if IS_MOTION_VECTORS_PASS + posProjectedU = ProjectOntoPlane(input[0].WorldNormal, input[0].PrevWorldPosition, output.PrevWorldPosition); + posProjectedV = ProjectOntoPlane(input[1].WorldNormal, input[1].PrevWorldPosition, output.PrevWorldPosition); + posProjectedW = ProjectOntoPlane(input[2].WorldNormal, input[2].PrevWorldPosition, output.PrevWorldPosition); + output.PrevWorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW; +#endif +#endif + + // Perform displacement mapping +#if USE_DISPLACEMENT + MaterialInput materialInput = GetMaterialInput(output); + Material material = GetMaterialDS(materialInput); + output.WorldPosition += material.WorldDisplacement; +#if IS_MOTION_VECTORS_PASS + output.PrevWorldPosition += material.WorldDisplacement; +#endif +#endif + + // Recalculate the clip space position + output.Position = mul(float4(output.WorldPosition, 1), ViewProjectionMatrix); + + return output; +} + +#endif diff --git a/Content/Editor/MaterialTemplates/GUI.shader b/Content/Editor/MaterialTemplates/GUI.shader index 1457a0bc3..08fddde79 100644 --- a/Content/Editor/MaterialTemplates/GUI.shader +++ b/Content/Editor/MaterialTemplates/GUI.shader @@ -185,6 +185,8 @@ float4 GetVertexColor(MaterialInput input) #endif } +@8 + // Get material properties function (for vertex shader) Material GetMaterialVS(MaterialInput input) { @@ -257,3 +259,5 @@ float4 PS_GUI(PixelInput input) : SV_Target0 return float4(material.Emissive, material.Opacity); } + +@9 diff --git a/Content/Editor/MaterialTemplates/Particle.shader b/Content/Editor/MaterialTemplates/Particle.shader index 5fd161645..e8d00d1db 100644 --- a/Content/Editor/MaterialTemplates/Particle.shader +++ b/Content/Editor/MaterialTemplates/Particle.shader @@ -312,6 +312,8 @@ float3 TransformParticleVector(float3 input) return mul(float4(input, 0.0f), WorldMatrixInverseTransposed).xyz; } +@8 + // Get material properties function (for vertex shader) Material GetMaterialVS(MaterialInput input) { @@ -865,3 +867,5 @@ void PS_Depth(PixelInput input OutColor = 0; #endif } + +@9 diff --git a/Content/Editor/MaterialTemplates/PostProcess.shader b/Content/Editor/MaterialTemplates/PostProcess.shader index dad9795af..a3e5e42c7 100644 --- a/Content/Editor/MaterialTemplates/PostProcess.shader +++ b/Content/Editor/MaterialTemplates/PostProcess.shader @@ -128,6 +128,8 @@ float4 GetVertexColor(MaterialInput input) return 1; } +@8 + // Get material properties function (for pixel shader) Material GetMaterialPS(MaterialInput input) { @@ -147,3 +149,5 @@ float4 PS_PostFx(PixelInput input) : SV_Target0 return float4(material.Emissive, material.Opacity); } + +@9 diff --git a/Content/Editor/MaterialTemplates/SurfaceDeferred.shader b/Content/Editor/MaterialTemplates/SurfaceDeferred.shader index 6f393464b..303cb2fee 100644 --- a/Content/Editor/MaterialTemplates/SurfaceDeferred.shader +++ b/Content/Editor/MaterialTemplates/SurfaceDeferred.shader @@ -3,7 +3,6 @@ #define MATERIAL 1 @3 - #include "./Flax/Common.hlsl" #include "./Flax/MaterialCommon.hlsl" #include "./Flax/GBufferCommon.hlsl" @@ -21,7 +20,6 @@ float3 ViewDir; float TimeParam; float4 ViewInfo; float4 ScreenSize; -float4 LightmapArea; float3 WorldInvScale; float WorldDeterminantSign; float2 Dummy0; @@ -32,18 +30,8 @@ float3 GeometrySize; float Dummy1; @1META_CB_END -#if CAN_USE_LIGHTMAP - -// Irradiance and directionality prebaked lightmaps -Texture2D Lightmap0 : register(t0); -Texture2D Lightmap1 : register(t1); -Texture2D Lightmap2 : register(t2); - -#endif - // Material shader resources @2 - // Interpolants passed from the vertex shader struct VertexOutput { @@ -322,6 +310,8 @@ float4 GetVertexColor(MaterialInput input) #endif } +@8 + // Get material properties function (for vertex shader) Material GetMaterialVS(MaterialInput input) { @@ -613,320 +603,6 @@ VertexOutput VS_Skinned(ModelInput_Skinned input) #endif -#if USE_TESSELLATION - -// Interpolants passed from the hull shader to the domain shader -struct TessalationHSToDS -{ - float4 Position : SV_Position; - float3 WorldPosition : TEXCOORD0; - float2 TexCoord : TEXCOORD1; - float2 LightmapUV : TEXCOORD2; -#if USE_VERTEX_COLOR - half4 VertexColor : COLOR; -#endif - float3 WorldNormal : TEXCOORD3; - float4 WorldTangent : TEXCOORD4; -#if USE_CUSTOM_VERTEX_INTERPOLATORS - float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; -#endif - float3 InstanceOrigin : TEXCOORD6; - float2 InstanceParams : TEXCOORD7; -#if IS_MOTION_VECTORS_PASS - float3 PrevWorldPosition : TEXCOORD8; -#endif - float TessellationMultiplier : TESS; -}; - -// Interpolants passed from the domain shader and to the pixel shader -struct TessalationDSToPS -{ - float4 Position : SV_Position; - float3 WorldPosition : TEXCOORD0; - float2 TexCoord : TEXCOORD1; - float2 LightmapUV : TEXCOORD2; -#if USE_VERTEX_COLOR - half4 VertexColor : COLOR; -#endif - float3 WorldNormal : TEXCOORD3; - float4 WorldTangent : TEXCOORD4; -#if USE_CUSTOM_VERTEX_INTERPOLATORS - float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; -#endif - float3 InstanceOrigin : TEXCOORD6; - float2 InstanceParams : TEXCOORD7; -#if IS_MOTION_VECTORS_PASS - float3 PrevWorldPosition : TEXCOORD8; -#endif -}; - -MaterialInput GetMaterialInput(TessalationDSToPS input) -{ - MaterialInput result = (MaterialInput)0; - result.WorldPosition = input.WorldPosition; - result.TexCoord = input.TexCoord; -#if USE_LIGHTMAP - result.LightmapUV = input.LightmapUV; -#endif -#if USE_VERTEX_COLOR - result.VertexColor = input.VertexColor; -#endif - result.TBN = CalcTangentBasis(input.WorldNormal, input.WorldTangent); - result.TwoSidedSign = WorldDeterminantSign; - result.InstanceOrigin = input.InstanceOrigin; - result.InstanceParams = input.InstanceParams; - result.SvPosition = input.Position; -#if USE_CUSTOM_VERTEX_INTERPOLATORS - result.CustomVSToPS = input.CustomVSToPS; -#endif - return result; -} - -struct TessalationPatch -{ - float EdgeTessFactor[3] : SV_TessFactor; - float InsideTessFactor : SV_InsideTessFactor; -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN - float3 B210 : POSITION4; - float3 B120 : POSITION5; - float3 B021 : POSITION6; - float3 B012 : POSITION7; - float3 B102 : POSITION8; - float3 B201 : POSITION9; - float3 B111 : CENTER; -#endif -}; - -TessalationPatch HS_PatchConstant(InputPatch input) -{ - TessalationPatch output; - - // Average tess factors along edges, and pick an edge tess factor for the interior tessellation - float4 tessellationMultipliers; - tessellationMultipliers.x = 0.5f * (input[1].TessellationMultiplier + input[2].TessellationMultiplier); - tessellationMultipliers.y = 0.5f * (input[2].TessellationMultiplier + input[0].TessellationMultiplier); - tessellationMultipliers.z = 0.5f * (input[0].TessellationMultiplier + input[1].TessellationMultiplier); - tessellationMultipliers.w = 0.333f * (input[0].TessellationMultiplier + input[1].TessellationMultiplier + input[2].TessellationMultiplier); - tessellationMultipliers = clamp(tessellationMultipliers, 1, MAX_TESSELLATION_FACTOR); - - output.EdgeTessFactor[0] = tessellationMultipliers.x; // 1->2 edge - output.EdgeTessFactor[1] = tessellationMultipliers.y; // 2->0 edge - output.EdgeTessFactor[2] = tessellationMultipliers.z; // 0->1 edge - output.InsideTessFactor = tessellationMultipliers.w; - -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN - // Calculate PN-Triangle coefficients - // Refer to Vlachos 2001 for the original formula - float3 p1 = input[0].WorldPosition; - float3 p2 = input[1].WorldPosition; - float3 p3 = input[2].WorldPosition; - float3 n1 = input[0].WorldNormal; - float3 n2 = input[1].WorldNormal; - float3 n3 = input[2].WorldNormal; - - // Calculate control points - output.B210 = (2.0f * p1 + p2 - dot((p2 - p1), n1) * n1) / 3.0f; - output.B120 = (2.0f * p2 + p1 - dot((p1 - p2), n2) * n2) / 3.0f; - output.B021 = (2.0f * p2 + p3 - dot((p3 - p2), n2) * n2) / 3.0f; - output.B012 = (2.0f * p3 + p2 - dot((p2 - p3), n3) * n3) / 3.0f; - output.B102 = (2.0f * p3 + p1 - dot((p1 - p3), n3) * n3) / 3.0f; - output.B201 = (2.0f * p1 + p3 - dot((p3 - p1), n1) * n1) / 3.0f; - float3 e = (output.B210 + output.B120 + output.B021 + - output.B012 + output.B102 + output.B201) / 6.0f; - float3 v = (p1 + p2 + p3) / 3.0f; - output.B111 = e + ((e - v) / 2.0f); -#endif - - return output; -} - -META_HS(USE_TESSELLATION, FEATURE_LEVEL_SM5) -META_PERMUTATION_1(IS_MOTION_VECTORS_PASS=0) -META_PERMUTATION_1(IS_MOTION_VECTORS_PASS=1) -META_HS_PATCH(TESSELLATION_IN_CONTROL_POINTS) -[domain("tri")] -[partitioning("fractional_odd")] -[outputtopology("triangle_cw")] -[maxtessfactor(MAX_TESSELLATION_FACTOR)] -[outputcontrolpoints(3)] -[patchconstantfunc("HS_PatchConstant")] -TessalationHSToDS HS(InputPatch input, uint ControlPointID : SV_OutputControlPointID) -{ - TessalationHSToDS output; - - // Pass through shader -#define COPY(thing) output.thing = input[ControlPointID].thing; - COPY(Position); - COPY(WorldPosition); - COPY(TexCoord); - COPY(LightmapUV); -#if USE_VERTEX_COLOR - COPY(VertexColor); -#endif - COPY(WorldNormal); - COPY(WorldTangent); - COPY(InstanceOrigin); - COPY(InstanceParams); -#if IS_MOTION_VECTORS_PASS - COPY(PrevWorldPosition); -#endif - COPY(TessellationMultiplier); -#if USE_CUSTOM_VERTEX_INTERPOLATORS - COPY(CustomVSToPS); -#endif -#undef COPY - - return output; -} - -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PHONG - -// Orthogonal projection on to plane -float3 ProjectOntoPlane(float3 planeNormal, float3 planePoint, float3 pointToProject) -{ - return pointToProject - dot(pointToProject-planePoint, planeNormal) * planeNormal; -} - -#endif - -META_DS(USE_TESSELLATION, FEATURE_LEVEL_SM5) -META_PERMUTATION_1(IS_MOTION_VECTORS_PASS=0) -META_PERMUTATION_1(IS_MOTION_VECTORS_PASS=1) -[domain("tri")] -TessalationDSToPS DS(TessalationPatch constantData, float3 barycentricCoords : SV_DomainLocation, const OutputPatch input) -{ - TessalationDSToPS output; - - // Get the barycentric coords - float U = barycentricCoords.x; - float V = barycentricCoords.y; - float W = barycentricCoords.z; - - // Interpolate patch attributes to generated vertices -#define INTERPOLATE(thing) output.thing = U * input[0].thing + V * input[1].thing + W * input[2].thing -#define COPY(thing) output.thing = input[0].thing - INTERPOLATE(Position); -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN - float UU = U * U; - float VV = V * V; - float WW = W * W; - float UU3 = UU * 3.0f; - float VV3 = VV * 3.0f; - float WW3 = WW * 3.0f; - - // Interpolate using barycentric coordinates and PN Triangle control points - output.WorldPosition = - input[0].WorldPosition * UU * U + - input[1].WorldPosition * VV * V + - input[2].WorldPosition * WW * W + - constantData.B210 * UU3 * V + - constantData.B120 * VV3 * U + - constantData.B021 * VV3 * W + - constantData.B012 * WW3 * V + - constantData.B102 * WW3 * U + - constantData.B201 * UU3 * W + - constantData.B111 * 6.0f * W * U * V; -#if IS_MOTION_VECTORS_PASS - output.PrevWorldPosition = - input[0].PrevWorldPosition * UU * U + - input[1].PrevWorldPosition * VV * V + - input[2].PrevWorldPosition * WW * W + - constantData.B210 * UU3 * V + - constantData.B120 * VV3 * U + - constantData.B021 * VV3 * W + - constantData.B012 * WW3 * V + - constantData.B102 * WW3 * U + - constantData.B201 * UU3 * W + - constantData.B111 * 6.0f * W * U * V; -#endif -#else - INTERPOLATE(WorldPosition); -#if IS_MOTION_VECTORS_PASS - INTERPOLATE(PrevWorldPosition); -#endif -#endif - INTERPOLATE(TexCoord); - INTERPOLATE(LightmapUV); -#if USE_VERTEX_COLOR - INTERPOLATE(VertexColor); -#endif - INTERPOLATE(WorldNormal); - INTERPOLATE(WorldTangent); - COPY(InstanceOrigin); - COPY(InstanceParams); -#if USE_CUSTOM_VERTEX_INTERPOLATORS - UNROLL - for (int i = 0; i < CUSTOM_VERTEX_INTERPOLATORS_COUNT; i++) - { - INTERPOLATE(CustomVSToPS[i]); - } -#endif -#undef INTERPOLATE -#undef COPY - - // Interpolating tangents can unnormalize it, so normalize it - output.WorldNormal = normalize(output.WorldNormal); - output.WorldTangent.xyz = normalize(output.WorldTangent.xyz); - -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PHONG - // Orthogonal projection in the tangent planes - float3 posProjectedU = ProjectOntoPlane(input[0].WorldNormal, input[0].WorldPosition, output.WorldPosition); - float3 posProjectedV = ProjectOntoPlane(input[1].WorldNormal, input[1].WorldPosition, output.WorldPosition); - float3 posProjectedW = ProjectOntoPlane(input[2].WorldNormal, input[2].WorldPosition, output.WorldPosition); - - // Interpolate the projected points - output.WorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW; -#if IS_MOTION_VECTORS_PASS - posProjectedU = ProjectOntoPlane(input[0].WorldNormal, input[0].PrevWorldPosition, output.PrevWorldPosition); - posProjectedV = ProjectOntoPlane(input[1].WorldNormal, input[1].PrevWorldPosition, output.PrevWorldPosition); - posProjectedW = ProjectOntoPlane(input[2].WorldNormal, input[2].PrevWorldPosition, output.PrevWorldPosition); - output.PrevWorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW; -#endif -#endif - - // Perform displacement mapping -#if USE_DISPLACEMENT - MaterialInput materialInput = GetMaterialInput(output); - Material material = GetMaterialDS(materialInput); - output.WorldPosition += material.WorldDisplacement; -#if IS_MOTION_VECTORS_PASS - output.PrevWorldPosition += material.WorldDisplacement; -#endif -#endif - - // Recalculate the clip space position - output.Position = mul(float4(output.WorldPosition, 1), ViewProjectionMatrix); - - return output; -} - -#endif - -#if USE_LIGHTMAP - -float3 SampleLightmap(Material material, MaterialInput materialInput) -{ - // Sample lightmaps - float4 lightmap0 = Lightmap0.Sample(SamplerLinearClamp, materialInput.LightmapUV); - float4 lightmap1 = Lightmap1.Sample(SamplerLinearClamp, materialInput.LightmapUV); - float4 lightmap2 = Lightmap2.Sample(SamplerLinearClamp, materialInput.LightmapUV); - - // Unpack H-basis - float3 h0 = float3(lightmap0.x, lightmap1.x, lightmap2.x); - float3 h1 = float3(lightmap0.y, lightmap1.y, lightmap2.y); - float3 h2 = float3(lightmap0.z, lightmap1.z, lightmap2.z); - float3 h3 = float3(lightmap0.w, lightmap1.w, lightmap2.w); - - // Sample baked diffuse irradiance from the H-basis coefficients - float3 normal = material.TangentNormal; -#if MATERIAL_SHADING_MODEL == SHADING_MODEL_FOLIAGE - normal *= material.TangentNormal; -#endif - return GetHBasisIrradiance(normal, h0, h1, h2, h3) / PI; -} - -#endif - #if USE_DITHERED_LOD_TRANSITION void ClipLODTransition(PixelInput input) @@ -1081,3 +757,5 @@ float4 PS_MotionVectors(PixelInput input) : SV_Target0 return float4(0, 0, 0, 1); #endif } + +@9 diff --git a/Content/Editor/MaterialTemplates/SurfaceForward.shader b/Content/Editor/MaterialTemplates/SurfaceForward.shader index 7a97e5e5a..9ce0a5f5a 100644 --- a/Content/Editor/MaterialTemplates/SurfaceForward.shader +++ b/Content/Editor/MaterialTemplates/SurfaceForward.shader @@ -29,7 +29,6 @@ float3 ViewDir; float TimeParam; float4 ViewInfo; float4 ScreenSize; -float4 LightmapArea; float3 WorldInvScale; float WorldDeterminantSign; float2 Dummy0; @@ -319,6 +318,8 @@ float4 GetVertexColor(MaterialInput input) #endif } +@8 + // Get material properties function (for vertex shader) Material GetMaterialVS(MaterialInput input) { @@ -387,12 +388,11 @@ VertexOutput VS(ModelInput input) #if USE_VERTEX_COLOR output.VertexColor = input.Color; #endif + output.LightmapUV = input.LightmapUV; #if USE_INSTANCING - output.LightmapUV = input.LightmapUV * input.InstanceLightmapArea.zw + input.InstanceLightmapArea.xy; output.InstanceOrigin = world[3].xyz; output.InstanceParams = float2(input.InstanceOrigin.w, input.InstanceTransform1.w); #else - output.LightmapUV = input.LightmapUV * LightmapArea.zw + LightmapArea.xy; output.InstanceOrigin = WorldMatrix[3].xyz; output.InstanceParams = float2(PerInstanceRandom, LODDitherFactor); #endif @@ -576,256 +576,6 @@ VertexOutput VS_Skinned(ModelInput_Skinned input) #endif -#if USE_TESSELLATION - -// Interpolants passed from the hull shader to the domain shader -struct TessalationHSToDS -{ - float4 Position : SV_Position; - float3 WorldPosition : TEXCOORD0; - float2 TexCoord : TEXCOORD1; - float2 LightmapUV : TEXCOORD2; -#if USE_VERTEX_COLOR - half4 VertexColor : COLOR; -#endif - float3x3 TBN : TEXCOORD3; -#if USE_CUSTOM_VERTEX_INTERPOLATORS - float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; -#endif - float3 InstanceOrigin : TEXCOORD6; - float2 InstanceParams : TEXCOORD7; - float TessellationMultiplier : TESS; -}; - -// Interpolants passed from the domain shader and to the pixel shader -struct TessalationDSToPS -{ - float4 Position : SV_Position; - float3 WorldPosition : TEXCOORD0; - float2 TexCoord : TEXCOORD1; - float2 LightmapUV : TEXCOORD2; -#if USE_VERTEX_COLOR - half4 VertexColor : COLOR; -#endif - float3x3 TBN : TEXCOORD3; -#if USE_CUSTOM_VERTEX_INTERPOLATORS - float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; -#endif - float3 InstanceOrigin : TEXCOORD6; - float2 InstanceParams : TEXCOORD7; -}; - -MaterialInput GetMaterialInput(TessalationDSToPS input) -{ - MaterialInput result = (MaterialInput)0; - result.WorldPosition = input.WorldPosition; - result.TexCoord = input.TexCoord; -#if USE_LIGHTMAP - result.LightmapUV = input.LightmapUV; -#endif -#if USE_VERTEX_COLOR - result.VertexColor = input.VertexColor; -#endif - result.TBN = input.TBN; - result.TwoSidedSign = WorldDeterminantSign; - result.InstanceOrigin = input.InstanceOrigin; - result.InstanceParams = input.InstanceParams; - result.SvPosition = input.Position; -#if USE_CUSTOM_VERTEX_INTERPOLATORS - result.CustomVSToPS = input.CustomVSToPS; -#endif - return result; -} - -struct TessalationPatch -{ - float EdgeTessFactor[3] : SV_TessFactor; - float InsideTessFactor : SV_InsideTessFactor; -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN - float3 B210 : POSITION4; - float3 B120 : POSITION5; - float3 B021 : POSITION6; - float3 B012 : POSITION7; - float3 B102 : POSITION8; - float3 B201 : POSITION9; - float3 B111 : CENTER; -#endif -}; - -TessalationPatch HS_PatchConstant(InputPatch input) -{ - TessalationPatch output; - - // Average tess factors along edges, and pick an edge tess factor for the interior tessellation - float4 tessellationMultipliers; - tessellationMultipliers.x = 0.5f * (input[1].TessellationMultiplier + input[2].TessellationMultiplier); - tessellationMultipliers.y = 0.5f * (input[2].TessellationMultiplier + input[0].TessellationMultiplier); - tessellationMultipliers.z = 0.5f * (input[0].TessellationMultiplier + input[1].TessellationMultiplier); - tessellationMultipliers.w = 0.333f * (input[0].TessellationMultiplier + input[1].TessellationMultiplier + input[2].TessellationMultiplier); - tessellationMultipliers = clamp(tessellationMultipliers, 1, MAX_TESSELLATION_FACTOR); - - output.EdgeTessFactor[0] = tessellationMultipliers.x; // 1->2 edge - output.EdgeTessFactor[1] = tessellationMultipliers.y; // 2->0 edge - output.EdgeTessFactor[2] = tessellationMultipliers.z; // 0->1 edge - output.InsideTessFactor = tessellationMultipliers.w; - -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN - // Calculate PN-Triangle coefficients - // Refer to Vlachos 2001 for the original formula - float3 p1 = input[0].WorldPosition; - float3 p2 = input[1].WorldPosition; - float3 p3 = input[2].WorldPosition; - float3 n1 = input[0].TBN[2]; - float3 n2 = input[1].TBN[2]; - float3 n3 = input[2].TBN[2]; - - // Calculate control points - output.B210 = (2.0f * p1 + p2 - dot((p2 - p1), n1) * n1) / 3.0f; - output.B120 = (2.0f * p2 + p1 - dot((p1 - p2), n2) * n2) / 3.0f; - output.B021 = (2.0f * p2 + p3 - dot((p3 - p2), n2) * n2) / 3.0f; - output.B012 = (2.0f * p3 + p2 - dot((p2 - p3), n3) * n3) / 3.0f; - output.B102 = (2.0f * p3 + p1 - dot((p1 - p3), n3) * n3) / 3.0f; - output.B201 = (2.0f * p1 + p3 - dot((p3 - p1), n1) * n1) / 3.0f; - float3 e = (output.B210 + output.B120 + output.B021 + - output.B012 + output.B102 + output.B201) / 6.0f; - float3 v = (p1 + p2 + p3) / 3.0f; - output.B111 = e + ((e - v) / 2.0f); -#endif - - return output; -} - -META_HS(USE_TESSELLATION, FEATURE_LEVEL_SM5) -META_HS_PATCH(TESSELLATION_IN_CONTROL_POINTS) -[domain("tri")] -[partitioning("fractional_odd")] -[outputtopology("triangle_cw")] -[maxtessfactor(MAX_TESSELLATION_FACTOR)] -[outputcontrolpoints(3)] -[patchconstantfunc("HS_PatchConstant")] -TessalationHSToDS HS(InputPatch input, uint ControlPointID : SV_OutputControlPointID) -{ - TessalationHSToDS output; - - // Pass through shader -#define COPY(thing) output.thing = input[ControlPointID].thing; - COPY(Position); - COPY(WorldPosition); - COPY(TexCoord); - COPY(LightmapUV); -#if USE_VERTEX_COLOR - COPY(VertexColor); -#endif - COPY(TBN); - COPY(InstanceOrigin); - COPY(InstanceParams); - COPY(TessellationMultiplier); -#if USE_CUSTOM_VERTEX_INTERPOLATORS - COPY(CustomVSToPS); -#endif -#undef COPY - - return output; -} - -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PHONG - -// Orthogonal projection on to plane -float3 ProjectOntoPlane(float3 planeNormal, float3 planePoint, float3 pointToProject) -{ - return pointToProject - dot(pointToProject-planePoint, planeNormal) * planeNormal; -} - -#endif - -META_DS(USE_TESSELLATION, FEATURE_LEVEL_SM5) -[domain("tri")] -TessalationDSToPS DS(TessalationPatch constantData, float3 barycentricCoords : SV_DomainLocation, const OutputPatch input) -{ - TessalationDSToPS output; - - // Get the barycentric coords - float U = barycentricCoords.x; - float V = barycentricCoords.y; - float W = barycentricCoords.z; - - // Interpolate patch attributes to generated vertices -#define INTERPOLATE(thing) output.thing = U * input[0].thing + V * input[1].thing + W * input[2].thing -#define COPY(thing) output.thing = input[0].thing - INTERPOLATE(Position); -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN - float UU = U * U; - float VV = V * V; - float WW = W * W; - float UU3 = UU * 3.0f; - float VV3 = VV * 3.0f; - float WW3 = WW * 3.0f; - - // Interpolate using barycentric coordinates and PN Triangle control points - output.WorldPosition = - input[0].WorldPosition * UU * U + - input[1].WorldPosition * VV * V + - input[2].WorldPosition * WW * W + - constantData.B210 * UU3 * V + - constantData.B120 * VV3 * U + - constantData.B021 * VV3 * W + - constantData.B012 * WW3 * V + - constantData.B102 * WW3 * U + - constantData.B201 * UU3 * W + - constantData.B111 * 6.0f * W * U * V; -#else - INTERPOLATE(WorldPosition); -#endif - INTERPOLATE(TexCoord); - INTERPOLATE(LightmapUV); -#if USE_VERTEX_COLOR - INTERPOLATE(VertexColor); -#endif - INTERPOLATE(TBN[0]); - INTERPOLATE(TBN[1]); - INTERPOLATE(TBN[2]); - COPY(InstanceOrigin); - COPY(InstanceParams); -#if USE_CUSTOM_VERTEX_INTERPOLATORS - UNROLL - for (int i = 0; i < CUSTOM_VERTEX_INTERPOLATORS_COUNT; i++) - { - INTERPOLATE(CustomVSToPS[i]); - } -#endif -#undef INTERPOLATE -#undef COPY - - // Interpolating normal can unnormalize it, so normalize it - output.TBN[0] = normalize(output.TBN[0]); - output.TBN[1] = normalize(output.TBN[1]); - output.TBN[2] = normalize(output.TBN[2]); - -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PHONG - // Orthogonal projection in the tangent planes - float3 posProjectedU = ProjectOntoPlane(input[0].TBN[2], input[0].WorldPosition, output.WorldPosition); - float3 posProjectedV = ProjectOntoPlane(input[1].TBN[2], input[1].WorldPosition, output.WorldPosition); - float3 posProjectedW = ProjectOntoPlane(input[2].TBN[2], input[2].WorldPosition, output.WorldPosition); - - // Interpolate the projected points - output.WorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW; -#endif - - // Perform displacement mapping -#if USE_DISPLACEMENT - MaterialInput materialInput = GetMaterialInput(output); - Material material = GetMaterialDS(materialInput); - output.WorldPosition += material.WorldDisplacement; -#endif - - // Recalculate the clip space position - output.Position = mul(float4(output.WorldPosition, 1), ViewProjectionMatrix); - - return output; -} - -#endif - #if USE_DITHERED_LOD_TRANSITION void ClipLODTransition(PixelInput input) @@ -1009,3 +759,5 @@ void PS_Depth(PixelInput input OutColor = 0; #endif } + +@9 diff --git a/Content/Editor/MaterialTemplates/Terrain.shader b/Content/Editor/MaterialTemplates/Terrain.shader index 03a2081bf..0a6cb8028 100644 --- a/Content/Editor/MaterialTemplates/Terrain.shader +++ b/Content/Editor/MaterialTemplates/Terrain.shader @@ -39,15 +39,11 @@ float2 OffsetUV; float2 Dummy0; @1META_CB_END -#if CAN_USE_LIGHTMAP - // Irradiance and directionality prebaked lightmaps Texture2D Lightmap0 : register(t0); Texture2D Lightmap1 : register(t1); Texture2D Lightmap2 : register(t2); -#endif - // Terrain data Texture2D Heightmap : register(t3); Texture2D Splatmap0 : register(t4); @@ -216,6 +212,22 @@ float4 GetVertexColor(MaterialInput input) return 1; } +// Evaluates the H-Basis coefficients in the tangent space normal direction +float3 GetHBasisIrradiance(float3 n, float3 h0, float3 h1, float3 h2, float3 h3) +{ + // Band 0 + float3 color = h0 * (1.0f / sqrt(2.0f * PI)); + + // Band 1 + color += h1 * -sqrt(1.5f / PI) * n.y; + color += h2 * sqrt(1.5f / PI) * (2 * n.z - 1.0f); + color += h3 * -sqrt(1.5f / PI) * n.x; + + return color; +} + +@8 + // Get material properties function (for vertex shader) Material GetMaterialVS(MaterialInput input) { @@ -781,3 +793,5 @@ void PS_Depth(PixelInput input OutColor = 0; #endif } + +@9 diff --git a/Source/Engine/Content/Assets/Material.cpp b/Source/Engine/Content/Assets/Material.cpp index 571e46243..966253aba 100644 --- a/Source/Engine/Content/Assets/Material.cpp +++ b/Source/Engine/Content/Assets/Material.cpp @@ -458,7 +458,6 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options) options.Macros.Add({ "USE_FORWARD", Numbers[useForward ? 1 : 0] }); options.Macros.Add({ "USE_DEFERRED", Numbers[isSurfaceOrTerrain && info.BlendMode == MaterialBlendMode::Opaque ? 1 : 0] }); options.Macros.Add({ "USE_DISTORTION", Numbers[useDistortion ? 1 : 0] }); - options.Macros.Add({ "CAN_USE_LIGHTMAP", Numbers[isSurfaceOrTerrain ? 1 : 0] }); #endif } diff --git a/Source/Engine/Graphics/Materials/DecalMaterialShader.cpp b/Source/Engine/Graphics/Materials/DecalMaterialShader.cpp index c5166a088..27e9c7b38 100644 --- a/Source/Engine/Graphics/Materials/DecalMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/DecalMaterialShader.cpp @@ -44,7 +44,7 @@ void DecalMaterialShader::Bind(BindParameters& params) // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; - bindMeta.Buffer0 = hasCb0 ? _cb0Data.Get() + sizeof(DecalMaterialShaderData) : nullptr; + bindMeta.Constants = hasCb0 ? _cb0Data.Get() + sizeof(DecalMaterialShaderData) : nullptr; bindMeta.Input = nullptr; bindMeta.Buffers = nullptr; bindMeta.CanSampleDepth = true; diff --git a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp index 5a460079d..322726a3e 100644 --- a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "DeferredMaterialShader.h" +#include "MaterialShaderFeatures.h" #include "MaterialParams.h" #include "Engine/Graphics/RenderBuffers.h" #include "Engine/Graphics/RenderView.h" @@ -28,7 +29,6 @@ PACK_STRUCT(struct DeferredMaterialShaderData { float TimeParam; Vector4 ViewInfo; Vector4 ScreenSize; - Rectangle LightmapArea; Vector3 WorldInvScale; float WorldDeterminantSign; Vector2 Dummy0; @@ -63,11 +63,21 @@ void DeferredMaterialShader::Bind(BindParameters& params) auto& drawCall = *params.FirstDrawCall; const auto cb0 = _shader->GetCB(0); const bool hasCb0 = cb0 && cb0->GetSize() != 0; + ASSERT(hasCb0 && "TODO: fix it"); // TODO: always make cb pointer valid even if cb is missing + byte* cb = _cb0Data.Get(); + auto materialData = reinterpret_cast(cb); + cb += sizeof(DeferredMaterialShaderData); + int32 srv = 0; + + // Setup features + if (_info.TessellationMode != TessellationMethod::None) + TessellationFeature::Bind(params, cb, srv); + const bool useLightmap = _info.BlendMode == MaterialBlendMode::Opaque && LightmapFeature::Bind(params, cb, srv); // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; - bindMeta.Buffer0 = hasCb0 ? _cb0Data.Get() + sizeof(DeferredMaterialShaderData) : nullptr; + bindMeta.Constants = cb; bindMeta.Input = nullptr; bindMeta.Buffers = nullptr; bindMeta.CanSampleDepth = false; @@ -75,7 +85,6 @@ void DeferredMaterialShader::Bind(BindParameters& params) MaterialParams::Bind(params.ParamsLink, bindMeta); // Setup material constants data - auto materialData = reinterpret_cast(_cb0Data.Get()); if (hasCb0) { Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix); @@ -107,23 +116,6 @@ void DeferredMaterialShader::Bind(BindParameters& params) materialData->TemporalAAJitter = view.TemporalAAJitter; materialData->GeometrySize = drawCall.Surface.GeometrySize; } - const bool useLightmap = view.Flags & ViewFlags::GI -#if USE_EDITOR - && EnableLightmapsUsage -#endif - && drawCall.Surface.Lightmap != nullptr; - if (useLightmap) - { - // Bind lightmap textures - GPUTexture *lightmap0, *lightmap1, *lightmap2; - drawCall.Surface.Lightmap->GetTextures(&lightmap0, &lightmap1, &lightmap2); - context->BindSR(0, lightmap0); - context->BindSR(1, lightmap1); - context->BindSR(2, lightmap2); - - // Set lightmap data - materialData->LightmapArea = drawCall.Surface.LightmapUVsArea; - } // Check if is using mesh skinning const bool useSkinning = drawCall.Surface.Skinning != nullptr; diff --git a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp index 39e39a7d4..7dc68a85a 100644 --- a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "ForwardMaterialShader.h" +#include "MaterialShaderFeatures.h" #include "MaterialParams.h" #include "Engine/Engine/Time.h" #include "Engine/Graphics/GPULimits.h" @@ -29,7 +30,6 @@ PACK_STRUCT(struct ForwardMaterialShaderData { float TimeParam; Vector4 ViewInfo; Vector4 ScreenSize; - Rectangle LightmapArea; Vector3 WorldInvScale; float WorldDeterminantSign; Vector2 Dummy0; @@ -70,13 +70,22 @@ void ForwardMaterialShader::Bind(BindParameters& params) auto& drawCall = *params.FirstDrawCall; const auto cb0 = _shader->GetCB(0); const bool hasCb0 = cb0 && cb0->GetSize() != 0; + ASSERT(hasCb0 && "TODO: fix it"); // TODO: always make cb pointer valid even if cb is missing const auto cb1 = _shader->GetCB(1); const bool hasCb1 = cb1 && cb1->GetSize() != 0; + byte* cb = _cb0Data.Get(); + auto materialData = reinterpret_cast(cb); + cb += sizeof(ForwardMaterialShaderData); + int32 srv = 0; + + // Setup features + if (_info.TessellationMode != TessellationMethod::None) + TessellationFeature::Bind(params, cb, srv); // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; - bindMeta.Buffer0 = hasCb0 ? _cb0Data.Get() + sizeof(ForwardMaterialShaderData) : nullptr; + bindMeta.Constants = cb; bindMeta.Input = nullptr; // forward pass materials cannot sample scene color for now bindMeta.Buffers = params.RenderContext.Buffers; bindMeta.CanSampleDepth = GPUDevice::Instance->Limits.HasReadOnlyDepth; @@ -93,7 +102,6 @@ void ForwardMaterialShader::Bind(BindParameters& params) } // Setup material constants data - const auto materialData = reinterpret_cast(_cb0Data.Get()); if (hasCb0) { Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix); diff --git a/Source/Engine/Graphics/Materials/GUIMaterialShader.cpp b/Source/Engine/Graphics/Materials/GUIMaterialShader.cpp index 4b079dd54..23b327a1b 100644 --- a/Source/Engine/Graphics/Materials/GUIMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/GUIMaterialShader.cpp @@ -36,7 +36,7 @@ void GUIMaterialShader::Bind(BindParameters& params) // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; - bindMeta.Buffer0 = hasCb0 ? _cb0Data.Get() + sizeof(GUIMaterialShaderData) : nullptr; + bindMeta.Constants = hasCb0 ? _cb0Data.Get() + sizeof(GUIMaterialShaderData) : nullptr; bindMeta.Input = nullptr; bindMeta.Buffers = nullptr; bindMeta.CanSampleDepth = false; diff --git a/Source/Engine/Graphics/Materials/IMaterial.h b/Source/Engine/Graphics/Materials/IMaterial.h index f1e7856af..e85328133 100644 --- a/Source/Engine/Graphics/Materials/IMaterial.h +++ b/Source/Engine/Graphics/Materials/IMaterial.h @@ -29,7 +29,6 @@ public: /// /// Determines whether material is a surface shader. /// - /// true if material is surface shader; otherwise, false. FORCE_INLINE bool IsSurface() const { return GetInfo().Domain == MaterialDomain::Surface; @@ -38,7 +37,6 @@ public: /// /// Determines whether material is a post fx. /// - /// true if material is post fx; otherwise, false. FORCE_INLINE bool IsPostFx() const { return GetInfo().Domain == MaterialDomain::PostProcess; @@ -47,7 +45,6 @@ public: /// /// Determines whether material is a decal. /// - /// true if material is decal; otherwise, false. FORCE_INLINE bool IsDecal() const { return GetInfo().Domain == MaterialDomain::Decal; @@ -56,7 +53,6 @@ public: /// /// Determines whether material is a GUI shader. /// - /// true if material is GUI shader; otherwise, false. FORCE_INLINE bool IsGUI() const { return GetInfo().Domain == MaterialDomain::GUI; @@ -65,7 +61,6 @@ public: /// /// Determines whether material is a terrain shader. /// - /// true if material is terrain shader; otherwise, false. FORCE_INLINE bool IsTerrain() const { return GetInfo().Domain == MaterialDomain::Terrain; @@ -74,7 +69,6 @@ public: /// /// Determines whether material is a particle shader. /// - /// true if material is particle shader; otherwise, false. FORCE_INLINE bool IsParticle() const { return GetInfo().Domain == MaterialDomain::Particle; diff --git a/Source/Engine/Graphics/Materials/MaterialParams.cpp b/Source/Engine/Graphics/Materials/MaterialParams.cpp index 1f1d1bac6..ea70d4587 100644 --- a/Source/Engine/Graphics/Materials/MaterialParams.cpp +++ b/Source/Engine/Graphics/Materials/MaterialParams.cpp @@ -230,36 +230,28 @@ void MaterialParameter::Bind(BindMeta& meta) const switch (_type) { case MaterialParameterType::Bool: - if (meta.Buffer0) - *((int32*)(meta.Buffer0 + _offset)) = _asBool; + *((int32*)(meta.Constants + _offset)) = _asBool; break; case MaterialParameterType::Integer: - if (meta.Buffer0) - *((int32*)(meta.Buffer0 + _offset)) = _asInteger; + *((int32*)(meta.Constants + _offset)) = _asInteger; break; case MaterialParameterType::Float: - if (meta.Buffer0) - *((float*)(meta.Buffer0 + _offset)) = _asFloat; + *((float*)(meta.Constants + _offset)) = _asFloat; break; case MaterialParameterType::Vector2: - if (meta.Buffer0) - *((Vector2*)(meta.Buffer0 + _offset)) = _asVector2; + *((Vector2*)(meta.Constants + _offset)) = _asVector2; break; case MaterialParameterType::Vector3: - if (meta.Buffer0) - *((Vector3*)(meta.Buffer0 + _offset)) = _asVector3; + *((Vector3*)(meta.Constants + _offset)) = _asVector3; break; case MaterialParameterType::Vector4: - if (meta.Buffer0) - *((Vector4*)(meta.Buffer0 + _offset)) = _asVector4; + *((Vector4*)(meta.Constants + _offset)) = _asVector4; break; case MaterialParameterType::Color: - if (meta.Buffer0) - *((Color*)(meta.Buffer0 + _offset)) = _asColor; + *((Color*)(meta.Constants + _offset)) = _asColor; break; case MaterialParameterType::Matrix: - if (meta.Buffer0) - Matrix::Transpose(_asMatrix, *(Matrix*)(meta.Buffer0 + _offset)); + Matrix::Transpose(_asMatrix, *(Matrix*)(meta.Constants + _offset)); break; case MaterialParameterType::NormalMap: { @@ -336,11 +328,10 @@ void MaterialParameter::Bind(BindMeta& meta) const break; } case MaterialParameterType::ChannelMask: - if (meta.Buffer0) - *((Vector4*)(meta.Buffer0 + _offset)) = Vector4(_asInteger == 0, _asInteger == 1, _asInteger == 2, _asInteger == 3); + *((Vector4*)(meta.Constants + _offset)) = Vector4(_asInteger == 0, _asInteger == 1, _asInteger == 2, _asInteger == 3); break; case MaterialParameterType::GameplayGlobal: - if (meta.Buffer0 && _asAsset) + if (_asAsset) { const auto e = _asAsset.As()->Variables.TryGet(_name); if (e) @@ -348,26 +339,26 @@ void MaterialParameter::Bind(BindMeta& meta) const switch (e->Value.Type.Type) { case VariantType::Bool: - *((bool*)(meta.Buffer0 + _offset)) = e->Value.AsBool; + *((bool*)(meta.Constants + _offset)) = e->Value.AsBool; break; case VariantType::Int: - *((int32*)(meta.Buffer0 + _offset)) = e->Value.AsInt; + *((int32*)(meta.Constants + _offset)) = e->Value.AsInt; break; case VariantType::Uint: - *((uint32*)(meta.Buffer0 + _offset)) = e->Value.AsUint; + *((uint32*)(meta.Constants + _offset)) = e->Value.AsUint; break; case VariantType::Float: - *((float*)(meta.Buffer0 + _offset)) = e->Value.AsFloat; + *((float*)(meta.Constants + _offset)) = e->Value.AsFloat; break; case VariantType::Vector2: - *((Vector2*)(meta.Buffer0 + _offset)) = e->Value.AsVector2(); + *((Vector2*)(meta.Constants + _offset)) = e->Value.AsVector2(); break; case VariantType::Vector3: - *((Vector3*)(meta.Buffer0 + _offset)) = e->Value.AsVector3(); + *((Vector3*)(meta.Constants + _offset)) = e->Value.AsVector3(); break; case VariantType::Vector4: case VariantType::Color: - *((Vector4*)(meta.Buffer0 + _offset)) = e->Value.AsVector4(); + *((Vector4*)(meta.Constants + _offset)) = e->Value.AsVector4(); break; default: ; } diff --git a/Source/Engine/Graphics/Materials/MaterialParams.h b/Source/Engine/Graphics/Materials/MaterialParams.h index 997b18d34..ca48dc143 100644 --- a/Source/Engine/Graphics/Materials/MaterialParams.h +++ b/Source/Engine/Graphics/Materials/MaterialParams.h @@ -309,9 +309,9 @@ public: GPUContext* Context; /// - /// The pointer to the first constants buffer in memory. + /// The pointer to the constants buffer in the memory. /// - byte* Buffer0; + byte* Constants; /// /// The input scene color. It's optional and used in forward/postFx rendering. diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp new file mode 100644 index 000000000..a07e740f8 --- /dev/null +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp @@ -0,0 +1,172 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#include "MaterialShaderFeatures.h" +#include "Engine/Graphics/RenderTask.h" +#include "Engine/Renderer/RenderList.h" +#include "Engine/Renderer/ShadowsPass.h" +#if USE_EDITOR +#include "Engine/Renderer/Lightmaps.h" +#endif +#include "Engine/Level/Scene/Lightmap.h" +#include "Engine/Level/Actors/EnvironmentProbe.h" + +void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, byte*& cb, int32& srv) +{ + auto context = params.GPUContext; + auto cache = params.RenderContext.List; + auto& view = params.RenderContext.View; + auto& drawCall = *params.FirstDrawCall; + auto& data = *(Data*)cb; + const int32 envProbeShaderRegisterIndex = srv + 0; + const int32 skyLightShaderRegisterIndex = srv + 1; + const int32 dirLightShaderRegisterIndex = srv + 2; + + // Set fog input + if (cache->Fog) + { + cache->Fog->GetExponentialHeightFogData(view, data.ExponentialHeightFog); + } + else + { + data.ExponentialHeightFog.FogMinOpacity = 1.0f; + data.ExponentialHeightFog.ApplyDirectionalInscattering = 0.0f; + } + + // Set directional light input + if (cache->DirectionalLights.HasItems()) + { + const auto& dirLight = cache->DirectionalLights.First(); + const auto shadowPass = ShadowsPass::Instance(); + const bool useShadow = shadowPass->LastDirLightIndex == 0; + if (useShadow) + { + data.DirectionalLightShadow = shadowPass->LastDirLight; + context->BindSR(dirLightShaderRegisterIndex, shadowPass->LastDirLightShadowMap); + } + else + { + context->UnBindSR(dirLightShaderRegisterIndex); + } + dirLight.SetupLightData(&data.DirectionalLight, view, useShadow); + } + else + { + data.DirectionalLight.Color = Vector3::Zero; + data.DirectionalLight.CastShadows = 0.0f; + context->UnBindSR(dirLightShaderRegisterIndex); + } + + // Set sky light + if (cache->SkyLights.HasItems()) + { + auto& skyLight = cache->SkyLights.First(); + skyLight.SetupLightData(&data.SkyLight, view, false); + const auto texture = skyLight.Image ? skyLight.Image->GetTexture() : nullptr; + context->BindSR(skyLightShaderRegisterIndex, GET_TEXTURE_VIEW_SAFE(texture)); + } + else + { + Platform::MemoryClear(&data.SkyLight, sizeof(data.SkyLight)); + context->UnBindSR(skyLightShaderRegisterIndex); + } + + // Set reflection probe data + EnvironmentProbe* probe = nullptr; + // TODO: optimize env probe searching for a transparent material - use spatial cache for renderer to find it + for (int32 i = 0; i < cache->EnvironmentProbes.Count(); i++) + { + const auto p = cache->EnvironmentProbes[i]; + if (p->GetSphere().Contains(drawCall.World.GetTranslation()) != ContainmentType::Disjoint) + { + probe = p; + break; + } + } + if (probe && probe->GetProbe()) + { + probe->SetupProbeData(&data.EnvironmentProbe); + const auto texture = probe->GetProbe()->GetTexture(); + context->BindSR(envProbeShaderRegisterIndex, GET_TEXTURE_VIEW_SAFE(texture)); + } + else + { + data.EnvironmentProbe.Data1 = Vector4::Zero; + context->UnBindSR(envProbeShaderRegisterIndex); + } + + // Set local lights + data.LocalLightsCount = 0; + for (int32 i = 0; i < cache->PointLights.Count(); i++) + { + const auto& light = cache->PointLights[i]; + if (BoundingSphere(light.Position, light.Radius).Contains(drawCall.World.GetTranslation()) != ContainmentType::Disjoint) + { + light.SetupLightData(&data.LocalLights[data.LocalLightsCount], view, false); + data.LocalLightsCount++; + if (data.LocalLightsCount == MaxLocalLights) + break; + } + } + for (int32 i = 0; i < cache->SpotLights.Count(); i++) + { + const auto& light = cache->SpotLights[i]; + if (BoundingSphere(light.Position, light.Radius).Contains(drawCall.World.GetTranslation()) != ContainmentType::Disjoint) + { + light.SetupLightData(&data.LocalLights[data.LocalLightsCount], view, false); + data.LocalLightsCount++; + if (data.LocalLightsCount == MaxLocalLights) + break; + } + } + + cb += sizeof(Data); + srv += SRVs; +} + +bool LightmapFeature::Bind(MaterialShader::BindParameters& params, byte*& cb, int32& srv) +{ + auto context = params.GPUContext; + auto& view = params.RenderContext.View; + auto& drawCall = *params.FirstDrawCall; + auto& data = *(Data*)cb; + + const bool useLightmap = view.Flags & ViewFlags::GI +#if USE_EDITOR + && EnableLightmapsUsage +#endif + && drawCall.Surface.Lightmap != nullptr; + if (useLightmap) + { + // Bind lightmap textures + GPUTexture *lightmap0, *lightmap1, *lightmap2; + drawCall.Surface.Lightmap->GetTextures(&lightmap0, &lightmap1, &lightmap2); + context->BindSR(0, lightmap0); + context->BindSR(1, lightmap1); + context->BindSR(2, lightmap2); + + // Set lightmap data + data.LightmapArea = drawCall.Surface.LightmapUVsArea; + } + + srv += SRVs; + cb += sizeof(Data); + return useLightmap; +} + +#if USE_EDITOR + +void TessellationFeature::Generate(GeneratorData& data) +{ + data.Template = TEXT("Tessellation.hlsl"); + data.ConstantsSize = 0; + data.ResourcesCount = SRVs; +} + +void LightmapFeature::Generate(GeneratorData& data) +{ + data.Template = TEXT("Lightmap.hlsl"); + data.ConstantsSize = sizeof(Data); + data.ResourcesCount = SRVs; +} + +#endif diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h new file mode 100644 index 000000000..0a2349dfe --- /dev/null +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h @@ -0,0 +1,70 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "MaterialShader.h" +#include "Engine/Core/Math/Rectangle.h" + +// Material shader features are plugin-based functionalities that are reusable between different material domains. +struct MaterialShaderFeature +{ +#if USE_EDITOR + struct GeneratorData + { + const Char* Template; + int32 ConstantsSize; + int32 ResourcesCount; + }; +#endif; +}; + +// Material shader feature that add support for Forward shading inside the material shader. +struct ForwardShadingFeature : MaterialShaderFeature +{ + enum { MaxLocalLights = 4 }; + + enum { SRVs = 3 }; + + PACK_STRUCT(struct Data + { + LightData DirectionalLight; + LightShadowData DirectionalLightShadow; + LightData SkyLight; + ProbeData EnvironmentProbe; + ExponentialHeightFogData ExponentialHeightFog; + Vector3 Dummy2; + uint32 LocalLightsCount; + LightData LocalLights[MaxLocalLights]; + }); + + static void Bind(MaterialShader::BindParameters& params, byte*& cb, int32& srv); +}; + +// Material shader feature that adds geometry hardware tessellation (using Hull and Domain shaders). +struct TessellationFeature : MaterialShaderFeature +{ + enum { SRVs = 0 }; + + static void Bind(MaterialShader::BindParameters& params, byte*& cb, int32& srv) + { + } +#if USE_EDITOR + static void Generate(GeneratorData& data); +#endif +}; + +// Material shader feature that adds lightmap sampling feature. +struct LightmapFeature : MaterialShaderFeature +{ + enum { SRVs = 3 }; + + PACK_STRUCT(struct Data + { + Rectangle LightmapArea; + }); + + static bool Bind(MaterialShader::BindParameters& params, byte*& cb, int32& srv); +#if USE_EDITOR + static void Generate(GeneratorData& data); +#endif +}; diff --git a/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp b/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp index 210c81e7d..5cfad0126 100644 --- a/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp @@ -81,7 +81,7 @@ void ParticleMaterialShader::Bind(BindParameters& params) // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; - bindMeta.Buffer0 = hasCb0 ? _cb0Data.Get() + sizeof(ParticleMaterialShaderData) : nullptr; + bindMeta.Constants = hasCb0 ? _cb0Data.Get() + sizeof(ParticleMaterialShaderData) : nullptr; bindMeta.Input = nullptr; bindMeta.Buffers = params.RenderContext.Buffers; bindMeta.CanSampleDepth = GPUDevice::Instance->Limits.HasReadOnlyDepth; @@ -105,7 +105,7 @@ void ParticleMaterialShader::Bind(BindParameters& params) auto name = StringView(param.GetName().Get() + 9); const int32 offset = drawCall.Particle.Particles->Layout->FindAttributeOffset(name); - *((int32*)(bindMeta.Buffer0 + param.GetBindOffset())) = offset; + *((int32*)(bindMeta.Constants + param.GetBindOffset())) = offset; } } } diff --git a/Source/Engine/Graphics/Materials/PostFxMaterialShader.cpp b/Source/Engine/Graphics/Materials/PostFxMaterialShader.cpp index 5c2fb1df0..2d9840103 100644 --- a/Source/Engine/Graphics/Materials/PostFxMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/PostFxMaterialShader.cpp @@ -33,7 +33,7 @@ void PostFxMaterialShader::Bind(BindParameters& params) // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; - bindMeta.Buffer0 = hasCb0 ? _cb0Data.Get() + sizeof(PostFxMaterialShaderData) : nullptr; + bindMeta.Constants = hasCb0 ? _cb0Data.Get() + sizeof(PostFxMaterialShaderData) : nullptr; bindMeta.Input = params.Input; bindMeta.Buffers = params.RenderContext.Buffers; bindMeta.CanSampleDepth = true; diff --git a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp index 37f932a5a..68c5fbd20 100644 --- a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp @@ -60,7 +60,7 @@ void TerrainMaterialShader::Bind(BindParameters& params) // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; - bindMeta.Buffer0 = hasCb0 ? _cb0Data.Get() + sizeof(TerrainMaterialShaderData) : nullptr; + bindMeta.Constants = hasCb0 ? _cb0Data.Get() + sizeof(TerrainMaterialShaderData) : nullptr; bindMeta.Input = nullptr; bindMeta.Buffers = nullptr; bindMeta.CanSampleDepth = false; diff --git a/Source/Engine/Particles/Graph/GPU/GPUParticles.cpp b/Source/Engine/Particles/Graph/GPU/GPUParticles.cpp index befd5b25d..cd16a9c52 100644 --- a/Source/Engine/Particles/Graph/GPU/GPUParticles.cpp +++ b/Source/Engine/Particles/Graph/GPU/GPUParticles.cpp @@ -155,7 +155,7 @@ void GPUParticles::Execute(GPUContext* context, ParticleEmitter* emitter, Partic // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; - bindMeta.Buffer0 = hasCB ? _cbData.Get() + sizeof(GPUParticlesData) : nullptr; + bindMeta.Constants = hasCB ? _cbData.Get() + sizeof(GPUParticlesData) : nullptr; bindMeta.Input = nullptr; if (viewTask) { diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Texture.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Texture.cpp index 90103c165..32af18364 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Texture.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Texture.cpp @@ -163,27 +163,4 @@ void MaterialGenerator::linearizeSceneDepth(Node* caller, const Value& depth, Va value = writeLocal(VariantType::Float, String::Format(TEXT("ViewInfo.w / ({0}.x - ViewInfo.z)"), depth.Value), caller); } -byte MaterialGenerator::getStartSrvRegister(MaterialLayer* baseLayer) -{ - // Note: this must match material templates - switch (baseLayer->Domain) - { - case MaterialDomain::Surface: - return baseLayer->BlendMode == MaterialBlendMode::Transparent ? 3 : 3; - case MaterialDomain::PostProcess: - return 0; - case MaterialDomain::Decal: - return 1; - case MaterialDomain::GUI: - return 0; - case MaterialDomain::Terrain: - return 6; - case MaterialDomain::Particle: - return 5; - default: - CRASH; - return 0; - } -} - #endif diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp index 7abd29af9..61c05149d 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp @@ -4,7 +4,9 @@ #include "MaterialGenerator.h" #include "Engine/Visject/ShaderGraphUtilities.h" +#include "Engine/Platform/File.h" #include "Engine/Graphics/Materials/MaterialShader.h" +#include "Engine/Graphics/Materials/MaterialShaderFeatures.h" /// /// Material shader source code template has special marks for generated code. @@ -20,10 +22,86 @@ enum MaterialTemplateInputsMapping In_GetMaterialVS = 5, In_GetMaterialDS = 6, In_Includes = 7, + In_Utilities = 8, + In_Shaders = 9, In_MAX }; +/// +/// Material shader feature source code template has special marks for generated code. Each starts with '@' char and index of the mapped string. +/// +enum class FeatureTemplateInputsMapping +{ + Defines = 0, + Includes = 1, + Constants = 2, + Resources = 3, + Utilities = 4, + Shaders = 5, + MAX +}; + +struct FeatureData +{ + MaterialShaderFeature::GeneratorData Data; + String Inputs[(int32)FeatureTemplateInputsMapping::MAX]; + + bool Init(); +}; + +namespace +{ + // Loaded and parsed features data cache + Dictionary Features; +} + +bool FeatureData::Init() +{ + // Load template file + const String path = Globals::EngineContentFolder / TEXT("Editor/MaterialTemplates/Features/") + Data.Template; + String contents; + if (File::ReadAllText(path, contents)) + { + LOG(Error, "Cannot open file {0}", path); + return true; + } + + int32 i = 0; + const int32 length = contents.Length(); + + // Skip until input start + for (; i < length; i++) + { + if (contents[i] == '@') + break; + } + + // Load all inputs + do + { + // Parse input type + i++; + const int32 inIndex = contents[i++] - '0'; + ASSERT_LOW_LAYER(Math::IsInRange(inIndex, 0, (int32)FeatureTemplateInputsMapping::MAX - 1)); + + // Read until next input start + const Char* start = &contents[i]; + for (; i < length; i++) + { + const auto c = contents[i]; + if (c == '@') + break; + } + const Char* end = &contents[i]; + + // Set input + Inputs[inIndex].Set(start, (int32)(end - start)); + } while (i < length); + + return false; +} + MaterialValue MaterialGenerator::getUVs(VariantType::Vector2, TEXT("input.TexCoord")); MaterialValue MaterialGenerator::getTime(VariantType::Float, TEXT("TimeParam")); MaterialValue MaterialGenerator::getNormal(VariantType::Vector3, TEXT("input.TBN[2]")); @@ -53,6 +131,7 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo ASSERT_LOW_LAYER(_layers.Count() > 0); String inputs[In_MAX]; + Array> features; // Setup and prepare layers _writer.Clear(); @@ -87,6 +166,32 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo const MaterialGraphBox* layerInputBox = baseLayer->Root->GetBox(0); const bool isLayered = layerInputBox->HasConnection(); + // Initialize features +#define ADD_FEATURE(type) \ + { \ + StringAnsiView typeName(#type, ARRAY_COUNT(#type) - 1); \ + features.Add(typeName); \ + if (!Features.ContainsKey(typeName)) \ + { \ + auto& feature = Features[typeName]; \ + type::Generate(feature.Data); \ + if (feature.Init()) \ + return true; \ + } \ + } + switch (baseLayer->Domain) + { + case MaterialDomain::Surface: + if (materialInfo.TessellationMode != TessellationMethod::None) + ADD_FEATURE(TessellationFeature); + if (materialInfo.BlendMode == MaterialBlendMode::Opaque) + ADD_FEATURE(LightmapFeature); + break; + default: + break; + } +#undef ADD_FEATURE + // Check if material is using special features and update the metadata flags if (!isLayered) { @@ -240,11 +345,13 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo // Update material usage based on material generator outputs materialInfo.UsageFlags = baseLayer->UsageFlags; +#define WRITE_FEATURES(input) for (auto f : features) _writer.Write(Features[f].Inputs[(int32)FeatureTemplateInputsMapping::input]); // Defines { _writer.Write(TEXT("#define MATERIAL_MASK_THRESHOLD ({0})\n"), baseLayer->MaskThreshold); _writer.Write(TEXT("#define CUSTOM_VERTEX_INTERPOLATORS_COUNT ({0})\n"), _vsToPsInterpolants.Count()); - _writer.Write(TEXT("#define MATERIAL_OPACITY_THRESHOLD ({0})"), baseLayer->OpacityThreshold); + _writer.Write(TEXT("#define MATERIAL_OPACITY_THRESHOLD ({0})\n"), baseLayer->OpacityThreshold); + WRITE_FEATURES(Defines); inputs[In_Defines] = _writer.ToString(); _writer.Clear(); } @@ -252,31 +359,87 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo // Includes { for (auto& include : _includes) - { _writer.Write(TEXT("#include \"{0}\"\n"), include.Item); - } + WRITE_FEATURES(Includes); inputs[In_Includes] = _writer.ToString(); _writer.Clear(); } - // Check if material is using any parameters - if (_parameters.HasItems()) + // Constants { - ShaderGraphUtilities::GenerateShaderConstantBuffer(_writer, _parameters); + WRITE_FEATURES(Constants); + if (_parameters.HasItems()) + ShaderGraphUtilities::GenerateShaderConstantBuffer(_writer, _parameters); inputs[In_Constants] = _writer.ToString(); _writer.Clear(); + } - const int32 startRegister = getStartSrvRegister(baseLayer); - const auto error = ShaderGraphUtilities::GenerateShaderResources(_writer, _parameters, startRegister); - if (error) + // Resources + { + int32 srv = 0; + switch (baseLayer->Domain) { - OnError(nullptr, nullptr, error); - return true; + case MaterialDomain::Surface: + if (materialInfo.BlendMode != MaterialBlendMode::Opaque) + srv = 3; // Forward shading resources + break; + case MaterialDomain::Decal: + srv = 1; + break; + case MaterialDomain::Terrain: + srv = 6; + break; + case MaterialDomain::Particle: + srv = 5; + break; + } + for (auto f : features) + { + const auto& text = Features[f].Inputs[(int32)FeatureTemplateInputsMapping::Resources]; + const Char* str = text.Get(); + int32 prevIdx = 0, idx = 0; + while (true) + { + idx = text.Find(TEXT("__SRV__"), StringSearchCase::CaseSensitive, prevIdx); + if (idx == -1) + break; + int32 len = idx - prevIdx; + _writer.Write(StringView(str, len)); + str += len; + _writer.Write(StringUtils::ToString(srv)); + srv++; + str += ARRAY_COUNT("__SRV__") - 1; + prevIdx = idx + ARRAY_COUNT("__SRV__") - 1; + } + _writer.Write(StringView(str)); + } + if (_parameters.HasItems()) + { + const auto error = ShaderGraphUtilities::GenerateShaderResources(_writer, _parameters, srv); + if (error) + { + OnError(nullptr, nullptr, error); + return true; + } } inputs[In_ShaderResources] = _writer.ToString(); _writer.Clear(); } + // Utilities + { + WRITE_FEATURES(Utilities); + inputs[In_Utilities] = _writer.ToString(); + _writer.Clear(); + } + + // Shaders + { + WRITE_FEATURES(Shaders); + inputs[In_Shaders] = _writer.ToString(); + _writer.Clear(); + } + // Save material parameters data if (_parameters.HasItems()) MaterialParams::Save(parametersData, &_parameters); @@ -315,16 +478,15 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo LOG(Warning, "Unknown material domain."); return true; } - auto file = FileReadStream::Open(path); if (file == nullptr) { - LOG(Warning, "Cannot load material base source code."); + LOG(Error, "Cannot open file {0}", path); return true; } // Format template - uint32 length = file->GetLength(); + const uint32 length = file->GetLength(); Array tmp; for (uint32 i = 0; i < length; i++) { diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.h b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.h index 005b9c58c..9ae85cac8 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.h +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.h @@ -209,8 +209,6 @@ public: static MaterialGraphBoxesMapping MaterialGraphBoxesMappings[]; static const MaterialGraphBoxesMapping& GetMaterialRootNodeBox(MaterialGraphBoxes box); - - static byte getStartSrvRegister(MaterialLayer* baseLayer); }; #endif diff --git a/Source/Engine/Utilities/TextWriter.h b/Source/Engine/Utilities/TextWriter.h index d2702bf3c..54f2e996a 100644 --- a/Source/Engine/Utilities/TextWriter.h +++ b/Source/Engine/Utilities/TextWriter.h @@ -4,6 +4,8 @@ #include "Engine/Core/NonCopyable.h" #include "Engine/Core/Formatting.h" +#include "Engine/Core/Types/String.h" +#include "Engine/Core/Types/StringView.h" #include "Engine/Serialization/MemoryWriteStream.h" /// @@ -97,6 +99,24 @@ public: WriteLine(); } + /// + /// Write text to the buffer + /// + /// Data + void Write(const StringViewBase& text) + { + _buffer.WriteBytes((void*)text.Get(), text.Length() * sizeof(CharType)); + } + + /// + /// Write text to the buffer + /// + /// Data + void Write(const StringBase& text) + { + _buffer.WriteBytes((void*)text.Get(), text.Length() * sizeof(CharType)); + } + /// /// Write text to the buffer /// diff --git a/Source/Engine/Visject/ShaderGraphUtilities.cpp b/Source/Engine/Visject/ShaderGraphUtilities.cpp index c84a500e4..079b6e602 100644 --- a/Source/Engine/Visject/ShaderGraphUtilities.cpp +++ b/Source/Engine/Visject/ShaderGraphUtilities.cpp @@ -177,10 +177,6 @@ const Char* ShaderGraphUtilities::GenerateShaderResources(TextWriterUnicode& wri } } } - - if (startRegister != registerIndex) - writer.WriteLine(); - return nullptr; } diff --git a/Source/Shaders/MaterialCommon.hlsl b/Source/Shaders/MaterialCommon.hlsl index 8f47124dc..aef3fc579 100644 --- a/Source/Shaders/MaterialCommon.hlsl +++ b/Source/Shaders/MaterialCommon.hlsl @@ -173,24 +173,4 @@ float3 AOMultiBounce(float visibility, float3 albedo) return max(visibility, ((visibility * a + b) * visibility + c) * visibility); } -#if CAN_USE_LIGHTMAP - -// Evaluates the H-Basis coefficients in the tangent space normal direction -float3 GetHBasisIrradiance(in float3 n, in float3 h0, in float3 h1, in float3 h2, in float3 h3) -{ - float3 color = 0.0f; - - // Band 0 - color += h0 * (1.0f / sqrt(2.0f * PI)); - - // Band 1 - color += h1 * -sqrt(1.5f / PI) * n.y; - color += h2 * sqrt(1.5f / PI) * (2 * n.z - 1.0f); - color += h3 * -sqrt(1.5f / PI) * n.x; - - return color; -} - -#endif - #endif