// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= ParticleBeamTrailVertexFactory.hlsl: Particle vertex factory shader code. =============================================================================*/ #include "/Engine/Private/VertexFactoryCommon.ush" #include "NiagaraVFCommon.usf" #define USE_PARTICLE_POSITION (NEEDS_PARTICLE_POSITION) #define USE_PARTICLE_VELOCITY (NEEDS_PARTICLE_VELOCITY) #define USE_PARTICLE_TIME (NEEDS_PARTICLE_TIME) #define USE_PARTICLE_SIZE (NEEDS_PARTICLE_SIZE) #define USE_PARTICLE_RANDOM (NEEDS_PARTICLE_RANDOM) #define USE_PARTICLE_INTERPOLATION 1 #define U_DISTRIBUTION_SCALED_UNIFORMLY 0 #define U_DISTRIBUTION_SCALED_USING_RIBBON_LENGTH 1 #define U_DISTRIBUTION_TILED_OVER_RIBBON_LENGTH 2 #define U_DISTRIBUTION_TILED_FROM_START_OVER_RIBBON_LENGTH 3 #define SLICEVERTEXDATA_POSITION_OFFSET 0 #define SLICEVERTEXDATA_NORMAL_OFFSET 2 #define SLICEVERTEXDATA_TEXTUREV_OFFSET 4 #define SLICEVERTEXDATA_STRIDE 5 #define INDEX_GEN_DRAW_INDIRECT_OFFSET 3 // Duplicated in NiagaraRibbonCommon.ush #define INDEX_GEN_NUM_SUB_SEGMENTS_OFFSET 15 // Duplicated in NiagaraRibbonCommon.ush #define INDEX_GEN_ONE_OVER_NUM_SUB_SEGMENTS_OFFSET 16 // Duplicated in NiagaraRibbonCommon.ush uint UnpackParticleId(uint InterpIdx) { return (InterpIdx >> NiagaraRibbonVF.ParticleIdShift) & NiagaraRibbonVF.ParticleIdMask; } uint UnpackInterpSlice(uint InterpIdx) { return (InterpIdx >> NiagaraRibbonVF.InterpIdShift) & NiagaraRibbonVF.InterpIdMask; } uint UnpackParticleVertexId(uint InterpIdx) { return InterpIdx & NiagaraRibbonVF.SliceVertexIdMask; } float2 UnpackParticleVertexPosition(uint InterpIdx) { uint SliceVertexPositionIndex = UnpackParticleVertexId(InterpIdx) * SLICEVERTEXDATA_STRIDE + SLICEVERTEXDATA_POSITION_OFFSET; return float2(NiagaraRibbonVFLooseParameters.SliceVertexData[SliceVertexPositionIndex], NiagaraRibbonVFLooseParameters.SliceVertexData[SliceVertexPositionIndex + 1]); } float2 UnpackParticleVertexNormal(uint InterpIdx) { uint SliceVertexNormalIndex = UnpackParticleVertexId(InterpIdx) * SLICEVERTEXDATA_STRIDE + SLICEVERTEXDATA_NORMAL_OFFSET; return float2(NiagaraRibbonVFLooseParameters.SliceVertexData[SliceVertexNormalIndex], NiagaraRibbonVFLooseParameters.SliceVertexData[SliceVertexNormalIndex + 1]); } float UnpackParticleVertexTextureV(uint InterpIdx) { return NiagaraRibbonVFLooseParameters.SliceVertexData[UnpackParticleVertexId(InterpIdx) * SLICEVERTEXDATA_STRIDE + SLICEVERTEXDATA_TEXTUREV_OFFSET]; } FLWCVector3 TransformPosition(FLWCMatrix LocalToWorld, float3 InPosition) { if (NiagaraRibbonVF.bLocalSpace) { return LWCMultiply(InPosition, LocalToWorld); } else { return MakeLWCVector3(NiagaraRibbonVF.SystemLWCTile, InPosition); } } float3 TransformVector(FLWCMatrix LocalToWorld, float3 InvScale, float3 InVector) { if (NiagaraRibbonVF.bLocalSpace) { return RotateLocalToWorld(InVector, LWCToFloat3x3(LocalToWorld), InvScale); } else { return InVector; } } float3 CubicInterp(float3 P0, float3 T0, float3 P1, float3 T1, float A, float TS) { const float A2 = A * A; const float A3 = A2 * A; return (((2*A3)-(3*A2)+1) * P0) + (TS * (A3-(2*A2)+A) * T0) + (TS * (A3-A2) * T1) + (((-2*A3)+(3*A2)) * P1); } /********************************************************************************* * Particle specific *********************************************************************************/ // Naming convention : // RawParticleId : Used to access per particle data stored in the VF. Actual particle data needs to be indirected through SortedIndices. // ParticleId : Used to access particle data in NiagaraVFParticleAccess. // VertexId : Used to represent a vertex on both side of the particles. Has some specific data like UV. // InterpVtxId : A virtual index that represent a vertex of or between 2 VertexId. struct FVertexFactoryInput { uint InterpVtxId : SV_VertexID; // Optional instance ID for vertex layered rendering #if FEATURE_LEVEL >= FEATURE_LEVEL_SM4 && ONEPASS_POINTLIGHT_SHADOW && USING_VERTEX_SHADER_LAYER #undef GetInstanceIdFromVF #define GetInstanceIdFromVF(VFInput) (VFInput.InstanceId) uint InstanceId : SV_InstanceID; #else VF_INSTANCED_STEREO_DECLARE_INPUT_BLOCK() #endif VF_MOBILE_MULTI_VIEW_DECLARE_INPUT_BLOCK() }; // The interpolation data required for an interp vertex. struct FInterpVtxData { uint RawParticleId0; uint RawParticleId1; uint ParticleId0; uint ParticleId1; float Alpha; }; struct FSegmentData { float2 UV0Scale; float2 UV0Offset; float2 UV1Scale; float2 UV1Offset; float U0DistributionScaler; float U1DistributionScaler; uint StartParticleId; uint LastParticleId; }; struct FRibbonPositionIntermediates { FLWCVector3 Position; float3 LocalPosition; float3 Direction; float DistanceOnSegment; float RibbonWidth; float3 GeoUp; float3 GeoRight; float3 TangentX; float3 TangentY; float3 TangentZ; }; struct FVertexFactoryIntermediates { FRibbonPositionIntermediates Current; FRibbonPositionIntermediates Previous; float4 Color; float4 TexCoord; #if USE_PARTICLE_POSITION float4 TranslatedWorldPositionAndSize; #endif float4 ParticleVelocity; #if USE_PARTICLE_TIME float RelativeTime; #endif #if USE_PARTICLE_SIZE float2 ParticleSize; #endif #if USE_PARTICLE_RANDOM float ParticleRandom; #endif #if (DYNAMIC_PARAMETERS_MASK & 1) float4 DynamicParameter; #endif #if (DYNAMIC_PARAMETERS_MASK & 2) float4 DynamicParameter1; #endif #if (DYNAMIC_PARAMETERS_MASK & 4) float4 DynamicParameter2; #endif #if (DYNAMIC_PARAMETERS_MASK & 8) float4 DynamicParameter3; #endif /** Cached primitive and instance data */ FSceneDataIntermediates SceneData; }; FPrimitiveSceneData GetPrimitiveData(FVertexFactoryIntermediates Intermediates) { return Intermediates.SceneData.Primitive; } struct FVertexFactoryInterpolantsVSToPS { TANGENTTOWORLD_INTERPOLATOR_BLOCK #if (DYNAMIC_PARAMETERS_MASK & 1) float4 DynamicParameter : PARTICLE_DYNAMIC_PARAM0; #endif #if (DYNAMIC_PARAMETERS_MASK & 2) float4 DynamicParameter1 : PARTICLE_DYNAMIC_PARAM1; #endif #if (DYNAMIC_PARAMETERS_MASK & 4) float4 DynamicParameter2 : PARTICLE_DYNAMIC_PARAM2; #endif #if (DYNAMIC_PARAMETERS_MASK & 8) float4 DynamicParameter3 : PARTICLE_DYNAMIC_PARAM3; #endif #if NUM_TEX_COORD_INTERPOLATORS float4 TexCoords[(NUM_TEX_COORD_INTERPOLATORS + 1) / 2] : TEXCOORD3; #endif #if NEEDS_PARTICLE_COLOR float4 Color : TEXCOORD2; #endif #if USE_PARTICLE_POSITION /** Cam-relative (translated) particle center and radius */ float4 TranslatedWorldPositionAndSize : PARTICLE_POSITION; #endif #if USE_PARTICLE_VELOCITY float4 ParticleVelocity : PARTICLE_VELOCITY; #endif #if USE_PARTICLE_TIME float RelativeTime : PARTICLE_TIME; #endif #if USE_PARTICLE_SIZE float2 ParticleSize : PARTICLE_SIZE; #endif #if USE_PARTICLE_RANDOM float ParticleRandom : PARTICLE_RANDOM; #endif }; #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 uint GetInterpCount() { BRANCH if (NiagaraRibbonVFLooseParameters.IndirectDrawOutputOffset != -1) { return NiagaraRibbonVFLooseParameters.IndirectDrawOutput[NiagaraRibbonVFLooseParameters.IndirectDrawOutputOffset + INDEX_GEN_NUM_SUB_SEGMENTS_OFFSET]; } else { return NiagaraRibbonVF.InterpCount; } } float GetOneOverInterpCount() { BRANCH if (NiagaraRibbonVFLooseParameters.IndirectDrawOutputOffset != -1) { return asfloat(NiagaraRibbonVFLooseParameters.IndirectDrawOutput[NiagaraRibbonVFLooseParameters.IndirectDrawOutputOffset + INDEX_GEN_ONE_OVER_NUM_SUB_SEGMENTS_OFFSET]); } else { return NiagaraRibbonVF.OneOverInterpCount; } } void GetInterpVtxAndRibbonDataData(FVertexFactoryInput Input, out FInterpVtxData InterpVtxData, out FSegmentData SegmentData) { // Get First Particle Data InterpVtxData.RawParticleId0 = UnpackParticleId(Input.InterpVtxId); InterpVtxData.ParticleId0 = NiagaraRibbonVFLooseParameters.SortedIndices[InterpVtxData.RawParticleId0]; // Unpack Ribbon Data const uint RibbonIndex = NiagaraRibbonVF.ShouldUseMultiRibbon == 1 ? NiagaraRibbonVFLooseParameters.MultiRibbonIndices[InterpVtxData.RawParticleId0] : 0; const uint RibbonBufferIndex = RibbonIndex * 8; SegmentData.UV0Scale = float2(asfloat(NiagaraRibbonVFLooseParameters.PackedPerRibbonDataByIndex[RibbonBufferIndex + 0]), NiagaraRibbonVF.PackedVData.x); SegmentData.UV0Offset = float2(asfloat(NiagaraRibbonVFLooseParameters.PackedPerRibbonDataByIndex[RibbonBufferIndex + 1]), NiagaraRibbonVF.PackedVData.y); SegmentData.U0DistributionScaler = asfloat(NiagaraRibbonVFLooseParameters.PackedPerRibbonDataByIndex[RibbonBufferIndex + 2]); SegmentData.UV1Scale = float2(asfloat(NiagaraRibbonVFLooseParameters.PackedPerRibbonDataByIndex[RibbonBufferIndex + 3]), NiagaraRibbonVF.PackedVData.z); SegmentData.UV1Offset = float2(asfloat(NiagaraRibbonVFLooseParameters.PackedPerRibbonDataByIndex[RibbonBufferIndex + 4]), NiagaraRibbonVF.PackedVData.w); SegmentData.U1DistributionScaler = asfloat(NiagaraRibbonVFLooseParameters.PackedPerRibbonDataByIndex[RibbonBufferIndex + 5]); SegmentData.StartParticleId = NiagaraRibbonVFLooseParameters.PackedPerRibbonDataByIndex[RibbonBufferIndex + 6]; SegmentData.LastParticleId = NiagaraRibbonVFLooseParameters.PackedPerRibbonDataByIndex[RibbonBufferIndex + 7]; // Get Remaining Interp Data InterpVtxData.RawParticleId1 = min(InterpVtxData.RawParticleId0 + 1, SegmentData.LastParticleId); InterpVtxData.ParticleId1 = NiagaraRibbonVFLooseParameters.SortedIndices[InterpVtxData.RawParticleId1]; #if USE_PARTICLE_INTERPOLATION { InterpVtxData.Alpha = float(UnpackInterpSlice(Input.InterpVtxId)) * GetOneOverInterpCount(); } #else InterpVtxData.Alpha = 0; #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 // NUM_MATERIAL_TEXCOORDS Result.VertexColor = 1; #if NEEDS_PARTICLE_COLOR Result.Particle.Color = Interpolants.Color; #endif half3 TangentToWorld0 = Interpolants.TangentToWorld0.xyz; half4 TangentToWorld2 = Interpolants.TangentToWorld2; Result.TangentToWorld = AssembleTangentToWorld( TangentToWorld0, TangentToWorld2 ); Result.UnMirrored = 1; Result.TwoSidedSign = 1; #if (DYNAMIC_PARAMETERS_MASK != 0) Result.Particle.DynamicParameterValidMask = NiagaraRibbonVF.MaterialParamValidMask; #endif #if (DYNAMIC_PARAMETERS_MASK & 1) Result.Particle.DynamicParameter = Interpolants.DynamicParameter; #endif #if (DYNAMIC_PARAMETERS_MASK & 2) Result.Particle.DynamicParameter1 = Interpolants.DynamicParameter1; #endif #if (DYNAMIC_PARAMETERS_MASK & 4) Result.Particle.DynamicParameter2 = Interpolants.DynamicParameter2; #endif #if (DYNAMIC_PARAMETERS_MASK & 8) Result.Particle.DynamicParameter3 = Interpolants.DynamicParameter3; #endif #if USE_PARTICLE_POSITION Result.Particle.TranslatedWorldPositionAndSize = Interpolants.TranslatedWorldPositionAndSize; 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.RelativeTime; #endif #if USE_PARTICLE_SIZE Result.Particle.Size = Interpolants.ParticleSize; #endif #if USE_PARTICLE_RANDOM Result.Particle.Random = Interpolants.ParticleRandom; #else Result.Particle.Random = 0.0f; #endif 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 = Intermediates.Color; Result.TangentToWorld = mul(TangentToLocal, GetLocalToWorld3x3()); Result.Particle.Color = Intermediates.Color; Result.PreSkinnedPosition = LWCToFloat(Intermediates.Current.Position); Result.PreSkinnedNormal = TangentToLocal[2].xyz; #if (DYNAMIC_PARAMETERS_MASK != 0) Result.Particle.DynamicParameterValidMask = NiagaraRibbonVF.MaterialParamValidMask; #endif #if (DYNAMIC_PARAMETERS_MASK & 1) Result.Particle.DynamicParameter = Intermediates.DynamicParameter; #endif #if (DYNAMIC_PARAMETERS_MASK & 2) Result.Particle.DynamicParameter1 = Intermediates.DynamicParameter1; #endif #if (DYNAMIC_PARAMETERS_MASK & 4) Result.Particle.DynamicParameter2 = Intermediates.DynamicParameter2; #endif #if (DYNAMIC_PARAMETERS_MASK & 8) Result.Particle.DynamicParameter3 = Intermediates.DynamicParameter3; #endif #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 USE_PARTICLE_SIZE Result.Particle.Size = Intermediates.ParticleSize; #endif #if USE_PARTICLE_RANDOM Result.Particle.Random = Intermediates.ParticleRandom; #endif Result.PrevFrameLocalToWorld = GetPrimitiveDataFromUniformBuffer().PreviousLocalToWorld; #if NUM_MATERIAL_TEXCOORDS_VERTEX Result.TexCoords[0] = Intermediates.TexCoord.xy; #if NUM_MATERIAL_TEXCOORDS_VERTEX > 1 Result.TexCoords[1] = Intermediates.TexCoord.zw; #endif #endif #if ENABLE_NEW_HLSL_GENERATOR EvaluateVertexMaterialAttributes(Result); #endif Result.LWCData = MakeMaterialLWCData(Result); return Result; } float3 SafeNormalize(float3 V, float3 Default) { const float len2 = dot(V, V); if ( len2 > 0.0f ) { return V * rsqrt(len2); } return Default; } float4 GetDirectionAndMagnitude(float3 V) { const float MagSquared = dot(V, V); if (MagSquared > 0) { const float MagScale = rsqrt(MagSquared); return float4(MagScale * V, rcp(MagScale)); } return float4(0.0f, 0.0f, 0.0f, 0.0f); } /** derive basis vectors */ float3x3 CalcTangentBasis(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) { return float3x3(Intermediates.Current.TangentX, Intermediates.Current.TangentY, Intermediates.Current.TangentZ); } float GetTangentAndDistanceComponent(int RawParticleID, int ElementIndex) { return NiagaraRibbonVFLooseParameters.TangentsAndDistances[RawParticleID * 4 + ElementIndex]; } // known limitations: // -TangentsAndDistances is from the current frame and if we're generating intermediates for the previous frame for velocity // rendering we won't get the exact same results. Issues around trying to supply a previous version of the TangentsAndDistances // buffer include the fact that the active particles can differ between frames and so we'd also need to supply particle indices // for the previous frames as well FRibbonPositionIntermediates GetRibbonPositionIntermediates( FInterpVtxData InterpVtxData, int PositionDataOffset, int FacingDataOffset, int TwistDataOffset, int WidthDataOffset, float2 SliceVertexNormals, FLWCMatrix LocalToWorld, float3 InvScale) { FRibbonPositionIntermediates Intermediates = (FRibbonPositionIntermediates) 0; BRANCH // Also acts as ISOLATE if (PositionDataOffset != -1) { const float3 Pos0 = GetVec3(PositionDataOffset, InterpVtxData.ParticleId0); const float3 Pos1 = GetVec3(PositionDataOffset, InterpVtxData.ParticleId1); const float4 TangentAndDist0 = float4( GetTangentAndDistanceComponent(InterpVtxData.RawParticleId0, 0), GetTangentAndDistanceComponent(InterpVtxData.RawParticleId0, 1), GetTangentAndDistanceComponent(InterpVtxData.RawParticleId0, 2), GetTangentAndDistanceComponent(InterpVtxData.RawParticleId0, 3)); const float4 TangentAndDist1 = float4( GetTangentAndDistanceComponent(InterpVtxData.RawParticleId1, 0), GetTangentAndDistanceComponent(InterpVtxData.RawParticleId1, 1), GetTangentAndDistanceComponent(InterpVtxData.RawParticleId1, 2), GetTangentAndDistanceComponent(InterpVtxData.RawParticleId1, 3)); const float3 LocalPosition = CubicInterp(Pos0, TangentAndDist0.xyz, Pos1, TangentAndDist1.xyz, InterpVtxData.Alpha, TangentAndDist1.w - TangentAndDist0.w); Intermediates.LocalPosition = LocalPosition; Intermediates.Position = TransformPosition(LocalToWorld, LocalPosition); Intermediates.Direction = SafeNormalize(TransformVector(LocalToWorld, InvScale, lerp(TangentAndDist0.xyz, TangentAndDist1.xyz, InterpVtxData.Alpha)), float3(0, 0, 0)); Intermediates.DistanceOnSegment = lerp(TangentAndDist0.w, TangentAndDist1.w, InterpVtxData.Alpha); } float3 Facing; BRANCH if(FacingDataOffset != -1) { const float3 Facing0 = GetVec3(FacingDataOffset, InterpVtxData.ParticleId0); const float3 Facing1 = GetVec3(FacingDataOffset, InterpVtxData.ParticleId1); Facing = TransformVector(LocalToWorld, InvScale, lerp(Facing0, Facing1, InterpVtxData.Alpha)); } else { Facing = LWCToFloat(LWCSubtract(ResolvedView.TileOffset.WorldCameraOrigin, Intermediates.Position)); } Facing = SafeNormalize(Facing, float3(0, 0, 0)); float3 Right = cross(Facing, Intermediates.Direction); Right = SafeNormalize(Right, float3(0, 0, 0)); if (NiagaraRibbonVFLooseParameters.FacingMode == 2 /* CustomSide */) { // Swap axis const float3 Temp = Right; Right = -Facing; Facing = Temp; } BRANCH if (TwistDataOffset != -1) { const float Twist0 = GetFloat(TwistDataOffset, InterpVtxData.ParticleId0); const float Twist1 = GetFloat(TwistDataOffset, InterpVtxData.ParticleId1); float SinTwist, CosTwist; sincos(lerp(Twist0, Twist1, InterpVtxData.Alpha) /* + ResolvedView.RealTime * .5 */, SinTwist, CosTwist); // Apply a rotation matrix to up and right. float3 OriginalRight = Right; // Back it up for the transform. Right = CosTwist * OriginalRight + SinTwist * Facing; Facing = -SinTwist * OriginalRight + CosTwist * Facing; } Intermediates.TangentY = Intermediates.Direction; Intermediates.TangentX = Right; Intermediates.GeoUp = cross(Intermediates.TangentY, Intermediates.TangentX); Intermediates.GeoRight = Intermediates.TangentX; Intermediates.TangentZ = NiagaraRibbonVFLooseParameters.UseGeometryNormals ? Intermediates.GeoUp : Facing; Intermediates.TangentZ = SafeNormalize(Intermediates.TangentZ * SliceVertexNormals.y + Intermediates.TangentX * SliceVertexNormals.x, float3(0, 0, 1)); // Flip the normal only for multiplane shape if the normal is looking away from the camera. // This lets us still render as two sided, but at least gets the normals corrected so the plane faces the camera. float3 ViewNormal = SafeNormalize(LWCToFloat(LWCSubtract(ResolvedView.TileOffset.WorldCameraOrigin, Intermediates.Position)), float3(0,0,0)); Intermediates.TangentZ *= (NiagaraRibbonVF.ShouldFlipNormalToView > 0 && dot(ViewNormal, Intermediates.TangentZ) < 0) ? -1 : 1; Intermediates.TangentX = cross(Intermediates.TangentZ, Intermediates.TangentY); // Tangent X/Y are incorrectly swapped above detangling that results in 2 paths so less messy to swap here float3 t = Intermediates.TangentX; Intermediates.TangentX = Intermediates.TangentY; Intermediates.TangentY = t; // Overrides (FVertexFactoryIntermediates)0; Intermediates.RibbonWidth = 1.0f; FLATTEN if (WidthDataOffset != -1) { Intermediates.RibbonWidth = lerp(GetFloat(WidthDataOffset, InterpVtxData.ParticleId0), GetFloat(WidthDataOffset, InterpVtxData.ParticleId1), InterpVtxData.Alpha); } return Intermediates; } FVertexFactoryIntermediates GetVertexFactoryIntermediates(FVertexFactoryInput Input) { FVertexFactoryIntermediates Intermediates = (FVertexFactoryIntermediates)0; FInterpVtxData InterpVtxData = (FInterpVtxData)0; FSegmentData SegmentData = (FSegmentData)0; GetInterpVtxAndRibbonDataData(Input, InterpVtxData, SegmentData); Intermediates.SceneData = VF_GPUSCENE_GET_INTERMEDIATES(Input); const float2 SliceVertexNormals = UnpackParticleVertexNormal(Input.InterpVtxId); FPrimitiveSceneData PrimitiveData = GetPrimitiveDataFromUniformBuffer(); FLWCMatrix LocalToWorld = DFFastToTileOffset(GetPrimitiveDataFromUniformBuffer().LocalToWorld); float3 InvScale = PrimitiveData.InvNonUniformScale; Intermediates.Current = GetRibbonPositionIntermediates( InterpVtxData, NiagaraRibbonVF.PositionDataOffset, NiagaraRibbonVF.FacingDataOffset, NiagaraRibbonVF.TwistDataOffset, NiagaraRibbonVF.WidthDataOffset, SliceVertexNormals, LocalToWorld, InvScale); BRANCH if(NiagaraRibbonVF.VelocityDataOffset != -1) { const float3 Velocity0 = GetVec3(NiagaraRibbonVF.VelocityDataOffset, InterpVtxData.ParticleId0); const float3 Velocity1 = GetVec3(NiagaraRibbonVF.VelocityDataOffset, InterpVtxData.ParticleId1); const float3 Velocity = TransformVector(LocalToWorld, InvScale, lerp(Velocity0, Velocity1, InterpVtxData.Alpha)); Intermediates.ParticleVelocity = GetDirectionAndMagnitude(Velocity); } BRANCH if (NiagaraRibbonVFLooseParameters.NeedsPreciseMotionVectors) { FLWCMatrix PreviousLocalToWorld = DFFastToTileOffset(PrimitiveData.PreviousLocalToWorld); // we don't cache the InvScale so we generate it here float3x3 PreviousTransform = LWCToFloat3x3(PreviousLocalToWorld); const float3 PreviousXAxis = float3(PreviousTransform[0][0], PreviousTransform[0][1], PreviousTransform[0][2]); const float3 PreviousYAxis = float3(PreviousTransform[1][0], PreviousTransform[1][1], PreviousTransform[1][2]); const float3 PreviousZAxis = float3(PreviousTransform[2][0], PreviousTransform[2][1], PreviousTransform[2][2]); float3 PreviousInvScale = float3( rsqrt(max(0.0001f, dot(PreviousXAxis, PreviousXAxis))), rsqrt(max(0.0001f, dot(PreviousYAxis, PreviousYAxis))), rsqrt(max(0.0001f, dot(PreviousZAxis, PreviousZAxis))) ); Intermediates.Previous = GetRibbonPositionIntermediates( InterpVtxData, NiagaraRibbonVF.PrevPositionDataOffset, NiagaraRibbonVF.PrevFacingDataOffset, NiagaraRibbonVF.PrevTwistDataOffset, NiagaraRibbonVF.PrevWidthDataOffset, SliceVertexNormals, PreviousLocalToWorld, PreviousInvScale); } else { const float3 VelocityOffset = TransformVector(LocalToWorld, InvScale, -Intermediates.ParticleVelocity.xyz * (Intermediates.ParticleVelocity.w * NiagaraRibbonVF.DeltaSeconds)); Intermediates.Previous = Intermediates.Current; Intermediates.Previous.Position = LWCAdd(Intermediates.Current.Position, LWCPromote(VelocityOffset)); } // Overrides (FVertexFactoryIntermediates)0; Intermediates.Color = float4(1.0f, 1.0f, 1.0f, 1.0f); if (NiagaraRibbonVF.ColorDataOffset != -1) { const float4 Color0 = GetVec4(NiagaraRibbonVF.ColorDataOffset, InterpVtxData.ParticleId0); const float4 Color1 = GetVec4(NiagaraRibbonVF.ColorDataOffset, InterpVtxData.ParticleId1); Intermediates.Color = lerp(Color0, Color1, InterpVtxData.Alpha); } #if (DYNAMIC_PARAMETERS_MASK & 1) // Overrides (FVertexFactoryIntermediates)0; Intermediates.DynamicParameter = float4(1.0f, 1.0f, 1.0f, 1.0f); FLATTEN if (NiagaraRibbonVF.MaterialParamDataOffset != -1) { Intermediates.DynamicParameter = lerp(GetVec4(NiagaraRibbonVF.MaterialParamDataOffset, InterpVtxData.ParticleId0), GetVec4(NiagaraRibbonVF.MaterialParamDataOffset, InterpVtxData.ParticleId1), InterpVtxData.Alpha); } #endif #if (DYNAMIC_PARAMETERS_MASK & 2) // Overrides (FVertexFactoryIntermediates)0; Intermediates.DynamicParameter1 = float4(1.0f, 1.0f, 1.0f, 1.0f); FLATTEN if (NiagaraRibbonVF.MaterialParam1DataOffset != -1) { Intermediates.DynamicParameter1 = lerp(GetVec4(NiagaraRibbonVF.MaterialParam1DataOffset, InterpVtxData.ParticleId0), GetVec4(NiagaraRibbonVF.MaterialParam1DataOffset, InterpVtxData.ParticleId1), InterpVtxData.Alpha); } #endif #if (DYNAMIC_PARAMETERS_MASK & 4) // Overrides (FVertexFactoryIntermediates)0; Intermediates.DynamicParameter2 = float4(1.0f, 1.0f, 1.0f, 1.0f); FLATTEN if (NiagaraRibbonVF.MaterialParam2DataOffset != -1) { Intermediates.DynamicParameter2 = lerp(GetVec4(NiagaraRibbonVF.MaterialParam2DataOffset, InterpVtxData.ParticleId0), GetVec4(NiagaraRibbonVF.MaterialParam2DataOffset, InterpVtxData.ParticleId1), InterpVtxData.Alpha); } #endif #if (DYNAMIC_PARAMETERS_MASK & 8) // Overrides (FVertexFactoryIntermediates)0; Intermediates.DynamicParameter3 = float4(1.0f, 1.0f, 1.0f, 1.0f); FLATTEN if (NiagaraRibbonVF.MaterialParam3DataOffset != -1) { Intermediates.DynamicParameter3 = lerp(GetVec4(NiagaraRibbonVF.MaterialParam3DataOffset, InterpVtxData.ParticleId0), GetVec4(NiagaraRibbonVF.MaterialParam3DataOffset, InterpVtxData.ParticleId1), InterpVtxData.Alpha); } #endif float U0ForSegment = 0; BRANCH if (NiagaraRibbonVF.U0OverrideDataOffset != -1) { U0ForSegment = lerp(GetFloat(NiagaraRibbonVF.U0OverrideDataOffset, InterpVtxData.ParticleId0), GetFloat(NiagaraRibbonVF.U0OverrideDataOffset, InterpVtxData.ParticleId1), InterpVtxData.Alpha); } else if (NiagaraRibbonVF.U0DistributionMode == U_DISTRIBUTION_SCALED_UNIFORMLY) { U0ForSegment = ((float) (InterpVtxData.RawParticleId0 - SegmentData.StartParticleId) + InterpVtxData.Alpha) * SegmentData.U0DistributionScaler; } else if (NiagaraRibbonVF.U0DistributionMode == U_DISTRIBUTION_SCALED_USING_RIBBON_LENGTH || NiagaraRibbonVF.U0DistributionMode == U_DISTRIBUTION_TILED_OVER_RIBBON_LENGTH) { U0ForSegment = Intermediates.Current.DistanceOnSegment * SegmentData.U0DistributionScaler; } else if (NiagaraRibbonVF.DistanceFromStartOffset != -1 && NiagaraRibbonVF.U0DistributionMode == U_DISTRIBUTION_TILED_FROM_START_OVER_RIBBON_LENGTH) { U0ForSegment = lerp(GetFloat(NiagaraRibbonVF.DistanceFromStartOffset, InterpVtxData.ParticleId0), GetFloat(NiagaraRibbonVF.DistanceFromStartOffset, InterpVtxData.ParticleId1), InterpVtxData.Alpha) * SegmentData.U0DistributionScaler; } float U1ForSegment = 0; BRANCH if(NiagaraRibbonVF.U1OverrideDataOffset != -1) { U1ForSegment = lerp(GetFloat(NiagaraRibbonVF.U1OverrideDataOffset, InterpVtxData.ParticleId0), GetFloat(NiagaraRibbonVF.U1OverrideDataOffset, InterpVtxData.ParticleId1), InterpVtxData.Alpha); } else if (NiagaraRibbonVF.U1DistributionMode == U_DISTRIBUTION_SCALED_UNIFORMLY) { U1ForSegment = ((float)(InterpVtxData.RawParticleId0 - SegmentData.StartParticleId) + InterpVtxData.Alpha) * SegmentData.U1DistributionScaler; } else if (NiagaraRibbonVF.U1DistributionMode == U_DISTRIBUTION_SCALED_USING_RIBBON_LENGTH || NiagaraRibbonVF.U1DistributionMode == U_DISTRIBUTION_TILED_OVER_RIBBON_LENGTH) { U1ForSegment = Intermediates.Current.DistanceOnSegment * SegmentData.U1DistributionScaler; } else if (NiagaraRibbonVF.DistanceFromStartOffset != -1 && NiagaraRibbonVF.U1DistributionMode == U_DISTRIBUTION_TILED_FROM_START_OVER_RIBBON_LENGTH) { U1ForSegment = lerp(GetFloat(NiagaraRibbonVF.DistanceFromStartOffset, InterpVtxData.ParticleId0), GetFloat(NiagaraRibbonVF.DistanceFromStartOffset, InterpVtxData.ParticleId1), InterpVtxData.Alpha) * SegmentData.U1DistributionScaler; } float V0ForSegment; BRANCH if (NiagaraRibbonVF.V0RangeOverrideDataOffset != -1) { float2 V0RangeForSegment = lerp(GetVec2(NiagaraRibbonVF.V0RangeOverrideDataOffset, InterpVtxData.ParticleId0), GetVec2(NiagaraRibbonVF.V0RangeOverrideDataOffset, InterpVtxData.ParticleId1), InterpVtxData.Alpha); V0ForSegment = (1 - UnpackParticleVertexTextureV(Input.InterpVtxId) * V0RangeForSegment.x) + (UnpackParticleVertexTextureV(Input.InterpVtxId) * V0RangeForSegment.y); } else { V0ForSegment = UnpackParticleVertexTextureV(Input.InterpVtxId); } float V1ForSegment; BRANCH if (NiagaraRibbonVF.V1RangeOverrideDataOffset != -1) { float2 V1RangeForSegment = lerp(GetVec2(NiagaraRibbonVF.V1RangeOverrideDataOffset, InterpVtxData.ParticleId0), GetVec2(NiagaraRibbonVF.V1RangeOverrideDataOffset, InterpVtxData.ParticleId1), InterpVtxData.Alpha); V1ForSegment = (1 - UnpackParticleVertexTextureV(Input.InterpVtxId) * V1RangeForSegment.x) + (UnpackParticleVertexTextureV(Input.InterpVtxId) * V1RangeForSegment.y); } else { V1ForSegment = UnpackParticleVertexTextureV(Input.InterpVtxId); } float2 UV0ForSegment = float2(U0ForSegment, V0ForSegment); float2 UV1ForSegment = float2(U1ForSegment, V1ForSegment); Intermediates.TexCoord.xy = UV0ForSegment * SegmentData.UV0Scale + SegmentData.UV0Offset; Intermediates.TexCoord.zw = UV1ForSegment * SegmentData.UV1Scale + SegmentData.UV1Offset; #if USE_PARTICLE_POSITION Intermediates.TranslatedWorldPositionAndSize = float4(LWCToFloat(LWCAdd(Intermediates.Current.Position, ResolvedView.TileOffset.PreViewTranslation)), .5 * Intermediates.Current.RibbonWidth); #endif #if USE_PARTICLE_TIME FLATTEN if (NiagaraRibbonVF.NormalizedAgeDataOffset != -1) { Intermediates.RelativeTime = lerp(GetFloat(NiagaraRibbonVF.NormalizedAgeDataOffset, InterpVtxData.ParticleId0), GetFloat(NiagaraRibbonVF.NormalizedAgeDataOffset, InterpVtxData.ParticleId1), InterpVtxData.Alpha); } #endif #if USE_PARTICLE_SIZE Intermediates.ParticleSize = Intermediates.Current.RibbonWidth.xx; #endif #if USE_PARTICLE_RANDOM FLATTEN if (NiagaraRibbonVF.MaterialRandomDataOffset != -1) { Intermediates.ParticleRandom = lerp(GetFloat(NiagaraRibbonVF.MaterialRandomDataOffset, InterpVtxData.ParticleId0), GetFloat(NiagaraRibbonVF.MaterialRandomDataOffset, InterpVtxData.ParticleId1), InterpVtxData.Alpha); } #endif return Intermediates; } float3 ConstructWorldPosition(FVertexFactoryInput Input, FRibbonPositionIntermediates Intermediates, FLWCVector3 PreViewTranslation) { const float2 SliceVertexPositions = UnpackParticleVertexPosition(Input.InterpVtxId); const float3 Offset = ((Intermediates.GeoRight * SliceVertexPositions.xxx) + (Intermediates.GeoUp * SliceVertexPositions.yyy)) * Intermediates.RibbonWidth.xxx; const FLWCVector3 Pos = LWCAdd(LWCAdd(Intermediates.Position, LWCPromote(Offset)), PreViewTranslation); // Intermediates are already in world space, only need to account for the translation around view origin. return LWCToFloat(Pos); } float4 VertexFactoryGetWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) { return float4(ConstructWorldPosition(Input, Intermediates.Current, ResolvedView.TileOffset.PreViewTranslation), 1); } float3 VertexFactoryGetInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) { return Intermediates.Current.LocalPosition; } float4 VertexFactoryGetPreviousWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) { return float4(ConstructWorldPosition(Input, Intermediates.Previous, ResolvedView.TileOffset.PrevPreViewTranslation), 1); } // local position relative to instance float3 VertexFactoryGetPreviousInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) { return Intermediates.Previous.LocalPosition; } float3 VertexFactoryGetWorldNormal(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) { return Intermediates.Current.TangentZ; } float4 VertexFactoryGetRasterizedWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float4 InWorldPosition) { return InWorldPosition; } float3 VertexFactoryGetPositionForVertexLighting(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float3 TranslatedWorldPosition) { return TranslatedWorldPosition; } FVertexFactoryInterpolantsVSToPS VertexFactoryGetInterpolantsVSToPS(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, FMaterialVertexParameters VertexParameters) { FVertexFactoryInterpolantsVSToPS Interpolants; 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 NEEDS_PARTICLE_COLOR Interpolants.Color = Intermediates.Color; #endif #if (DYNAMIC_PARAMETERS_MASK & 1) Interpolants.DynamicParameter = Intermediates.DynamicParameter; #endif #if (DYNAMIC_PARAMETERS_MASK & 2) Interpolants.DynamicParameter1 = Intermediates.DynamicParameter1; #endif #if (DYNAMIC_PARAMETERS_MASK & 4) Interpolants.DynamicParameter2 = Intermediates.DynamicParameter2; #endif #if (DYNAMIC_PARAMETERS_MASK & 8) Interpolants.DynamicParameter3 = Intermediates.DynamicParameter3; #endif #if USE_PARTICLE_POSITION Interpolants.TranslatedWorldPositionAndSize = Intermediates.TranslatedWorldPositionAndSize; #endif #if USE_PARTICLE_VELOCITY Interpolants.ParticleVelocity = Intermediates.ParticleVelocity; #endif #if USE_PARTICLE_TIME Interpolants.RelativeTime = Intermediates.RelativeTime; #endif #if USE_PARTICLE_SIZE Interpolants.ParticleSize = Intermediates.ParticleSize; #endif #if USE_PARTICLE_RANDOM Interpolants.ParticleRandom = Intermediates.ParticleRandom; #endif float3x3 TangentToWorld = CalcTangentBasis(Input, Intermediates); Interpolants.TangentToWorld0.xyz = TangentToWorld[0]; Interpolants.TangentToWorld0.w = 0; // GetNiagaraParticleTangents() technically makes the determinant to be always 1. Interpolants.TangentToWorld2 = float4(TangentToWorld[2], 1 /*sign(determinant(TangentToWorld))*/); return Interpolants; } /** * 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 CalcTangentBasis(Input, Intermediates); } float4 VertexFactoryGetTranslatedPrimitiveVolumeBounds(FVertexFactoryInterpolantsVSToPS Interpolants) { return 0; } uint VertexFactoryGetPrimitiveId(FVertexFactoryInterpolantsVSToPS Interpolants) { return 0; } // RHI_RAYTRACING #if COMPUTESHADER FVertexFactoryInput LoadVertexFactoryInputForDynamicUpdate(uint TriangleIndex, int VertexIndex, uint PrimitiveId, uint DrawInstanceId) { FVertexFactoryInput Input; Input.InterpVtxId = TriangleIndex * 3 + VertexIndex; if ( NiagaraRibbonVFLooseParameters.UseIndexBufferForRayTracing != 0 ) { Input.InterpVtxId = NiagaraRibbonVFLooseParameters.IndexBuffer[Input.InterpVtxId]; } #if FEATURE_LEVEL >= FEATURE_LEVEL_SM4 && ONEPASS_POINTLIGHT_SHADOW && USING_VERTEX_SHADER_LAYER Input.InstanceId = 0; #endif return Input; } uint GetNumRayTracingDynamicMeshVerticesIndirect() { return NiagaraRibbonVFLooseParameters.IndirectDrawOutput[NiagaraRibbonVFLooseParameters.IndirectDrawOutputOffset + INDEX_GEN_DRAW_INDIRECT_OFFSET + 0]; } #endif #if RAYHITGROUPSHADER FVertexFactoryInput LoadVertexFactoryInputForHGS(uint TriangleIndex, int VertexIndex) { FVertexFactoryInput Input; FTriangleBaseAttributes Tri = LoadTriangleBaseAttributes(TriangleIndex); uint VertexId = Tri.Indices[VertexIndex]; Input.InterpVtxId = VertexId; #if FEATURE_LEVEL >= FEATURE_LEVEL_SM4 && ONEPASS_POINTLIGHT_SHADOW && USING_VERTEX_SHADER_LAYER Input.InstanceId = 0; #endif return Input; } uint GetNumRayTracingDynamicMeshVerticesIndirect() { return NiagaraRibbonVFLooseParameters.IndirectDrawOutput[NiagaraRibbonVFLooseParameters.IndirectDrawOutputOffset + INDEX_GEN_DRAW_INDIRECT_OFFSET + 0]; } #endif #if RAYHITGROUPSHADER struct FVertexFactoryRayTracingInterpolants { FVertexFactoryInterpolantsVSToPS InterpolantsVSToPS; }; FVertexFactoryRayTracingInterpolants VertexFactoryGetRayTracingInterpolants(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, FMaterialVertexParameters VertexParameters) { FVertexFactoryRayTracingInterpolants Interpolants; Interpolants.InterpolantsVSToPS = VertexFactoryGetInterpolantsVSToPS(Input, Intermediates, VertexParameters); return Interpolants; } float2 VertexFactoryGetRayTracingTextureCoordinate( FVertexFactoryRayTracingInterpolants Interpolants ) { #if NUM_TEX_COORD_INTERPOLATORS return Interpolants.InterpolantsVSToPS.TexCoords[0].xy; #else // #if NUM_MATERIAL_TEXCOORDS return float2(0,0); #endif // #if NUM_MATERIAL_TEXCOORDS } FVertexFactoryRayTracingInterpolants VertexFactoryInterpolate(FVertexFactoryRayTracingInterpolants a, float aInterp, FVertexFactoryRayTracingInterpolants b, float bInterp) { FVertexFactoryRayTracingInterpolants O; #if NUM_TEX_COORD_INTERPOLATORS UNROLL for(int tc = 0; tc < (NUM_TEX_COORD_INTERPOLATORS+1)/2; ++tc) { INTERPOLATE_MEMBER(InterpolantsVSToPS.TexCoords[tc]); } #endif INTERPOLATE_MEMBER(InterpolantsVSToPS.TangentToWorld0); INTERPOLATE_MEMBER(InterpolantsVSToPS.TangentToWorld2); #if NEEDS_PARTICLE_COLOR INTERPOLATE_MEMBER(InterpolantsVSToPS.Color); #endif #if (DYNAMIC_PARAMETERS_MASK & 1) INTERPOLATE_MEMBER(InterpolantsVSToPS.DynamicParameter); #endif #if (DYNAMIC_PARAMETERS_MASK & 2) INTERPOLATE_MEMBER(InterpolantsVSToPS.DynamicParameter1); #endif #if (DYNAMIC_PARAMETERS_MASK & 4) INTERPOLATE_MEMBER(InterpolantsVSToPS.DynamicParameter2); #endif #if (DYNAMIC_PARAMETERS_MASK & 8) INTERPOLATE_MEMBER(InterpolantsVSToPS.DynamicParameter3); #endif #if USE_PARTICLE_POSITION INTERPOLATE_MEMBER(InterpolantsVSToPS.TranslatedWorldPositionAndSize); #endif #if USE_PARTICLE_VELOCITY INTERPOLATE_MEMBER(InterpolantsVSToPS.ParticleVelocity); #endif #if USE_PARTICLE_TIME INTERPOLATE_MEMBER(InterpolantsVSToPS.RelativeTime); #endif #if USE_PARTICLE_SIZE INTERPOLATE_MEMBER(InterpolantsVSToPS.ParticleSize); #endif #if USE_PARTICLE_RANDOM INTERPOLATE_MEMBER(InterpolantsVSToPS.ParticleRandom); #endif return O; } FVertexFactoryInterpolantsVSToPS VertexFactoryAssignInterpolants(FVertexFactoryRayTracingInterpolants Input) { return Input.InterpolantsVSToPS; } #endif #if WITH_NIAGARA_VERTEX_FACTORY_EXPORT FVertexFactoryInput NiagaraVertexFactoryExport_InitVertexFactoryInput(uint TriangleIndex, int VertexIndex, uint PrimitiveId, uint DrawInstanceId) { FVertexFactoryInput Input; Input.InterpVtxId = TriangleIndex * 3 + VertexIndex; Input.InterpVtxId = NiagaraRibbonVFLooseParameters.IndexBuffer[Input.InterpVtxId]; #if FEATURE_LEVEL >= FEATURE_LEVEL_SM4 && ONEPASS_POINTLIGHT_SHADOW && USING_VERTEX_SHADER_LAYER Input.InstanceId = 0; #endif return Input; } uint NiagaraVertexFactoryExport_GetIndirectVertexCount() { return NiagaraRibbonVFLooseParameters.IndirectDrawOutput[NiagaraRibbonVFLooseParameters.IndirectDrawOutputOffset + INDEX_GEN_DRAW_INDIRECT_OFFSET + 0]; } float3 NiagaraVertexFactoryExport_GetPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) { return ConstructWorldPosition(Input, Intermediates.Current, LWCPromote(float3(0,0,0))); } float4 NiagaraVertexFactoryExport_GetColor(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) { #if NEEDS_PARTICLE_COLOR return Intermediates.Color; #endif return float4(1,1,1,1); } float3x3 NiagaraVertexFactoryExport_GetTangentBasis(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates) { return float3x3(Intermediates.Current.TangentX, Intermediates.Current.TangentY, Intermediates.Current.TangentZ); } float2 NiagaraVertexFactoryExport_GetTexCoord(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, uint CoordIndex) { switch (CoordIndex) { #if NUM_MATERIAL_TEXCOORDS_VERTEX > 0 case 0: return Intermediates.TexCoord.xy; #endif #if NUM_MATERIAL_TEXCOORDS_VERTEX > 1 case 1: return Intermediates.TexCoord.zw; #endif default: return float2(0,0); } } #endif //WITH_NIAGARA_VERTEX_FACTORY_EXPORT #include "/Engine/Private/VertexFactoryDefaultInterface.ush"