// File generated by Flax Materials Editor // Version: @0 #define MATERIAL 1 #define MAX_LOCAL_LIGHTS 4 @3 #include "./Flax/Common.hlsl" #include "./Flax/MaterialCommon.hlsl" #include "./Flax/GBufferCommon.hlsl" #include "./Flax/LightingCommon.hlsl" #if USE_REFLECTIONS #include "./Flax/ReflectionsCommon.hlsl" #endif #include "./Flax/Lighting.hlsl" #include "./Flax/ShadowsSampling.hlsl" #include "./Flax/ExponentialHeightFog.hlsl" #include "./Flax/Matrix.hlsl" @7 struct SpriteInput { float2 Position : POSITION; float2 TexCoord : TEXCOORD; }; // 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; uint SortedIndicesOffset; float PerInstanceRandom; int ParticleStride; int PositionOffset; int SpriteSizeOffset; int SpriteFacingModeOffset; int SpriteFacingVectorOffset; int VelocityOffset; int RotationOffset; int ScaleOffset; int ModelFacingModeOffset; float RibbonUVTilingDistance; float2 RibbonUVScale; float2 RibbonUVOffset; int RibbonWidthOffset; int RibbonTwistOffset; int RibbonFacingVectorOffset; uint RibbonSegmentCount; float4x4 WorldMatrixInverseTransposed; @1META_CB_END // Secondary constantant buffer (for lighting) META_CB_BEGIN(1, LightingData) LightData DirectionalLight; LightShadowData DirectionalLightShadow; LightData SkyLight; ProbeData EnvironmentProbe; ExponentialHeightFogData ExponentialHeightFog; float3 Dummy1; uint LocalLightsCount; LightData LocalLights[MAX_LOCAL_LIGHTS]; META_CB_END DECLARE_LIGHTSHADOWDATA_ACCESS(DirectionalLightShadow); // Particles attributes buffer ByteAddressBuffer ParticlesData : register(t0); // Ribbons don't use sorted indices so overlap the segment distances buffer on the slot #define HAS_SORTED_INDICES (!defined(_VS_Ribbon)) #if HAS_SORTED_INDICES // Sorted particles indices Buffer SortedIndices : register(t1); #else // Ribbon particles segments distances buffer Buffer SegmentDistances : register(t1); #endif // Shader resources TextureCube EnvProbe : register(t2); TextureCube SkyLightTexture : register(t3); Texture2DArray DirectionalLightShadowMap : register(t4); @2 // Interpolants passed from the vertex shader struct VertexOutput { float4 Position : SV_Position; float3 WorldPosition : TEXCOORD0; float2 TexCoord : TEXCOORD1; uint ParticleIndex : 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; float InstanceParams : TEXCOORD7; // x-PerInstanceRandom }; // Interpolants passed to the pixel shader struct PixelInput { float4 Position : SV_Position; float3 WorldPosition : TEXCOORD0; float2 TexCoord : TEXCOORD1; uint ParticleIndex : 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; float InstanceParams : TEXCOORD7; // x-PerInstanceRandom bool IsFrontFace : SV_IsFrontFace; }; // Material properties generation input struct MaterialInput { float3 WorldPosition; float TwoSidedSign; float2 TexCoord; uint ParticleIndex; #if USE_VERTEX_COLOR half4 VertexColor; #endif float3x3 TBN; float4 SvPosition; float3 PreSkinnedPosition; float3 PreSkinnedNormal; float3 InstanceOrigin; float InstanceParams; #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; result.ParticleIndex = input.ParticleIndex; #if USE_VERTEX_COLOR result.VertexColor = input.VertexColor; #endif result.TBN = input.TBN; result.TwoSidedSign = input.IsFrontFace ? 1.0 : -1.0; result.InstanceOrigin = input.InstanceOrigin; result.InstanceParams = input.InstanceParams; result.SvPosition = input.Position; #if USE_CUSTOM_VERTEX_INTERPOLATORS result.CustomVSToPS = input.CustomVSToPS; #endif return result; } // Gets the local to world transform matrix (supports instancing) float4x4 GetInstanceTransform(ModelInput input) { return WorldMatrix; } float4x4 GetInstanceTransform(MaterialInput input) { return WorldMatrix; } // Removes the scale vector from the local to world transformation matrix (supports instancing) float3x3 RemoveScaleFromLocalToWorld(float3x3 localToWorld) { // Extract per axis scales from localToWorld transform float scaleX = length(localToWorld[0]); float scaleY = length(localToWorld[1]); float scaleZ = length(localToWorld[2]); float3 invScale = float3( scaleX > 0.00001f ? 1.0f / scaleX : 0.0f, scaleY > 0.00001f ? 1.0f / scaleY : 0.0f, scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f); localToWorld[0] *= invScale.x; localToWorld[1] *= invScale.y; localToWorld[2] *= invScale.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)GetInstanceTransform(input); //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)GetInstanceTransform(input); //localToWorld = RemoveScaleFromLocalToWorld(localToWorld); return mul(localToWorld, worldVector); } // Gets the current object position (supports instancing) float3 GetObjectPosition(MaterialInput input) { return input.InstanceOrigin.xyz; } // Gets the current object size float3 GetObjectSize(MaterialInput input) { return float3(1, 1, 1); } // Get the current object random value (supports instancing) float GetPerInstanceRandom(MaterialInput input) { return input.InstanceParams; } // Get the current object LOD transition dither factor (supports instancing) float GetLODDitherFactor(MaterialInput input) { return 0; } // Gets the interpolated vertex color (in linear space) float4 GetVertexColor(MaterialInput input) { #if USE_VERTEX_COLOR return input.VertexColor; #else return 1; #endif } uint GetParticleUint(uint particleIndex, int offset) { return ParticlesData.Load(particleIndex * ParticleStride + offset); } int GetParticleInt(uint particleIndex, int offset) { return asint(ParticlesData.Load(particleIndex * ParticleStride + offset)); } float GetParticleFloat(uint particleIndex, int offset) { return asfloat(ParticlesData.Load(particleIndex * ParticleStride + offset)); } float2 GetParticleVec2(uint particleIndex, int offset) { return asfloat(ParticlesData.Load2(particleIndex * ParticleStride + offset)); } float3 GetParticleVec3(uint particleIndex, int offset) { return asfloat(ParticlesData.Load3(particleIndex * ParticleStride + offset)); } float4 GetParticleVec4(uint particleIndex, int offset) { return asfloat(ParticlesData.Load4(particleIndex * ParticleStride + offset)); } float3 TransformParticlePosition(float3 input) { return mul(float4(input, 1.0f), WorldMatrix).xyz; } float3 TransformParticleVector(float3 input) { return mul(float4(input, 0.0f), WorldMatrixInverseTransposed).xyz; } // 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 the transform matrix from mesh tangent space to local space half3x3 CalcTangentToLocal(ModelInput input) { float bitangentSign = input.Tangent.w ? -1.0f : +1.0f; float3 normal = input.Normal.xyz * 2.0 - 1.0; float3 tangent = input.Tangent.xyz * 2.0 - 1.0; float3 bitangent = cross(normal, tangent) * bitangentSign; return float3x3(tangent, bitangent, normal); } half3x3 CalcTangentToWorld(in float4x4 world, in half3x3 tangentToLocal) { half3x3 localToWorld = RemoveScaleFromLocalToWorld((float3x3)world); return mul(tangentToLocal, localToWorld); } float3 GetParticlePosition(uint ParticleIndex) { return TransformParticlePosition(GetParticleVec3(ParticleIndex, PositionOffset)); } // Vertex Shader function for Sprite Rendering META_VS(true, FEATURE_LEVEL_ES2) META_VS_IN_ELEMENT(POSITION, 0, R32G32_FLOAT, 0, 0, PER_VERTEX, 0, true) META_VS_IN_ELEMENT(TEXCOORD, 0, R32G32_FLOAT, 0, ALIGN, PER_VERTEX, 0, true) VertexOutput VS_Sprite(SpriteInput input, uint particleIndex : SV_InstanceID) { VertexOutput output; #if HAS_SORTED_INDICES // Sorted particles mapping if (SortedIndicesOffset != 0xFFFFFFFF) { particleIndex = SortedIndices[SortedIndicesOffset + particleIndex]; } #endif // Read particle data float3 particlePosition = GetParticleVec3(particleIndex, PositionOffset); float3 particleRotation = GetParticleVec3(particleIndex, RotationOffset); float2 spriteSize = GetParticleVec2(particleIndex, SpriteSizeOffset); int spriteFacingMode = SpriteFacingModeOffset != -1 ? GetParticleInt(particleIndex, SpriteFacingModeOffset) : -1; float4x4 world = WorldMatrix; float3x3 eulerMatrix = EulerMatrix(radians(particleRotation)); float3x3 viewRot = transpose((float3x3)ViewMatrix); float3 position = mul(float4(particlePosition, 1), world).xyz; // Orient sprite float3 axisX, axisY, axisZ; if (spriteFacingMode == 0) { // Face Camera Position axisZ = normalize(ViewPos - position); axisX = -normalize(cross(viewRot[1].xyz, axisZ)); axisY = cross(axisZ, axisX); } else if (spriteFacingMode == 1) { // Face Camera Plane axisX = viewRot[0].xyz; axisY = -viewRot[1].xyz; axisZ = viewRot[2].xyz; } else if (spriteFacingMode == 2) { // Along Velocity float3 velocity = GetParticleVec3(particleIndex, VelocityOffset); axisY = normalize(velocity); axisZ = ViewPos - position; axisX = normalize(cross(axisY, axisZ)); axisZ = cross(axisX, axisY); } else if (spriteFacingMode == 3) { // Custom Facing Vector float3 spriteFacingVector = GetParticleVec3(particleIndex, SpriteFacingVectorOffset); axisZ = spriteFacingVector; axisX = normalize(cross(viewRot[1].xyz, axisZ)); axisY = cross(axisZ, axisX); } else if (spriteFacingMode == 4) { // Fixed Axis float3 spriteFacingVector = GetParticleVec3(particleIndex, SpriteFacingVectorOffset); axisY = spriteFacingVector; axisZ = ViewPos - position; axisX = normalize(cross(axisY, axisZ)); axisZ = cross(axisX, axisY); } else { // Default axisX = float3(1, 0, 0); axisY = float3(0, 1, 0); axisZ = float3(0, 0, 1); } // Compute world space vertex position float3 spriteVertexPosition = float3(input.Position.xy * spriteSize, 0); spriteVertexPosition = mul(spriteVertexPosition, eulerMatrix); spriteVertexPosition = mul(spriteVertexPosition, float3x3(axisX, axisY, axisZ)); output.WorldPosition = position + spriteVertexPosition; // Compute clip space position output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix); // Pass vertex attributes output.TexCoord = input.TexCoord; output.ParticleIndex = particleIndex; #if USE_VERTEX_COLOR output.VertexColor = 1; #endif output.InstanceOrigin = world[3].xyz; output.InstanceParams = PerInstanceRandom; // Calculate tanget space to world space transformation matrix for unit vectors half3x3 tangentToLocal = float3x3(axisX, axisY, axisZ); half3x3 tangentToWorld = CalcTangentToWorld(world, tangentToLocal); output.TBN = tangentToWorld; // Get material input params if need to evaluate any material property #if USE_POSITION_OFFSET || USE_CUSTOM_VERTEX_INTERPOLATORS MaterialInput materialInput = (MaterialInput)0; materialInput.WorldPosition = output.WorldPosition; materialInput.TexCoord = output.TexCoord; materialInput.ParticleIndex = output.ParticleIndex; #if USE_VERTEX_COLOR materialInput.VertexColor = output.VertexColor; #endif materialInput.TBN = output.TBN; materialInput.TwoSidedSign = 1; materialInput.SvPosition = output.Position; materialInput.PreSkinnedPosition = float3(input.Position.xy, 0); materialInput.PreSkinnedNormal = tangentToLocal[2].xyz; materialInput.InstanceOrigin = output.InstanceOrigin; materialInput.InstanceParams = output.InstanceParams; 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 // Copy interpolants for other shader stages #if USE_CUSTOM_VERTEX_INTERPOLATORS output.CustomVSToPS = material.CustomVSToPS; #endif return output; } // Vertex Shader function for Model Rendering META_VS(true, FEATURE_LEVEL_ES2) META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, 0, PER_VERTEX, 0, true) META_VS_IN_ELEMENT(TEXCOORD, 0, R16G16_FLOAT, 1, 0, PER_VERTEX, 0, true) META_VS_IN_ELEMENT(NORMAL, 0, R10G10B10A2_UNORM, 1, ALIGN, PER_VERTEX, 0, true) META_VS_IN_ELEMENT(TANGENT, 0, R10G10B10A2_UNORM, 1, ALIGN, PER_VERTEX, 0, true) META_VS_IN_ELEMENT(TEXCOORD, 1, R16G16_FLOAT, 1, ALIGN, PER_VERTEX, 0, true) META_VS_IN_ELEMENT(COLOR, 0, R8G8B8A8_UNORM, 2, 0, PER_VERTEX, 0, USE_VERTEX_COLOR) VertexOutput VS_Model(ModelInput input, uint particleIndex : SV_InstanceID) { VertexOutput output; #if HAS_SORTED_INDICES // Sorted particles mapping if (SortedIndicesOffset != 0xFFFFFFFF) { particleIndex = SortedIndices[SortedIndicesOffset + particleIndex]; } #endif // Read particle data float3 particlePosition = GetParticleVec3(particleIndex, PositionOffset); float3 particleScale = GetParticleVec3(particleIndex, ScaleOffset); float3 particleRotation = GetParticleVec3(particleIndex, RotationOffset); int modelFacingMode = ModelFacingModeOffset != -1 ? GetParticleInt(particleIndex, ModelFacingModeOffset) : -1; float3 position = mul(float4(particlePosition, 1), WorldMatrix).xyz; // Compute final vertex position in the world float3x3 eulerMatrix = EulerMatrix(radians(particleRotation)); float4 transform0 = float4(eulerMatrix[0], particlePosition.x); float4 transform1 = float4(eulerMatrix[1], particlePosition.y); float4 transform2 = float4(eulerMatrix[2], particlePosition.z); float4x4 scaleMatrix = float4x4(float4(particleScale.x, 0.0f, 0.0f, 0.0f), float4(0.0f, particleScale.y, 0.0f, 0.0f), float4(0.0f, 0.0f, particleScale.z, 0.0f), float4(0.0f, 0.0f, 0.0f, 1.0f)); float4x4 world = float4x4(transform0, transform1, transform2, float4(0.0f, 0.0f, 0.0f, 1.0f)); if (modelFacingMode == 0) { // Face Camera Position float3 direction = normalize(ViewPos - position); float4 alignmentQuat = FindQuatBetween(float3(1.0f, 0.0f, 0.0f), direction); float4x4 alignmentMat = QuaternionToMatrix(alignmentQuat); world = mul(world, mul(alignmentMat, scaleMatrix)); } else if (modelFacingMode == 1) { // Face Camera Plane float3 direction = -ViewDir; float4 alignmentQuat = FindQuatBetween(float3(1.0f, 0.0f, 0.0f), direction); float4x4 alignmentMat = QuaternionToMatrix(alignmentQuat); world = mul(world, mul(alignmentMat, scaleMatrix)); } else if (modelFacingMode == 2) { // Along Velocity float3 direction = GetParticleVec3(particleIndex, VelocityOffset); float4 alignmentQuat = FindQuatBetween(float3(1.0f, 0.0f, 0.0f), normalize(direction)); float4x4 alignmentMat = QuaternionToMatrix(alignmentQuat); world = mul(world, mul(alignmentMat, scaleMatrix)); } else { // Default world = mul(world, scaleMatrix); } world = transpose(world); world = mul(world, WorldMatrix); // Calculate the vertex position in world space output.WorldPosition = mul(float4(input.Position, 1), world).xyz; // Compute clip space position output.Position = mul(float4(output.WorldPosition, 1), ViewProjectionMatrix); // Pass vertex attributes output.TexCoord = input.TexCoord; output.ParticleIndex = particleIndex; #if USE_VERTEX_COLOR output.VertexColor = input.Color; #endif output.InstanceOrigin = WorldMatrix[3].xyz; output.InstanceParams = PerInstanceRandom; // Calculate tanget space to world space transformation matrix for unit vectors half3x3 tangentToLocal = CalcTangentToLocal(input); half3x3 tangentToWorld = CalcTangentToWorld(WorldMatrix, tangentToLocal); output.TBN = tangentToWorld; // Get material input params if need to evaluate any material property #if USE_POSITION_OFFSET || USE_CUSTOM_VERTEX_INTERPOLATORS MaterialInput materialInput = (MaterialInput)0; materialInput.WorldPosition = output.WorldPosition; materialInput.TexCoord = output.TexCoord; materialInput.ParticleIndex = output.ParticleIndex; #if USE_VERTEX_COLOR materialInput.VertexColor = output.VertexColor; #endif materialInput.TBN = output.TBN; materialInput.TwoSidedSign = 1; materialInput.SvPosition = output.Position; materialInput.PreSkinnedPosition = input.Position.xyz; materialInput.PreSkinnedNormal = tangentToLocal[2].xyz; materialInput.InstanceOrigin = output.InstanceOrigin; materialInput.InstanceParams = output.InstanceParams; 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 // Copy interpolants for other shader stages #if USE_CUSTOM_VERTEX_INTERPOLATORS output.CustomVSToPS = material.CustomVSToPS; #endif return output; } // Vertex Shader function for Ribbon Rendering META_VS(true, FEATURE_LEVEL_ES2) VertexOutput VS_Ribbon(uint vertexIndex : SV_VertexID) { VertexOutput output; // Get particle data uint particleIndex = vertexIndex / 2; int vertexSign = (((int)vertexIndex & 0x1) * 2) - 1; float3 position = GetParticlePosition(particleIndex); float ribbonWidth = RibbonWidthOffset != -1 ? GetParticleFloat(particleIndex, RibbonWidthOffset) : 20.0f; float ribbonTwist = RibbonTwistOffset != -1 ? GetParticleFloat(particleIndex, RibbonTwistOffset) : 0.0f; // Calculate ribbon direction float3 direction; if (particleIndex == 0) { float3 nextParticlePos = GetParticlePosition(particleIndex + 1); direction = nextParticlePos - position; } else { float3 previousParticlePos = GetParticlePosition(particleIndex - 1); direction = position - previousParticlePos; } // Calculate particle orientation (tangent vectors) float3 cameraDirection = SafeNormalize(ViewPos - position); float3 tangentUp = SafeNormalize(direction); float3 facing = RibbonFacingVectorOffset != -1 ? GetParticleVec3(particleIndex, RibbonFacingVectorOffset) : cameraDirection; float twistSine, twistCosine; sincos(radians(ribbonTwist), twistSine, twistCosine); facing = facing * twistCosine + cross(facing, tangentUp) * twistSine + tangentUp * dot(tangentUp, facing) * (1 - twistCosine); float3 tangentRight = SafeNormalize(cross(tangentUp, facing)); if (!any(tangentRight)) { tangentRight = SafeNormalize(cross(tangentUp, float3(0.0f, 0.0f, 1.0f))); } // Calculate texture coordinates float texCoordU; #ifdef _VS_Ribbon if (RibbonUVTilingDistance != 0.0f) { texCoordU = SegmentDistances[particleIndex] / RibbonUVTilingDistance; } else #endif { texCoordU = (float)particleIndex / RibbonSegmentCount; } float texCoordV = (vertexIndex + 1) & 0x1; output.TexCoord = float2(texCoordU, texCoordV) * RibbonUVScale + RibbonUVOffset; // Compute world space vertex position output.WorldPosition = position + tangentRight * vertexSign * (ribbonWidth.xxx * 0.5f); // Compute clip space position output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix); // Pass vertex attributes output.ParticleIndex = particleIndex; #if USE_VERTEX_COLOR output.VertexColor = 1; #endif output.InstanceOrigin = WorldMatrix[3].xyz; output.InstanceParams = PerInstanceRandom; // Calculate tanget space to world space transformation matrix for unit vectors half3x3 tangentToLocal = float3x3(tangentRight, tangentUp, cross(tangentRight, tangentUp)); half3x3 tangentToWorld = CalcTangentToWorld(WorldMatrix, tangentToLocal); output.TBN = tangentToWorld; // Get material input params if need to evaluate any material property #if USE_POSITION_OFFSET || USE_CUSTOM_VERTEX_INTERPOLATORS MaterialInput materialInput = (MaterialInput)0; materialInput.WorldPosition = output.WorldPosition; materialInput.TexCoord = output.TexCoord; materialInput.ParticleIndex = output.ParticleIndex; #if USE_VERTEX_COLOR materialInput.VertexColor = output.VertexColor; #endif materialInput.TBN = output.TBN; materialInput.TwoSidedSign = 1; materialInput.SvPosition = output.Position; materialInput.PreSkinnedPosition = Position; materialInput.PreSkinnedNormal = tangentToLocal[2].xyz; materialInput.InstanceOrigin = output.InstanceOrigin; materialInput.InstanceParams = output.InstanceParams; 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 // Copy interpolants for other shader stages #if USE_CUSTOM_VERTEX_INTERPOLATORS output.CustomVSToPS = material.CustomVSToPS; #endif return output; } // Pixel Shader function for Forward Pass META_PS(USE_FORWARD, FEATURE_LEVEL_ES2) float4 PS_Forward(PixelInput input) : SV_Target0 { float4 output = 0; // Get material parameters MaterialInput materialInput = GetMaterialInput(input); Material material = GetMaterialPS(materialInput); // Masking #if MATERIAL_MASKED clip(material.Mask - MATERIAL_MASK_THRESHOLD); #endif // Add emissive light output = float4(material.Emissive, material.Opacity); #if MATERIAL_SHADING_MODEL != SHADING_MODEL_UNLIT // Setup GBuffer data as proxy for lighting GBufferSample gBuffer; gBuffer.Normal = material.WorldNormal; gBuffer.Roughness = material.Roughness; gBuffer.Metalness = material.Metalness; gBuffer.Color = material.Color; gBuffer.Specular = material.Specular; gBuffer.AO = material.AO; gBuffer.ViewPos = mul(float4(materialInput.WorldPosition, 1), ViewMatrix).xyz; #if MATERIAL_SHADING_MODEL == SHADING_MODEL_SUBSURFACE gBuffer.CustomData = float4(material.SubsurfaceColor, material.Opacity); #elif MATERIAL_SHADING_MODEL == SHADING_MODEL_FOLIAGE gBuffer.CustomData = float4(material.SubsurfaceColor, material.Opacity); #else gBuffer.CustomData = float4(0, 0, 0, 0); #endif gBuffer.WorldPos = materialInput.WorldPosition; gBuffer.ShadingModel = MATERIAL_SHADING_MODEL; // Calculate lighting from a single directional light float4 shadowMask = 1.0f; if (DirectionalLight.CastShadows > 0) { LightShadowData directionalLightShadowData = GetDirectionalLightShadowData(); shadowMask.r = SampleShadow(DirectionalLight, directionalLightShadowData, DirectionalLightShadowMap, gBuffer, shadowMask.g); } float4 light = GetLighting(ViewPos, DirectionalLight, gBuffer, shadowMask, false, false); // Calculate lighting from sky light light += GetSkyLightLighting(SkyLight, gBuffer, SkyLightTexture); // Calculate lighting from local lights LOOP for (uint localLightIndex = 0; localLightIndex < LocalLightsCount; localLightIndex++) { const LightData localLight = LocalLights[localLightIndex]; bool isSpotLight = localLight.SpotAngles.x > -2.0f; shadowMask = 1.0f; light += GetLighting(ViewPos, localLight, gBuffer, shadowMask, true, isSpotLight); } #if USE_REFLECTIONS // Calculate reflections light.rgb += GetEnvProbeLighting(ViewPos, EnvProbe, EnvironmentProbe, gBuffer) * light.a; #endif // Add lighting (apply ambient occlusion) output.rgb += light.rgb * gBuffer.AO; #if USE_FOG // Calculate exponential height fog float4 fog = GetExponentialHeightFog(ExponentialHeightFog, materialInput.WorldPosition, ViewPos, 0); // Apply fog to the output color #if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE output = float4(output.rgb * fog.a + fog.rgb, output.a); #elif MATERIAL_BLEND == MATERIAL_BLEND_TRANSPARENT output = float4(output.rgb * fog.a + fog.rgb, output.a); #elif MATERIAL_BLEND == MATERIAL_BLEND_ADDITIVE output = float4(output.rgb * fog.a + fog.rgb, output.a * fog.a); #elif MATERIAL_BLEND == MATERIAL_BLEND_MULTIPLY output = float4(lerp(float3(1, 1, 1), output.rgb, fog.aaa * fog.aaa), output.a); #endif #endif #endif return output; } #if USE_DISTORTION // Pixel Shader function for Distortion Pass META_PS(USE_DISTORTION, FEATURE_LEVEL_ES2) float4 PS_Distortion(PixelInput input) : SV_Target0 { // Get material parameters MaterialInput materialInput = GetMaterialInput(input); Material material = GetMaterialPS(materialInput); // Masking #if MATERIAL_MASKED clip(material.Mask - MATERIAL_MASK_THRESHOLD); #endif float3 viewNormal = normalize(TransformWorldVectorToView(materialInput, material.WorldNormal)); float airIOR = 1.0f; #if USE_PIXEL_NORMAL_OFFSET_REFRACTION float3 viewVertexNormal = TransformWorldVectorToView(materialInput, TransformTangentVectorToWorld(materialInput, float3(0, 0, 1))); float2 distortion = (viewVertexNormal.xy - viewNormal.xy) * (material.Refraction - airIOR); #else float2 distortion = viewNormal.xy * (material.Refraction - airIOR); #endif // Clip if the distortion distance (squared) is too small to be noticed clip(dot(distortion, distortion) - 0.00001); // Scale up for better precision in low/subtle refractions at the expense of artefacts at higher refraction distortion *= 4.0f; // Store positive and negative offsets separately float2 addOffset = max(distortion, 0); float2 subOffset = abs(min(distortion, 0)); return float4(addOffset.x, addOffset.y, subOffset.x, subOffset.y); } #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 ) { // Get material parameters MaterialInput materialInput = GetMaterialInput(input); Material material = GetMaterialPS(materialInput); // Perform per pixel clipping #if MATERIAL_MASKED clip(material.Mask - MATERIAL_MASK_THRESHOLD); #endif #if MATERIAL_BLEND == MATERIAL_BLEND_TRANSPARENT clip(material.Opacity - MATERIAL_OPACITY_THRESHOLD); #endif #if GLSL OutColor = 0; #endif }