// File generated by Flax Materials Editor // Version: @0 #define MATERIAL 1 #define USE_PER_VIEW_CONSTANTS 1 #define LoadObjectFromCB(var) var = GetObject() @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" #include "./Flax/TerrainCommon.hlsl" @7 // Primary constant buffer (with additional material parameters) META_CB_BEGIN(0, Data) float4x3 WorldMatrix; float3 WorldInvScale; float WorldDeterminantSign; float PerInstanceRandom; float CurrentLOD; float ChunkSizeNextLOD; float TerrainChunkSizeLOD0; float4 HeightmapUVScaleBias; float4 NeighborLOD; float2 OffsetUV; float2 Dummy0; float4 LightmapArea; @1META_CB_END // Terrain data Texture2D Heightmap : register(t0); Texture2D Splatmap0 : register(t1); Texture2D Splatmap1 : register(t2); // Shader resources @2 // Geometry data passed though the graphics rendering stages up to the pixel shader struct GeometryData { 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 }; // Interpolants passed from the vertex shader struct VertexOutput { float4 Position : SV_Position; GeometryData Geometry; #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; GeometryData Geometry; #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; ObjectData Object; #if USE_TERRAIN_LAYERS float4 Layers[TERRAIN_LAYERS_DATA_SIZE]; #endif #if USE_CUSTOM_VERTEX_INTERPOLATORS float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT]; #endif }; // Extracts geometry data to the material input MaterialInput GetGeometryMaterialInput(GeometryData geometry) { MaterialInput output = (MaterialInput)0; output.WorldPosition = geometry.WorldPosition; output.TexCoord = geometry.TexCoord; #if USE_LIGHTMAP output.LightmapUV = geometry.LightmapUV; #endif output.TBN = CalcTangentBasis(geometry.WorldNormal, geometry.WorldPosition, geometry.TexCoord); output.HolesMask = geometry.HolesMask; #if USE_TERRAIN_LAYERS output.Layers = geometry.Layers; #endif return output; } #if USE_TESSELLATION // Interpolates the geometry positions data only (used by the tessallation when generating vertices) #define InterpolateGeometryPositions(output, p0, w0, p1, w1, p2, w2, offset) output.WorldPosition = p0.WorldPosition * w0 + p1.WorldPosition * w1 + p2.WorldPosition * w2 + offset // Offsets the geometry positions data only (used by the tessallation when generating vertices) #define OffsetGeometryPositions(geometry, offset) geometry.WorldPosition += offset // Applies the Phong tessallation to the geometry positions (used by the tessallation when doing Phong tess) #define ApplyGeometryPositionsPhongTess(geometry, p0, p1, p2, U, V, W) \ float3 posProjectedU = TessalationProjectOntoPlane(p0.WorldNormal, p0.WorldPosition, geometry.WorldPosition); \ float3 posProjectedV = TessalationProjectOntoPlane(p1.WorldNormal, p1.WorldPosition, geometry.WorldPosition); \ float3 posProjectedW = TessalationProjectOntoPlane(p2.WorldNormal, p2.WorldPosition, geometry.WorldPosition); \ geometry.WorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW // Interpolates the geometry data except positions (used by the tessallation when generating vertices) GeometryData InterpolateGeometry(GeometryData p0, float w0, GeometryData p1, float w1, GeometryData p2, float w2) { GeometryData output = (GeometryData)0; output.TexCoord = p0.TexCoord * w0 + p1.TexCoord * w1 + p2.TexCoord * w2; output.LightmapUV = p0.LightmapUV * w0 + p1.LightmapUV * w1 + p2.LightmapUV * w2; output.WorldNormal = p0.WorldNormal * w0 + p1.WorldNormal * w1 + p2.WorldNormal * w2; output.WorldNormal = normalize(output.WorldNormal); output.HolesMask = p0.HolesMask * w0 + p1.HolesMask * w1 + p2.HolesMask * w2; #if USE_TERRAIN_LAYERS UNROLL for (int i = 0; i < TERRAIN_LAYERS_DATA_SIZE; i++) output.Layers[i] = p0.Layers[i] * w0 + p1.Layers[i] * w1 + p2.Layers[i] * w2; #endif return output; } #endif ObjectData GetObject() { ObjectData object = (ObjectData)0; object.WorldMatrix = ToMatrix4x4(WorldMatrix); object.PrevWorldMatrix = object.WorldMatrix; object.GeometrySize = float3(1, 1, 1); object.PerInstanceRandom = PerInstanceRandom; object.WorldDeterminantSign = WorldDeterminantSign; object.LODDitherFactor = 0.0f; object.LightmapArea = LightmapArea; return object; } MaterialInput GetMaterialInput(PixelInput input) { MaterialInput output = GetGeometryMaterialInput(input.Geometry); output.Object = GetObject(); output.TwoSidedSign = WorldDeterminantSign * (input.IsFrontFace ? 1.0 : -1.0); output.SvPosition = input.Position; #if USE_CUSTOM_VERTEX_INTERPOLATORS output.CustomVSToPS = input.CustomVSToPS; #endif return output; } // 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)ToMatrix4x4(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)ToMatrix4x4(WorldMatrix); //localToWorld = RemoveScaleFromLocalToWorld(localToWorld); return mul(localToWorld, worldVector); } // Gets the current object position float3 GetObjectPosition(MaterialInput input) { return ToMatrix4x4(WorldMatrix)[3].xyz; } // Gets the current object size float3 GetObjectSize(MaterialInput input) { return float3(1, 1, 1); } // Gets the current object scale (supports instancing) float3 GetObjectScale(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; } @8 // 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 } // 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) 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 and splatmaps 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); #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); #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 = DecodeHeightmapHeight(heightmapValue); // Extract normal and the holes mask bool isHole; float3 normal = DecodeHeightmapNormal(heightmapValue, isHole); output.Geometry.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 float4x4 worldMatrix = ToMatrix4x4(WorldMatrix); output.Geometry.WorldPosition = mul(float4(position, 1), worldMatrix).xyz; // Compute clip space position output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix); // Pass vertex attributes #if USE_SMOOTH_LOD_TRANSITION float2 texCoord = lerp(input.TexCoord, nextLODPos, morphAlpha); #else float2 texCoord = input.TexCoord; #endif output.Geometry.TexCoord = positionXZ * (1.0f / TerrainChunkSizeLOD0) + OffsetUV; output.Geometry.LightmapUV = texCoord * LightmapArea.zw + LightmapArea.xy; // Extract terrain layers weights from the splatmap #if USE_TERRAIN_LAYERS output.Geometry.Layers[0] = splatmap0Value; #if TERRAIN_LAYERS_DATA_SIZE > 1 output.Geometry.Layers[1] = splatmap1Value; #endif #endif // Compute world space normal vector float3x3 tangentToLocal = CalcTangentBasisFromWorldNormal(normal); float3x3 tangentToWorld = CalcTangentToWorld(worldMatrix, tangentToLocal); output.Geometry.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.Object = GetObject(); materialInput.WorldPosition = output.Geometry.WorldPosition; materialInput.TexCoord = output.Geometry.TexCoord; #if USE_LIGHTMAP materialInput.LightmapUV = output.Geometry.LightmapUV; #endif materialInput.TBN = tangentToWorld; materialInput.TwoSidedSign = WorldDeterminantSign; materialInput.SvPosition = output.Position; materialInput.PreSkinnedPosition = position; materialInput.PreSkinnedNormal = normal; materialInput.HolesMask = output.Geometry.HolesMask; #if USE_TERRAIN_LAYERS materialInput.Layers = output.Geometry.Layers; #endif Material material = GetMaterialVS(materialInput); #endif // Apply world position offset per-vertex #if USE_POSITION_OFFSET output.Geometry.WorldPosition += material.PositionOffset; output.Position = mul(float4(output.Geometry.WorldPosition, 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; } // Pixel Shader function for Depth Pass META_PS(true, FEATURE_LEVEL_ES2) void PS_Depth(PixelInput input) { #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 _PS_QuadOverdraw #include "./Flax/Editor/QuadOverdraw.hlsl" // Pixel Shader function for Quad Overdraw Pass (editor-only) [earlydepthstencil] META_PS(USE_EDITOR, FEATURE_LEVEL_SM5) void PS_QuadOverdraw(float4 svPos : SV_Position, uint primId : SV_PrimitiveID) { DoQuadOverdraw(svPos, primId); } #endif @9