// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= ParticleSpriteVertexFactory.hlsl: Particle vertex factory shader code. Shared by standard sprite particles and SubUV sprite particles. =============================================================================*/ #include "VertexFactoryCommon.ush" #include "ParticleVertexFactoryCommon.ush" #define USE_PARTICLE_LIGHTING_OFFSET (FEATURE_LEVEL >= FEATURE_LEVEL_SM5 && !MATERIAL_SHADINGMODEL_UNLIT && MATERIAL_DOMAIN_SURFACE) #define USE_PARTICLE_POSITION (NEEDS_PARTICLE_POSITION || PASS_NEEDS_PRIMITIVE_VOLUME_BOUNDS) #define USE_PARTICLE_VELOCITY (NEEDS_PARTICLE_VELOCITY) #define USE_PARTICLE_TIME (NEEDS_PARTICLE_TIME) #define USE_PARTICLE_SIZE (NEEDS_PARTICLE_SIZE) #define USE_PARTICLE_SPRITE_ROTATION (NEEDS_PARTICLE_SPRITE_ROTATION) // We don't compute a tangent basis in ES3.1 to save shader instructions. #define NEEDS_TANGENT_BASIS (FEATURE_LEVEL >= FEATURE_LEVEL_SM4) struct FVertexFactoryInput { float4 Position : ATTRIBUTE0; float4 OldPosition : ATTRIBUTE1; float4 SizeRotSubImage : ATTRIBUTE2; float4 Color : ATTRIBUTE3; #if USE_DYNAMIC_PARAMETERS float4 DynamicParameter : ATTRIBUTE5; #endif //USE_DYNAMIC_PARAMETERS float2 TexCoord : ATTRIBUTE4; uint VertexId : SV_VertexID; VF_INSTANCED_STEREO_DECLARE_INPUT_BLOCK() VF_MOBILE_MULTI_VIEW_DECLARE_INPUT_BLOCK() }; struct FVertexFactoryInterpolantsVSToPS { #if NEEDS_TANGENT_BASIS // First row of the tangent to world matrix, Interp_Sizer used by SUBUV_PARTICLES in w float4 TangentToWorld0AndInterp_Sizer : TANGENTTOWORLD0; // Last row of the tangent to world matrix in xyz float4 TangentToWorld2 : TANGENTTOWORLD2; #else float SubImageLerp : TEXCOORD0; #endif #if USE_DYNAMIC_PARAMETERS nointerpolation float4 DynamicParameter : TEXCOORD1; #endif //USE_DYNAMIC_PARAMETERS #if NEEDS_PARTICLE_COLOR float4 Color : TEXCOORD2; #endif #if NUM_TEX_COORD_INTERPOLATORS float4 TexCoords[(NUM_TEX_COORD_INTERPOLATORS + 1) / 2] : TEXCOORD3; #endif //Not sure this is actually being used now and it's awkward to slot in now we're supporting custom UVs so I'm just giving this its own interpolant. #if LIGHTMAP_UV_ACCESS float2 LightMapUVs : LIGHTMAP_UVS; #endif #if USE_PARTICLE_SUBUVS float4 ParticleSubUVs : PARTICLE_SUBUVS; #endif #if USE_PARTICLE_POSITION /** Cam-relative (translated) particle center and radius */ float4 ParticleTranslatedWorldPositionAndSize : PARTICLE_POSITION; #endif #if USE_PARTICLE_VELOCITY float4 ParticleVelocity : PARTICLE_VELOCITY; #endif #if USE_PARTICLE_TIME float ParticleTime : PARTICLE_TIME; #endif #if USE_PARTICLE_LIGHTING_OFFSET float3 LightingPositionOffset : PARTICLE_LIGHTING_OFFSET; #endif #if USE_PARTICLE_SIZE float2 ParticleSize : PARTICLE_SIZE; #endif #if USE_PARTICLE_SPRITE_ROTATION float ParticleSpriteRotation : PARTICLE_SPRITE_ROTATION; #endif }; struct FVertexFactoryIntermediates { /** The position of the particle in translated world space. */ float3 ParticleTranslatedWorldPosition; /** The position of the vertex in translated world space. */ float3 VertexWorldPosition; /** Particle translated world space position and size. */ float4 TranslatedWorldPositionAndSize; #if USE_PARTICLE_LIGHTING_OFFSET float3 LightingPositionOffset; #endif /** The texture coordinate at this vertex. */ float4 TexCoord; #if NUM_MATERIAL_TEXCOORDS_VERTEX >= 3 //Unflipped UVs are placed in UVs 2-3 /** A second UV set. Always non-UV flipped. Allows use of UV flipping with Normal maps etc. */ float4 TexCoord_Unflipped; #endif /** The sprite tangent in world space (+V). */ float3 TangentUp; /** The sprite tangent in world space (+U). */ float3 TangentRight; /** The color of the sprite. */ float4 Color; /** The delta between previous and current particle locations. */ float3 ParticlePrevToCurrDelta; /** The velocity of the particle, XYZ: direction, W: speed. */ float4 ParticleVelocity; /** Dynamic parameter. */ float4 DynamicParameter; /** The sub-image lerp. */ float SubImageLerp; /** Relative time. */ float RelativeTime; /** Transform from tangent space to local space. */ float3x3 TangentToLocal; /** Size of the particle */ float2 ParticleSize; #if USE_PARTICLE_SPRITE_ROTATION float ParticleSpriteRotation : PARTICLE_SPRITE_ROTATION; #endif /** Cached primitive and instance data */ FSceneDataIntermediates SceneData; }; FPrimitiveSceneData GetPrimitiveData(FVertexFactoryIntermediates Intermediates) { return Intermediates.SceneData.Primitive; } #if NUM_TEX_COORD_INTERPOLATORS bool UVIndexUseZW(int UVIndex) { return (UVIndex % 2) != 0; } float2 GetUV(FVertexFactoryInterpolantsVSToPS Interpolants, int UVIndex) { float4 UVVector = Interpolants.TexCoords[UVIndex / 2]; return UVIndexUseZW(UVIndex) ? UVVector.zw : UVVector.xy; } void SetUV(inout FVertexFactoryInterpolantsVSToPS Interpolants, int UVIndex, float2 InValue) { FLATTEN if (UVIndexUseZW(UVIndex)) { Interpolants.TexCoords[UVIndex / 2].zw = InValue; } else { Interpolants.TexCoords[UVIndex / 2].xy = InValue; } } #endif /** Converts from vertex factory specific interpolants to a FMaterialPixelParameters, which is used by material inputs. */ FMaterialPixelParameters GetMaterialPixelParameters(FVertexFactoryInterpolantsVSToPS Interpolants, float4 SvPosition) { // GetMaterialPixelParameters is responsible for fully initializing the result FMaterialPixelParameters Result = MakeInitializedMaterialPixelParameters(); #if NUM_TEX_COORD_INTERPOLATORS UNROLL for (int CoordinateIndex = 0; CoordinateIndex < NUM_TEX_COORD_INTERPOLATORS; CoordinateIndex++) { Result.TexCoords[CoordinateIndex] = GetUV(Interpolants, CoordinateIndex); } #endif Result.VertexColor = 1; #if NEEDS_TANGENT_BASIS half4 TangentToWorld0 = Interpolants.TangentToWorld0AndInterp_Sizer; half4 TangentToWorld2 = Interpolants.TangentToWorld2; float SubImageLerp = Interpolants.TangentToWorld0AndInterp_Sizer.w; #if GENERATE_SPHERICAL_PARTICLE_NORMALS && USE_PARTICLE_POSITION { // can be optimized float4 ScreenPosition = SvPositionToResolvedScreenPosition(SvPosition); float3 TranslatedWorldPosition = SvPositionToResolvedTranslatedWorld(SvPosition); Result.TangentToWorld = GetSphericalParticleNormal(TranslatedWorldPosition, Interpolants.ParticleTranslatedWorldPositionAndSize.xyz, Interpolants.ParticleTranslatedWorldPositionAndSize.w); } #else Result.TangentToWorld = AssembleTangentToWorld(TangentToWorld0.xyz, TangentToWorld2); #endif #else float SubImageLerp = Interpolants.SubImageLerp; #endif Result.UnMirrored = 1; Result.Particle.MacroUV = SpriteVF.MacroUVParameters; #if NEEDS_PARTICLE_COLOR Result.Particle.Color = Interpolants.Color; #endif #if USE_DYNAMIC_PARAMETERS Result.Particle.DynamicParameter = Interpolants.DynamicParameter; #endif //USE_DYNAMIC_PARAMETERS #if USE_PARTICLE_POSITION Result.Particle.TranslatedWorldPositionAndSize = Interpolants.ParticleTranslatedWorldPositionAndSize; Result.Particle.PrevTranslatedWorldPositionAndSize = Result.Particle.TranslatedWorldPositionAndSize; #endif #if USE_PARTICLE_VELOCITY Result.Particle.Velocity = Interpolants.ParticleVelocity; #endif #if USE_PARTICLE_TIME Result.Particle.RelativeTime = Interpolants.ParticleTime; #endif Result.Particle.MotionBlurFade = 1.0f; #if USE_PARTICLE_LIGHTING_OFFSET Result.LightingPositionOffset = Interpolants.LightingPositionOffset; #endif #if USE_PARTICLE_SUBUVS Result.Particle.SubUVCoords[0] = Interpolants.ParticleSubUVs.xy; Result.Particle.SubUVCoords[1] = Interpolants.ParticleSubUVs.zw; Result.Particle.SubUVLerp = SubImageLerp; #endif #if LIGHTMAP_UV_ACCESS Result.LightmapUVs = Interpolants.LightMapUVs; #endif #if USE_PARTICLE_SIZE Result.Particle.Size = Interpolants.ParticleSize; #endif #if USE_PARTICLE_SPRITE_ROTATION Result.Particle.SpriteRotation = Interpolants.ParticleSpriteRotation; #endif Result.TwoSidedSign = 1; return Result; } float3 VertexFactoryGetPreviousInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates); float3 VertexFactoryGetInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates); /** Converts from vertex factory specific input to a FMaterialVertexParameters, which is used by vertex shader material inputs. */ FMaterialVertexParameters GetMaterialVertexParameters( FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float3 WorldPosition, float3x3 TangentToLocal, bool bIsPreviousFrame = false) { FMaterialVertexParameters Result = MakeInitializedMaterialVertexParameters(); Result.SceneData = Intermediates.SceneData; Result.WorldPosition = WorldPosition; if (bIsPreviousFrame) { Result.PositionInstanceSpace = VertexFactoryGetPreviousInstanceSpacePosition(Input, Intermediates); } else { Result.PositionInstanceSpace = VertexFactoryGetInstanceSpacePosition(Input, Intermediates); } Result.PositionPrimitiveSpace = Result.PositionInstanceSpace; // No support for instancing, so instance == primitive Result.VertexColor = Input.Color; Result.TangentToWorld = mul(TangentToLocal, GetLocalToWorld3x3()); Result.Particle.MacroUV = SpriteVF.MacroUVParameters; Result.Particle.Color = Intermediates.Color; Result.Particle.MotionBlurFade = 1.0f; Result.PreSkinnedPosition = Input.Position.xyz; Result.PreSkinnedNormal = TangentToLocal[2].xyz; // Previous frame not handled deliberately. Lacks necessary information and // primitives using this VF are usually transparent and hence don't output velocity Result.PrevFrameLocalToWorld = GetPrimitiveDataFromUniformBuffer().LocalToWorld; #if USE_DYNAMIC_PARAMETERS Result.Particle.DynamicParameter = Intermediates.DynamicParameter; #endif //USE_DYNAMIC_PARAMETERS #if USE_PARTICLE_POSITION Result.Particle.TranslatedWorldPositionAndSize = Intermediates.TranslatedWorldPositionAndSize; Result.Particle.PrevTranslatedWorldPositionAndSize = Result.Particle.TranslatedWorldPositionAndSize; #endif #if USE_PARTICLE_VELOCITY Result.Particle.Velocity = Intermediates.ParticleVelocity; #endif #if USE_PARTICLE_TIME Result.Particle.RelativeTime = Intermediates.RelativeTime; #endif #if NUM_MATERIAL_TEXCOORDS_VERTEX #if NUM_MATERIAL_TEXCOORDS_VERTEX >= 1 Result.TexCoords[0] = Intermediates.TexCoord.xy; #if NUM_MATERIAL_TEXCOORDS_VERTEX >= 2 Result.TexCoords[1] = Intermediates.TexCoord.zw; #if NUM_MATERIAL_TEXCOORDS_VERTEX >= 3 Result.TexCoords[2] = Intermediates.TexCoord_Unflipped.xy; #if NUM_MATERIAL_TEXCOORDS_VERTEX >= 4 Result.TexCoords[3] = Intermediates.TexCoord_Unflipped.zw; #endif // >= 4 #endif // >= 3 #endif // >= 2 #endif // >= 1 #endif //NUM_MATERIAL_TEXCOORDS_VERTEX #if USE_PARTICLE_SUBUVS Result.Particle.SubUVCoords[0] = Intermediates.TexCoord.xy; Result.Particle.SubUVCoords[1] = Intermediates.TexCoord.zw; #endif #if USE_PARTICLE_SIZE Result.Particle.Size = Intermediates.ParticleSize; #endif #if USE_PARTICLE_SPRITE_ROTATION Result.Particle.SpriteRotation = Intermediates.ParticleSpriteRotation; #endif #if ENABLE_NEW_HLSL_GENERATOR EvaluateVertexMaterialAttributes(Result); #endif Result.LWCData = MakeMaterialLWCData(Result); return Result; } float3 SafeNormalize(float3 V) { return V * rsqrt(max(dot(V,V),0.00000001)); } void GetTangents(float3 TranslatedWorldPosition, float3 OldTranslatedWorldPosition, float SpriteRotation, out float3 OutRight, out float3 OutUp) { // Select camera up/right vectors. float3 ResolvedViewRight = lerp(ResolvedView.ViewRight, ResolvedView.HMDViewNoRollRight, SpriteVF.RemoveHMDRoll); float3 ResolvedViewUp = lerp(ResolvedView.ViewUp, ResolvedView.HMDViewNoRollUp, SpriteVF.RemoveHMDRoll); float3 CameraRight = lerp(ResolvedViewRight, SpriteVF.AxisLockRight.xyz, SpriteVF.AxisLockRight.w); float3 CameraUp = lerp(-ResolvedViewUp, SpriteVF.AxisLockUp.xyz, SpriteVF.AxisLockUp.w); // Determine the vector from the particle to the camera and the particle's movement direction. float3 CameraDirection = -GetCameraVectorFromTranslatedWorldPosition(ResolvedView, TranslatedWorldPosition); float3 RightVector = CameraRight.xyz; float3 UpVector = CameraUp.xyz; float4 LocalTangentSelector = SpriteVF.TangentSelector; BRANCH if (SpriteVF.CameraFacingBlend.x > 0.f) { // Blend between PSA_FacingCamera and PSA_Square over distance float CameraDistanceSq = GetDistanceToCameraFromViewVectorSqr(ResolvedView.TranslatedWorldCameraOrigin - TranslatedWorldPosition); float AlignmentMode = saturate(CameraDistanceSq * SpriteVF.CameraFacingBlend.y - SpriteVF.CameraFacingBlend.z); float3 CameraFacingRight = SafeNormalize(cross(CameraDirection, float3(0,0,1))); float3 CameraFacingUp = cross(CameraDirection, CameraFacingRight); RightVector = normalize(lerp(RightVector, CameraFacingRight, AlignmentMode)); UpVector = normalize(lerp(UpVector, CameraFacingUp, AlignmentMode)); } else { FLATTEN if (LocalTangentSelector.y > 0) { // Tangent vectors for PSA_Velocity. float3 ParticleDirection = SafeNormalize(TranslatedWorldPosition - OldTranslatedWorldPosition); RightVector = SafeNormalize(cross(CameraDirection, ParticleDirection)); UpVector = -ParticleDirection; } else if (LocalTangentSelector.z > 0) { // Tangent vectors for rotation locked about an axis. RightVector = SpriteVF.AxisLockRight.xyz; UpVector = -SafeNormalize(cross(RightVector,CameraDirection)); } else if (LocalTangentSelector.w > 0) { // Tangent vectors for camera facing position. RightVector = SafeNormalize(cross(CameraDirection,float3(0,0,1))); UpVector = cross(CameraDirection, RightVector); } } // Determine the angle of rotation. float SinRotation; // = 0 float CosRotation; // = 1 sincos(SpriteRotation, SinRotation, CosRotation); // Rotate the sprite to determine final tangents. OutRight = SinRotation * UpVector + CosRotation * RightVector; OutUp = CosRotation * UpVector - SinRotation * RightVector; } /** derive basis vectors */ float3x3 CalcTangentBasis(FVertexFactoryIntermediates Intermediates) { #if NEEDS_TANGENT_BASIS // Using camera facing TangentX and TangentY. The resulting tangent basis is not orthonormal with anything other than ENM_CameraFacing, // So there are artifacts with tangent space calculations like the TransformVector node, // But this allows lighting based on a world space shape via the normal while still having normal maps camera aligned. float3x3 Result; Result[0] = Intermediates.TangentRight; Result[1] = Intermediates.TangentUp; float3 ParticleWorldPosition = Intermediates.ParticleTranslatedWorldPosition + DFHackToFloat(ResolvedView.PreViewTranslation); // ENM_CameraFacing //@todo - use static branching if (SpriteVF.NormalsType < .5f) { Result[2] = normalize(cross(Result[0],Result[1])); } // ENM_Spherical else if (SpriteVF.NormalsType < 1.5f) { float3 TangentZ = normalize(ParticleWorldPosition - SpriteVF.NormalsSphereCenter.xyz); Result[2] = TangentZ; } // ENM_Cylindrical else { float3 ClosestPointOnCylinder = SpriteVF.NormalsSphereCenter.xyz + dot(SpriteVF.NormalsCylinderUnitDirection.xyz, ParticleWorldPosition - SpriteVF.NormalsSphereCenter.xyz) * SpriteVF.NormalsCylinderUnitDirection.xyz; float3 TangentZ = normalize(ParticleWorldPosition - ClosestPointOnCylinder); Result[2] = TangentZ; } return Result; #else // Return the identity matrix. return float3x3(1,0,0,0,1,0,0,0,1); #endif } // Note: C++ geometry setup behavior has to match this define #define SUPPORTS_PARTICLE_CUTOUTS (FEATURE_LEVEL >= FEATURE_LEVEL_ES3_1) #if SUPPORTS_PARTICLE_CUTOUTS /** Number of vertices in each SubUV frame of CutoutGeometry. */ uint NumCutoutVerticesPerFrame; /** SubUV animation bounding geometry used to minimize overdraw. Each frame has its own polygon. */ Buffer CutoutGeometry; #endif void ComputeBillboardUVs(FVertexFactoryInput Input, out float2 UVForPosition, out float2 UVForTexturing, out float2 UVForTexturingUnflipped) { // Encoding the UV flip in the sign of the size data. float2 UVFlip = sign(Input.SizeRotSubImage.xy); #if SUPPORTS_PARTICLE_CUTOUTS BRANCH if (NumCutoutVerticesPerFrame > 0) { // Avoid uint divide which is extremely slow on GCN uint CutoutVertexIndex = (uint)fmod(Input.VertexId, NumCutoutVerticesPerFrame); float NumFrames = SpriteVF.SubImageSize.x * SpriteVF.SubImageSize.y; uint SubImageIndexInt = (uint)fmod(Input.SizeRotSubImage.w, NumFrames); FLATTEN if (UVFlip.x * UVFlip.y < 0) { // Reverse the winding order of the polygon when only flipping in one direction to counteract UV flipping CutoutVertexIndex = NumCutoutVerticesPerFrame - 1 - CutoutVertexIndex; } // Fetch the billboard space positions from the appropriate frame of the preprocessed cutout geometry UVForTexturing = UVForTexturingUnflipped = CutoutGeometry[SubImageIndexInt * NumCutoutVerticesPerFrame + CutoutVertexIndex]; // Invert positions on the billboard so that the cutout geometry still contains the visible portion // This changes the winding order when only one direction is flipped UVForPosition.x = UVFlip.x < 0.0 ? 1.0 - UVForTexturing.x : UVForTexturing.x; UVForPosition.y = UVFlip.y < 0.0 ? 1.0 - UVForTexturing.y : UVForTexturing.y; } else #endif { UVForTexturing.x = UVFlip.x < 0.0 ? 1.0 - Input.TexCoord.x : Input.TexCoord.x; UVForTexturing.y = UVFlip.y < 0.0 ? 1.0 - Input.TexCoord.y : Input.TexCoord.y; // Note: not inverting positions, as that would change the winding order UVForPosition = UVForTexturingUnflipped = Input.TexCoord.xy; } } FVertexFactoryIntermediates GetVertexFactoryIntermediates(FVertexFactoryInput Input) { FVertexFactoryIntermediates Intermediates = (FVertexFactoryIntermediates)0; Intermediates.SceneData = VF_GPUSCENE_GET_INTERMEDIATES(Input); // World position. FLWCVector3 LWCTileOffset = MakeLWCVector3(SpriteVF.LWCTile, 0); FLWCMatrix Transform = DFToTileOffset(GetPrimitiveDataFromUniformBuffer().LocalToWorld); FLWCVector3 ParticleWorldPosition = LWCAdd(LWCMultiply(Input.Position.xyz, Transform), LWCTileOffset); FLWCVector3 ParticleOldWorldPosition = LWCAdd(LWCMultiply(Input.OldPosition.xyz, Transform), LWCTileOffset); float3 ParticleTranslatedWorldPosition = LWCToFloat(LWCAdd(ParticleWorldPosition, ResolvedView.TileOffset.PreViewTranslation)); float3 ParticleOldTranslatedWorldPosition = LWCToFloat(LWCAdd(ParticleOldWorldPosition, ResolvedView.TileOffset.PreViewTranslation)); Intermediates.ParticleTranslatedWorldPosition = ParticleTranslatedWorldPosition; const float SpriteRotation = Input.SizeRotSubImage.z * SpriteVF.RotationScale + SpriteVF.RotationBias; // Tangents. float3 Right,Up; GetTangents(ParticleTranslatedWorldPosition, ParticleOldTranslatedWorldPosition, SpriteRotation, Right, Up); Intermediates.TangentUp = Up; Intermediates.TangentRight = Right; float2 UVForPosition; float2 UVForTexturing; float2 UVForTexturingUnflipped; ComputeBillboardUVs(Input, UVForPosition, UVForTexturing, UVForTexturingUnflipped); // Vertex position. float4 VertexWorldPosition = float4(ParticleTranslatedWorldPosition, 1); float2 Size = abs(Input.SizeRotSubImage.xy); VertexWorldPosition += Size.x * (UVForPosition.x + SpriteVF.PivotOffset.x) * float4(Right,0); VertexWorldPosition += Size.y * (UVForPosition.y + SpriteVF.PivotOffset.y) * float4(Up,0); Intermediates.VertexWorldPosition = VertexWorldPosition.xyz; // SubUV. float SubImageIndex = Input.SizeRotSubImage.w; float SubImageLerp = frac(SubImageIndex); // add a bias to SubImageA to avoid numerical precision issues around SubImageSize.x vs SubImageSize.z float SubImageA = SubImageIndex - SubImageLerp + 0.5f; float SubImageB = SubImageA + 1; float SubImageAH = floor( fmod( SubImageA, SpriteVF.SubImageSize.x ) ); float SubImageBH = floor( fmod( SubImageB, SpriteVF.SubImageSize.x ) ); float SubImageAV = floor( SubImageA * SpriteVF.SubImageSize.z ); float SubImageBV = floor( SubImageB * SpriteVF.SubImageSize.z ); Intermediates.TexCoord.xy = (float2( SubImageAH, SubImageAV ) + UVForTexturing) * SpriteVF.SubImageSize.zw ; Intermediates.TexCoord.zw = (float2(SubImageBH, SubImageBV) + UVForTexturing) * SpriteVF.SubImageSize.zw; #if NUM_MATERIAL_TEXCOORDS_VERTEX >= 3 Intermediates.TexCoord_Unflipped.xy = (float2(SubImageAH, SubImageAV) + UVForTexturingUnflipped) * SpriteVF.SubImageSize.zw; Intermediates.TexCoord_Unflipped.zw = (float2(SubImageBH, SubImageBV) + UVForTexturingUnflipped) * SpriteVF.SubImageSize.zw; #endif Intermediates.SubImageLerp = SubImageLerp; Intermediates.Color = Input.Color; #if USE_DYNAMIC_PARAMETERS Intermediates.DynamicParameter = Input.DynamicParameter; #endif //USE_DYNAMIC_PARAMETERS #if USE_PARTICLE_POSITION float ParticleRadius = .5f * min(Input.SizeRotSubImage.x, Input.SizeRotSubImage.y); Intermediates.TranslatedWorldPositionAndSize = float4(ParticleTranslatedWorldPosition, ParticleRadius); #if GENERATE_SPHERICAL_PARTICLE_NORMALS // Recenter the particle position for GENERATE_SPHERICAL_PARTICLE_NORMALS to use the right center. Intermediates.TranslatedWorldPositionAndSize.xyz += Size.x * (SpriteVF.PivotOffset.x + .5) * Right + Size.y * (SpriteVF.PivotOffset.y + .5) * Up; #endif #endif #if !USE_PARTICLE_VELOCITY if (SpriteVF.UseVelocityForMotionBlur > 0.0f) #endif { Intermediates.ParticlePrevToCurrDelta = ParticleTranslatedWorldPosition - ParticleOldTranslatedWorldPosition; #if USE_PARTICLE_VELOCITY float3 ParticleVelocity = Intermediates.ParticlePrevToCurrDelta * SpriteVF.InvDeltaSeconds; Intermediates.ParticleVelocity.xyz = normalize(ParticleVelocity); Intermediates.ParticleVelocity.w = length(ParticleVelocity); #endif } #if USE_PARTICLE_TIME Intermediates.RelativeTime = Input.Position.w; #endif Intermediates.TangentToLocal = CalcTangentBasis(Intermediates); #if USE_PARTICLE_LIGHTING_OFFSET // Hash function based on the particle ID to generate a uniformly distributed 3d offset float3 RandomParticleOffset = frac(Square(Input.OldPosition.w + 10) * float3(1361.456345, 2333.578, 3623.983)) * 2 - 1; Intermediates.LightingPositionOffset = .5f * RandomParticleOffset; #endif #if USE_PARTICLE_SIZE Intermediates.ParticleSize = Size; #endif #if USE_PARTICLE_SPRITE_ROTATION Intermediates.ParticleSpriteRotation = SpriteRotation; #endif return Intermediates; } float4 VertexFactoryGetWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) { return float4(Intermediates.VertexWorldPosition,1); } float3 VertexFactoryGetInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) { return Input.Position.xyz; } float3 VertexFactoryGetWorldNormal(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) { return CalcTangentBasis(Intermediates)[2]; } float4 VertexFactoryGetRasterizedWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float4 TranslatedWorldPosition) { #if SPHERICAL_PARTICLE_OPACITY // For particles using spherical opacity, move the quad toward the viewer so that it lies in front of the sphere defined by the particle // This avoids opaque objects intersecting the particle from causing clipping artifacts due to depth testing // The downside is that the particle will clip the near plane sooner float Radius = .5f * min(Input.SizeRotSubImage.x, Input.SizeRotSubImage.y); return ReprojectPosition(TranslatedWorldPosition, Radius); #else return TranslatedWorldPosition; #endif } float3 VertexFactoryGetPositionForVertexLighting(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float3 TranslatedWorldPosition) { #if SUPPORTS_PARTICLE_CUTOUTS && (TRANSLUCENCY_LIGHTING_VOLUMETRIC_PERVERTEX_DIRECTIONAL || TRANSLUCENCY_LIGHTING_VOLUMETRIC_PERVERTEX_NONDIRECTIONAL) // Do per-vertex lighting with particle position instead of vertex position when we're using SubUV cutouts, since the vertex positions vary per-frame which would cause popping return NumCutoutVerticesPerFrame > 0 ? Intermediates.ParticleTranslatedWorldPosition : TranslatedWorldPosition; #else return TranslatedWorldPosition; #endif } FVertexFactoryInterpolantsVSToPS VertexFactoryGetInterpolantsVSToPS(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, FMaterialVertexParameters VertexParameters) { FVertexFactoryInterpolantsVSToPS Interpolants; // Initialize the whole struct to 0 Interpolants = (FVertexFactoryInterpolantsVSToPS)0; #if NUM_TEX_COORD_INTERPOLATORS float2 CustomizedUVs[NUM_TEX_COORD_INTERPOLATORS]; GetMaterialCustomizedUVs(VertexParameters, CustomizedUVs); GetCustomInterpolators(VertexParameters, CustomizedUVs); UNROLL for (int CoordinateIndex = 0; CoordinateIndex < NUM_TEX_COORD_INTERPOLATORS; CoordinateIndex++) { SetUV(Interpolants, CoordinateIndex, CustomizedUVs[CoordinateIndex]); } #endif #if LIGHTMAP_UV_ACCESS Interpolants.LightMapUVs = Intermediates.TexCoord.xy; #endif #if USE_PARTICLE_SUBUVS Interpolants.ParticleSubUVs.xy = VertexParameters.Particle.SubUVCoords[0]; Interpolants.ParticleSubUVs.zw = VertexParameters.Particle.SubUVCoords[1]; #endif #if NEEDS_TANGENT_BASIS // Calculate the transform from tangent to world space. // Note that "local" space for particles is actually oriented in world space! Therefore no rotation is needed. float3x3 TangentToWorld = Intermediates.TangentToLocal; Interpolants.TangentToWorld0AndInterp_Sizer.xyz = TangentToWorld[0]; Interpolants.TangentToWorld0AndInterp_Sizer.w = Intermediates.SubImageLerp; Interpolants.TangentToWorld2 = float4(TangentToWorld[2], sign(determinant(Intermediates.TangentToLocal))); #else Interpolants.SubImageLerp = Intermediates.SubImageLerp; #endif #if NEEDS_PARTICLE_COLOR Interpolants.Color = Intermediates.Color; #endif #if USE_DYNAMIC_PARAMETERS Interpolants.DynamicParameter = Intermediates.DynamicParameter; #endif //USE_DYNAMIC_PARAMETERS #if USE_PARTICLE_POSITION Interpolants.ParticleTranslatedWorldPositionAndSize = Intermediates.TranslatedWorldPositionAndSize; #endif #if USE_PARTICLE_VELOCITY Interpolants.ParticleVelocity = Intermediates.ParticleVelocity; #endif #if USE_PARTICLE_TIME Interpolants.ParticleTime = Intermediates.RelativeTime; #endif #if USE_PARTICLE_LIGHTING_OFFSET Interpolants.LightingPositionOffset = Intermediates.LightingPositionOffset; #endif #if USE_PARTICLE_SIZE Interpolants.ParticleSize = Intermediates.ParticleSize; #endif #if USE_PARTICLE_SPRITE_ROTATION Interpolants.ParticleSpriteRotation = Intermediates.ParticleSpriteRotation; #endif return Interpolants; } float4 VertexFactoryGetPreviousWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) { float4 Position = VertexFactoryGetWorldPosition(Input, Intermediates); Position.xyz -= Intermediates.ParticlePrevToCurrDelta * SpriteVF.UseVelocityForMotionBlur; return Position; } // local position relative to primitive float3 VertexFactoryGetPreviousInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) { return Input.Position.xyz; } /** * Get the 3x3 tangent basis vectors for this vertex factory * * @param Input - vertex input stream structure * @return 3x3 matrix */ float3x3 VertexFactoryGetTangentToLocal(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) { return Intermediates.TangentToLocal; } float4 VertexFactoryGetTranslatedPrimitiveVolumeBounds(FVertexFactoryInterpolantsVSToPS Interpolants) { #if USE_PARTICLE_POSITION return Interpolants.ParticleTranslatedWorldPositionAndSize; #endif return 0; } uint VertexFactoryGetPrimitiveId(FVertexFactoryInterpolantsVSToPS Interpolants) { return 0; } #include "VertexFactoryDefaultInterface.ush"