// File generated by Flax Materials Editor // Version: @0 #define MATERIAL 1 @3 // Enables/disables smooth terrain chunks LOD transitions (with morphing higher LOD near edges to the lower LOD in the neighbour) #define USE_SMOOTH_LOD_TRANSITION 1 // Switches between using 0, 4 or 8 heightmap layers (values: 0, 1, 2) #define TERRAIN_LAYERS_DATA_SIZE 2 #define USE_TERRAIN_LAYERS (TERRAIN_LAYERS_DATA_SIZE > 0) #include "./Flax/Common.hlsl" #include "./Flax/MaterialCommon.hlsl" #include "./Flax/GBufferCommon.hlsl" @7 // Primary constant buffer (with additional material parameters) META_CB_BEGIN(0, Data) float4x4 ViewProjectionMatrix; float4x4 WorldMatrix; float4x4 ViewMatrix; float3 ViewPos; float ViewFar; float3 ViewDir; float TimeParam; float4 ViewInfo; float4 ScreenSize; float4 LightmapArea; float3 WorldInvScale; float WorldDeterminantSign; float PerInstanceRandom; float CurrentLOD; float ChunkSizeNextLOD; float TerrainChunkSizeLOD0; float4 HeightmapUVScaleBias; float4 NeighborLOD; 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); Texture2D Splatmap1 : register(t5); // Material shader resources @2 // Interpolants passed from the vertex shader struct VertexOutput { float4 Position : SV_Position; float3 WorldPosition : TEXCOORD0; float2 TexCoord : TEXCOORD1; float2 LightmapUV : TEXCOORD2; float3 WorldNormal : TEXCOORD3; float HolesMask : TEXCOORD4; #if USE_TERRAIN_LAYERS float4 Layers[TERRAIN_LAYERS_DATA_SIZE] : TEXCOORD5; #endif #if USE_CUSTOM_VERTEX_INTERPOLATORS float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; #endif #if USE_TESSELLATION float TessellationMultiplier : TESS; #endif }; // Interpolants passed to the pixel shader struct PixelInput { float4 Position : SV_Position; float3 WorldPosition : TEXCOORD0; float2 TexCoord : TEXCOORD1; float2 LightmapUV : TEXCOORD2; float3 WorldNormal : TEXCOORD3; float HolesMask : TEXCOORD4; #if USE_TERRAIN_LAYERS float4 Layers[TERRAIN_LAYERS_DATA_SIZE] : TEXCOORD5; #endif #if USE_CUSTOM_VERTEX_INTERPOLATORS float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; #endif bool IsFrontFace : SV_IsFrontFace; }; // Material properties generation input struct MaterialInput { float3 WorldPosition; float TwoSidedSign; float2 TexCoord; #if USE_LIGHTMAP float2 LightmapUV; #endif float3x3 TBN; float4 SvPosition; float3 PreSkinnedPosition; float3 PreSkinnedNormal; float HolesMask; #if USE_TERRAIN_LAYERS float4 Layers[TERRAIN_LAYERS_DATA_SIZE]; #endif #if USE_CUSTOM_VERTEX_INTERPOLATORS float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT]; #endif }; MaterialInput GetMaterialInput(PixelInput input) { MaterialInput result = (MaterialInput)0; result.WorldPosition = input.WorldPosition; result.TexCoord = input.TexCoord; #if USE_LIGHTMAP result.LightmapUV = input.LightmapUV; #endif result.TBN = CalcTangentBasisFromWorldNormal(input.WorldNormal); result.TwoSidedSign = WorldDeterminantSign * (input.IsFrontFace ? 1.0 : -1.0); result.SvPosition = input.Position; result.HolesMask = input.HolesMask; #if USE_TERRAIN_LAYERS result.Layers = input.Layers; #endif #if USE_CUSTOM_VERTEX_INTERPOLATORS result.CustomVSToPS = input.CustomVSToPS; #endif return result; } // Removes the scale vector from the local to world transformation matrix float3x3 RemoveScaleFromLocalToWorld(float3x3 localToWorld) { localToWorld[0] *= WorldInvScale.x; localToWorld[1] *= WorldInvScale.y; localToWorld[2] *= WorldInvScale.z; return localToWorld; } // Transforms a vector from tangent space to world space float3 TransformTangentVectorToWorld(MaterialInput input, float3 tangentVector) { return mul(tangentVector, input.TBN); } // Transforms a vector from world space to tangent space float3 TransformWorldVectorToTangent(MaterialInput input, float3 worldVector) { return mul(input.TBN, worldVector); } // Transforms a vector from world space to view space float3 TransformWorldVectorToView(MaterialInput input, float3 worldVector) { return mul(worldVector, (float3x3)ViewMatrix); } // Transforms a vector from view space to world space float3 TransformViewVectorToWorld(MaterialInput input, float3 viewVector) { return mul((float3x3)ViewMatrix, viewVector); } // Transforms a vector from local space to world space float3 TransformLocalVectorToWorld(MaterialInput input, float3 localVector) { float3x3 localToWorld = (float3x3)WorldMatrix; //localToWorld = RemoveScaleFromLocalToWorld(localToWorld); return mul(localVector, localToWorld); } // Transforms a vector from local space to world space float3 TransformWorldVectorToLocal(MaterialInput input, float3 worldVector) { float3x3 localToWorld = (float3x3)WorldMatrix; //localToWorld = RemoveScaleFromLocalToWorld(localToWorld); return mul(localToWorld, worldVector); } // Gets the current object position float3 GetObjectPosition(MaterialInput input) { return WorldMatrix[3].xyz; } // Gets the current object size float3 GetObjectSize(MaterialInput input) { return float3(1, 1, 1); } // Get the current object random value float GetPerInstanceRandom(MaterialInput input) { return PerInstanceRandom; } // Get the current object LOD transition dither factor float GetLODDitherFactor(MaterialInput input) { return 0; } // Gets the interpolated vertex color (in linear space) float4 GetVertexColor(MaterialInput input) { return 1; } // Get material properties function (for vertex shader) Material GetMaterialVS(MaterialInput input) { @5 } // Get material properties function (for domain shader) Material GetMaterialDS(MaterialInput input) { @6 } // Get material properties function (for pixel shader) Material GetMaterialPS(MaterialInput input) { @4 } // Programmatically set the line number after all the material inputs which have a variable number of line endings // This allows shader error line numbers after this point to be the same regardless of which material is being compiled #line 1000 // Calculates LOD value (with fractional part for blending) float CalcLOD(float2 xy, float4 morph) { #if USE_SMOOTH_LOD_TRANSITION // Use LOD value based on Barycentric coordinates to morph to the lower LOD near chunk edges float4 lodCalculated = morph * CurrentLOD + NeighborLOD * (float4(1, 1, 1, 1) - morph); // Pick a quadrant (top, left, right or bottom) float lod; if ((xy.x + xy.y) > 1) { if (xy.x < xy.y) { lod = lodCalculated.w; } else { lod = lodCalculated.z; } } else { if (xy.x < xy.y) { lod = lodCalculated.y; } else { lod = lodCalculated.x; } } return lod; #else return CurrentLOD; #endif } float3x3 CalcTangentToWorld(float4x4 world, float3x3 tangentToLocal) { float3x3 localToWorld = RemoveScaleFromLocalToWorld((float3x3)world); return mul(tangentToLocal, localToWorld); } // Must match structure defined in TerrainManager.cpp struct TerrainVertexInput { float2 TexCoord : TEXCOORD0; float4 Morph : TEXCOORD1; }; // Vertex Shader function for terrain rendering META_VS(true, FEATURE_LEVEL_ES2) META_VS_IN_ELEMENT(TEXCOORD, 0, R32G32_FLOAT, 0, ALIGN, PER_VERTEX, 0, true) META_VS_IN_ELEMENT(TEXCOORD, 1, R8G8B8A8_UNORM, 0, ALIGN, PER_VERTEX, 0, true) VertexOutput VS(TerrainVertexInput input) { VertexOutput output; // Calculate terrain LOD for this chunk float lodCalculated = CalcLOD(input.TexCoord, input.Morph); float lodValue = CurrentLOD; float morphAlpha = lodCalculated - CurrentLOD; // Sample heightmap float2 heightmapUVs = input.TexCoord * HeightmapUVScaleBias.xy + HeightmapUVScaleBias.zw; #if USE_SMOOTH_LOD_TRANSITION float4 heightmapValueThisLOD = Heightmap.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue); float2 nextLODPos = round(input.TexCoord * ChunkSizeNextLOD) / ChunkSizeNextLOD; float2 heightmapUVsNextLOD = nextLODPos * HeightmapUVScaleBias.xy + HeightmapUVScaleBias.zw; float4 heightmapValueNextLOD = Heightmap.SampleLevel(SamplerPointClamp, heightmapUVsNextLOD, lodValue + 1); float4 heightmapValue = lerp(heightmapValueThisLOD, heightmapValueNextLOD, morphAlpha); bool isHole = max(heightmapValueThisLOD.b + heightmapValueThisLOD.a, heightmapValueNextLOD.b + heightmapValueNextLOD.a) >= 1.9f; #if USE_TERRAIN_LAYERS float4 splatmapValueThisLOD = Splatmap0.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue); float4 splatmapValueNextLOD = Splatmap0.SampleLevel(SamplerPointClamp, heightmapUVsNextLOD, lodValue + 1); float4 splatmap0Value = lerp(splatmapValueThisLOD, splatmapValueNextLOD, morphAlpha); #if TERRAIN_LAYERS_DATA_SIZE > 1 splatmapValueThisLOD = Splatmap1.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue); splatmapValueNextLOD = Splatmap1.SampleLevel(SamplerPointClamp, heightmapUVsNextLOD, lodValue + 1); float4 splatmap1Value = lerp(splatmapValueThisLOD, splatmapValueNextLOD, morphAlpha); #endif #endif #else float4 heightmapValue = Heightmap.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue); bool isHole = (heightmapValue.b + heightmapValue.a) >= 1.9f; #if USE_TERRAIN_LAYERS float4 splatmap0Value = Splatmap0.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue); #if TERRAIN_LAYERS_DATA_SIZE > 1 float4 splatmap1Value = Splatmap1.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue); #endif #endif #endif float height = (float)((int)(heightmapValue.x * 255.0) + ((int)(heightmapValue.y * 255) << 8)) / 65535.0; // Extract normal and the holes mask float2 normalTemp = float2(heightmapValue.b, heightmapValue.a) * 2.0f - 1.0f; float3 normal = float3(normalTemp.x, sqrt(1.0 - saturate(dot(normalTemp, normalTemp))), normalTemp.y); normal = normalize(normal); output.HolesMask = isHole ? 0 : 1; if (isHole) { normal = float3(0, 1, 0); } // Construct vertex position #if USE_SMOOTH_LOD_TRANSITION float2 positionXZThisLOD = input.TexCoord * TerrainChunkSizeLOD0; float2 positionXZNextLOD = nextLODPos * TerrainChunkSizeLOD0; float2 positionXZ = lerp(positionXZThisLOD, positionXZNextLOD, morphAlpha); #else float2 positionXZ = input.TexCoord * TerrainChunkSizeLOD0; #endif float3 position = float3(positionXZ.x, height, positionXZ.y); // Compute world space vertex position output.WorldPosition = mul(float4(position, 1), WorldMatrix).xyz; // Compute clip space position output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix); // Pass vertex attributes #if USE_SMOOTH_LOD_TRANSITION float2 texCoord = lerp(input.TexCoord, nextLODPos, morphAlpha); #else float2 texCoord = input.TexCoord; #endif output.TexCoord = positionXZ * (1.0f / TerrainChunkSizeLOD0) + OffsetUV; output.LightmapUV = texCoord * LightmapArea.zw + LightmapArea.xy; // Extract terrain layers weights from the splatmap #if USE_TERRAIN_LAYERS output.Layers[0] = splatmap0Value; #if TERRAIN_LAYERS_DATA_SIZE > 1 output.Layers[1] = splatmap1Value; #endif #endif // Compute world space normal vector float3x3 tangentToLocal = CalcTangentBasisFromWorldNormal(normal); float3x3 tangentToWorld = CalcTangentToWorld(WorldMatrix, tangentToLocal); output.WorldNormal = tangentToWorld[2]; // Get material input params if need to evaluate any material property #if USE_POSITION_OFFSET || USE_TESSELLATION || USE_CUSTOM_VERTEX_INTERPOLATORS MaterialInput materialInput = (MaterialInput)0; materialInput.WorldPosition = output.WorldPosition; materialInput.TexCoord = output.TexCoord; #if USE_LIGHTMAP materialInput.LightmapUV = output.LightmapUV; #endif materialInput.TBN = CalcTangentBasisFromWorldNormal(output.WorldNormal); materialInput.TwoSidedSign = WorldDeterminantSign; materialInput.SvPosition = output.Position; materialInput.PreSkinnedPosition = position; materialInput.PreSkinnedNormal = normal; materialInput.HolesMask = output.HolesMask; #if USE_TERRAIN_LAYERS materialInput.Layers = output.Layers; #endif Material material = GetMaterialVS(materialInput); #endif // Apply world position offset per-vertex #if USE_POSITION_OFFSET output.WorldPosition += material.PositionOffset; output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix); #endif // Get tessalation multiplier (per vertex) #if USE_TESSELLATION output.TessellationMultiplier = material.TessellationMultiplier; #endif // Copy interpolants for other shader stages #if USE_CUSTOM_VERTEX_INTERPOLATORS output.CustomVSToPS = material.CustomVSToPS; #endif return output; } #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; float3 WorldNormal : TEXCOORD3; float HolesMask : TEXCOORD4; #if USE_TERRAIN_LAYERS float4 Layers[TERRAIN_LAYERS_DATA_SIZE] : TEXCOORD5; #endif #if USE_CUSTOM_VERTEX_INTERPOLATORS float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; #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; float3 WorldNormal : TEXCOORD3; float HolesMask : TEXCOORD4; #if USE_TERRAIN_LAYERS float4 Layers[TERRAIN_LAYERS_DATA_SIZE] : TEXCOORD5; #endif #if USE_CUSTOM_VERTEX_INTERPOLATORS float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; #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 result.TBN = CalcTangentBasisFromWorldNormal(input.WorldNormal); result.TwoSidedSign = WorldDeterminantSign; result.SvPosition = input.Position; result.HolesMask = input.HolesMask; #if USE_TERRAIN_LAYERS result.Layers = input.Layers; #endif #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_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); COPY(WorldNormal); COPY(HolesMask); COPY(TessellationMultiplier); #if USE_TERRAIN_LAYERS COPY(Layers); #endif #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 // Precompute squares and squares * 3 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); INTERPOLATE(WorldNormal); INTERPOLATE(HolesMask); #if USE_TERRAIN_LAYERS UNROLL for (int i = 0; i < TERRAIN_LAYERS_DATA_SIZE; i++) { INTERPOLATE(Layers[i]); } #endif #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.WorldNormal = normalize(output.WorldNormal); #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; #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_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 // Pixel Shader function for GBuffer Pass META_PS(true, FEATURE_LEVEL_ES2) META_PERMUTATION_1(USE_LIGHTMAP=0) META_PERMUTATION_1(USE_LIGHTMAP=1) void PS_GBuffer( in PixelInput input ,out float4 Light : SV_Target0 #if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE ,out float4 RT0 : SV_Target1 ,out float4 RT1 : SV_Target2 ,out float4 RT2 : SV_Target3 #if USE_GBUFFER_CUSTOM_DATA ,out float4 RT3 : SV_Target4 #endif #endif ) { Light = 0; // Get material parameters MaterialInput materialInput = GetMaterialInput(input); Material material = GetMaterialPS(materialInput); // Masking #if MATERIAL_MASKED clip(material.Mask - MATERIAL_MASK_THRESHOLD); #endif #if USE_LIGHTMAP float3 diffuseColor = GetDiffuseColor(material.Color, material.Metalness); float3 specularColor = GetSpecularColor(material.Color, material.Specular, material.Metalness); // Sample lightmap float3 diffuseIndirectLighting = SampleLightmap(material, materialInput); // Apply static indirect light Light.rgb = diffuseColor * diffuseIndirectLighting * AOMultiBounce(material.AO, diffuseColor); #endif #if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE // Pack material properties to GBuffer RT0 = float4(material.Color, material.AO); RT1 = float4(material.WorldNormal * 0.5 + 0.5, MATERIAL_SHADING_MODEL * (1.0 / 3.0)); RT2 = float4(material.Roughness, material.Metalness, material.Specular, 0); // Custom data #if USE_GBUFFER_CUSTOM_DATA #if MATERIAL_SHADING_MODEL == SHADING_MODEL_SUBSURFACE RT3 = float4(material.SubsurfaceColor, material.Opacity); #elif MATERIAL_SHADING_MODEL == SHADING_MODEL_FOLIAGE RT3 = float4(material.SubsurfaceColor, material.Opacity); #else RT3 = float4(0, 0, 0, 0); #endif #endif // Add light emission #if USE_EMISSIVE Light.rgb += material.Emissive; #endif #else // Handle blending as faked forward pass (use Light buffer and skip GBuffer modification) Light = float4(material.Emissive, material.Opacity); #endif } // Pixel Shader function for Depth Pass META_PS(true, FEATURE_LEVEL_ES2) void PS_Depth(PixelInput input #if GLSL , out float4 OutColor : SV_Target0 #endif ) { #if MATERIAL_MASKED // Perform per pixel clipping if material requries it MaterialInput materialInput = GetMaterialInput(input); Material material = GetMaterialPS(materialInput); clip(material.Mask - MATERIAL_MASK_THRESHOLD); #endif #if GLSL OutColor = 0; #endif }