Files
UnrealEngine/Engine/Shaders/Private/ParticleGPUSpriteVertexFactory.ush
2025-05-18 13:04:45 +08:00

776 lines
28 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
ParticleGPUSpriteVertexFactory.usf: Vertex factory for GPU simulated particles.
=============================================================================*/
#define PARTICLE_SPRITE_FACTORY 1
#include "VertexFactoryCommon.ush"
#include "ParticleVertexFactoryCommon.ush"
#include "ParticleSimulationCommon.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_SPHERICAL_NORMALS (GENERATE_SPHERICAL_PARTICLE_NORMALS && USE_PARTICLE_POSITION)
#define USE_PARTICLE_SIZE (NEEDS_PARTICLE_SIZE)
#define USE_PARTICLE_SPRITE_ROTATION (NEEDS_PARTICLE_SPRITE_ROTATION)
/** Buffer containing particle indices. */
Buffer<float4> ParticleIndices;
/** Offset in to the particle indices buffer. */
uint ParticleIndicesOffset;
float3 TilePageScale;
/** Texture containing positions for all particles. */
Texture2D PositionTexture;
SamplerState PositionTextureSampler;
Texture2D VelocityTexture;
SamplerState VelocityTextureSampler;
Texture2D AttributesTexture;
SamplerState AttributesTextureSampler;
Texture2D CurveTexture;
SamplerState CurveTextureSampler;
#undef GetInstanceIdFromVF
#define GetInstanceIdFromVF(VFInput) (VFInput.InstanceId)
/**
* Vertex attributes to fetch.
*/
struct FVertexFactoryInput
{
/** Unique vertex ID. */
uint VertexId : SV_VertexID;
/** Unique instance ID. */
uint InstanceId : SV_InstanceID;
/** Per-vertex: texture coordinate. */
float2 TexCoord : ATTRIBUTE0;
VF_MOBILE_MULTI_VIEW_DECLARE_INPUT_BLOCK()
};
/**
* Attributes to interpolate from the vertex shader to the pixel shader.
*/
struct FVertexFactoryInterpolantsVSToPS
{
#if NUM_TEX_COORD_INTERPOLATORS
float4 TexCoords[(NUM_TEX_COORD_INTERPOLATORS + 1) / 2] : TEXCOORD0;
#endif
#if USE_PARTICLE_SUBUVS
float4 ParticleSubUVs : PARTICLE_SUBUVS;
#endif
#if NEEDS_PARTICLE_RANDOM
/** X: SubImageLerp Y:RelativeTime Z:MotionBlurFade W:Random */
float4 Misc : TEXCOORD4;
#elif USE_PARTICLE_SUBUVS || USE_PARTICLE_TIME || USES_PARTICLE_MOTION_BLUR
/** X: SubImageLerp Y:RelativeTime Z:MotionBlurFade */
float3 Misc : TEXCOORD4;
#endif
#if NEEDS_PARTICLE_COLOR
/** Sprite color. */
float4 Color : TEXCOORD5;
#endif
#if USE_PARTICLE_POSITION
/** Particle center in translated world space and radius */
float4 ParticleTranslatedWorldPositionAndSize : PARTICLE_POSITION;
#endif
/** The velocity of the particle, XYZ: direction, W: speed. */
#if USE_PARTICLE_VELOCITY
float4 ParticleVelocity : PARTICLE_VELOCITY;
#endif
#if USE_PARTICLE_LIGHTING_OFFSET
float3 LightingPositionOffset : PARTICLE_LIGHTING_OFFSET;
#endif
#if !USE_SPHERICAL_NORMALS
float4 TangentToWorld0 : TANGENTX;
float4 TangentToWorld2 : TANGENTZ;
#endif
/** The size of the particle. */
#if USE_PARTICLE_SIZE
float2 ParticleSize : PARTICLE_SIZE;
#endif
#if USE_PARTICLE_SPRITE_ROTATION
float ParticleSpriteRotation : PARTICLE_SPRITE_ROTATION;
#endif
};
/**
* Intermediate values computed in the vertex shader.
*/
struct FVertexFactoryIntermediates
{
/** The position of the particle in translated world space. */
float3 ParticleTranslatedWorldPosition;
/** The position of the vertex in translated world space. */
float3 VertexWorldPosition;
/** The position of the vertex in local instance space. */
float3 PositionInstanceSpace;
#if USE_PARTICLE_LIGHTING_OFFSET
float3 LightingPositionOffset;
#endif
/** The texture coordinate at this vertex. */
float4 TexCoord;
#if NUM_MATERIAL_TEXCOORDS_VERTEX >= 3 //Unflipped UVs in texcoords 3 and 4
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 velocity of the particle, XYZ: direction, W: speed. */
float4 ParticleVelocity;
/** The sub-image lerp. */
float SubImageLerp;
/** Relative time. */
float RelativeTime;
/** Amount to fade the sprite due to camera motion blur. */
float MotionBlurFade;
/** Random value per particle [0-1]. */
float Random;
/** Radius of the particle when represented by a sphere. */
float ParticleRadius;
/** Size of the particle. */
float2 ParticleSize;
#if USE_PARTICLE_SPRITE_ROTATION
float ParticleSpriteRotation;
#endif
/** Cached primitive and instance data */
FSceneDataIntermediates SceneData;
};
FPrimitiveSceneData GetPrimitiveData(FVertexFactoryIntermediates Intermediates)
{
return Intermediates.SceneData.Primitive;
}
float3 SafeNormalize(float3 V)
{
return V / sqrt(max(dot(V,V),0.01));
}
/**
* Compute the tangents for a sprite given a rotation.
* @param OutUp - The tangent vector pointing up in screen space.
* @param OutRight - The tangent vector pointing right in screen space.
* @param Rotation - The rotation of the sprite.
* @param ParticleTranslatedWorldPosition - Translated world position of the particle.
* @param ParticleWorldDirection - World space direction in which the particle is traveling.
*/
void GetSpriteTangents(
out float3 OutUp,
out float3 OutRight,
float Rotation,
float3 ParticleTranslatedWorldPosition,
float3 ParticleWorldDirection )
{
// Select camera up/right vectors.
float3 ResolvedViewRight = lerp(ResolvedView.ViewRight, ResolvedView.HMDViewNoRollRight, EmitterUniforms.RemoveHMDRoll);
float3 ResolvedViewUp = lerp(ResolvedView.ViewUp, ResolvedView.HMDViewNoRollUp, EmitterUniforms.RemoveHMDRoll);
float3 CameraRight = lerp(ResolvedViewRight, EmitterDynamicUniforms.AxisLockRight.xyz, EmitterDynamicUniforms.AxisLockRight.w);
float3 CameraUp = lerp(-ResolvedViewUp, EmitterDynamicUniforms.AxisLockUp.xyz, EmitterDynamicUniforms.AxisLockUp.w);
// Determine the vector from the particle to the camera and the particle's movement direction.
float3 CameraDirection = -GetCameraVectorFromTranslatedWorldPosition(ResolvedView, ParticleTranslatedWorldPosition.xyz);
float3 RightVector = CameraRight.xyz;
float3 UpVector = CameraUp.xyz;
BRANCH
if (EmitterUniforms.CameraFacingBlend.x > 0.f)
{
// Blend between PSA_FacingCamera and PSA_Square over distance
float CameraDistanceSq = GetDistanceToCameraFromViewVectorSqr(ResolvedView.TranslatedWorldCameraOrigin.xyz - ParticleTranslatedWorldPosition.xyz);
float AlignmentMode = saturate(CameraDistanceSq * EmitterUniforms.CameraFacingBlend.y - EmitterUniforms.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 (EmitterUniforms.TangentSelector.y > 0)
{
// Tangent vectors for PSA_Velocity.
RightVector = SafeNormalize(cross(CameraDirection, ParticleWorldDirection));
UpVector = -ParticleWorldDirection;
}
else if (EmitterUniforms.TangentSelector.z > 0)
{
// Tangent vectors for rotation locked about an axis.
RightVector = EmitterDynamicUniforms.AxisLockRight.xyz;
UpVector = -SafeNormalize(cross(RightVector,CameraDirection));
}
else if (EmitterUniforms.TangentSelector.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
const float SpriteRotation = Rotation + EmitterUniforms.RotationBias;
sincos(SpriteRotation, SinRotation, CosRotation);
// Rotate the sprite to determine final tangents.
OutRight = SinRotation * UpVector + CosRotation * RightVector;
OutUp = CosRotation * UpVector - SinRotation * RightVector;
}
/**
* Computes intermediates for the given vertex.
* @param Input - Vertex attributes.
* @returns the computed intermediate values.
*/
FVertexFactoryIntermediates GetVertexFactoryIntermediates( FVertexFactoryInput Input )
{
// Sample the position and velocity textures to get the current state of the particle.
uint InstanceId = GetInstanceId(Input.InstanceId) * PARTICLES_PER_INSTANCE + Input.VertexId / 4;
float3 ParticleIndex = ParticleIndices[ParticleIndicesOffset + InstanceId].xyz;
ParticleIndex.xy = ParticleIndex.xy * TilePageScale.xy + GetTilePageOffset(ParticleIndex.z, TilePageScale.xy, TilePageScale.z);
float4 PositionSample = Texture2DSampleLevel(PositionTexture, PositionTextureSampler, ParticleIndex.xy, 0);
float4 VelocitySample = Texture2DSampleLevel(VelocityTexture, VelocityTextureSampler, ParticleIndex.xy, 0);
float4 AttributeSample = Texture2DSampleLevel(AttributesTexture, AttributesTextureSampler, ParticleIndex.xy, 0);
// For clarity, store some information in local variables.
const float RelativeTime = PositionSample.w;
const float IsAlive = step( RelativeTime, 1.0f );
// Put velocity in to world space.
float3 ParticleWorldVelocity = mul(VelocitySample.xyz, GetLocalToWorld3x3()).xyz;
// Add a small bias to the direction to prevent issues when velocity is zero.
float3 ParticleDirection = normalize(ParticleWorldVelocity.xyz + float3(0,0,0.0001f));
float ParticleSpeed = length(ParticleWorldVelocity.xyz);
// Sample the color curve.
const float4 InitialColor = float4(1,1,1,1);
float2 ColorCurveTexCoord = EmitterUniforms.ColorCurve.xy +
EmitterUniforms.ColorCurve.zw * RelativeTime;
float4 ColorCurveSample = Texture2DSampleLevel(CurveTexture, CurveTextureSampler, ColorCurveTexCoord, 0 );
float4 ColorScale = ColorCurveSample * EmitterUniforms.ColorScale + EmitterUniforms.ColorBias;
float4 Color = ColorScale * InitialColor * EmitterDynamicUniforms.DynamicColor;
// Sample the curve containing misc. attributes.
float2 MiscCurveTexCoord = EmitterUniforms.MiscCurve.xy +
EmitterUniforms.MiscCurve.zw * RelativeTime;
float4 MiscCurveSample = Texture2DSampleLevel(CurveTexture, CurveTextureSampler, MiscCurveTexCoord, 0 );
float4 MiscCurve = MiscCurveSample * EmitterUniforms.MiscScale + EmitterUniforms.MiscBias;
// Compute the size of the sprite. Note it is (0,0) if the sprite is dead.
// Reconstruct the real size after we've encoded the UV flip mode as sizes > 0.5;
float2 UVFlipOffset = float2( AttributeSample.x < 0.5 ? 0.0f : -0.5, AttributeSample.y < 0.5 ? 0.0f : -0.5);
float2 InitialSize = (AttributeSample.xy + UVFlipOffset) * float2(2.0,2.0);
float2 SizeScale = MiscCurve.xy * EmitterDynamicUniforms.LocalToWorldScale;
//Explicit ordering of clamp's min max to bypass compiler diff on PS4.
float2 SizeScaleBySpeed = max(EmitterUniforms.SizeBySpeed.xy * ParticleSpeed,float2(1,1));
SizeScaleBySpeed = min(SizeScaleBySpeed, EmitterUniforms.SizeBySpeed.zw);
float2 Size = InitialSize * SizeScale * SizeScaleBySpeed * IsAlive.xx;
float2 FlippedUV = Input.TexCoord.xy;
FlippedUV.x = UVFlipOffset.x == 0.0 ? 1.0 - FlippedUV.x : FlippedUV.x;
FlippedUV.y = UVFlipOffset.y == 0.0 ? 1.0 - FlippedUV.y : FlippedUV.y;
// SubUV.
float SubImageIndex = MiscCurve.z;
float SubImageLerp = frac(SubImageIndex);
float SubImageA = SubImageIndex - SubImageLerp;
float SubImageB = SubImageA + 1;
float SubImageAH = fmod( SubImageA, EmitterUniforms.SubImageSize.x );
float SubImageBH = fmod( SubImageB, EmitterUniforms.SubImageSize.x );
float SubImageAV = floor( SubImageA * EmitterUniforms.SubImageSize.z );
float SubImageBV = floor( SubImageB * EmitterUniforms.SubImageSize.z );
// Compute the final texture coordinates for both subimages.
float4 TexCoord;
TexCoord.xy = (float2( SubImageAH, SubImageAV ) + FlippedUV) * EmitterUniforms.SubImageSize.zw;
TexCoord.zw = (float2(SubImageBH, SubImageBV) + FlippedUV) * EmitterUniforms.SubImageSize.zw;
#if NUM_MATERIAL_TEXCOORDS_VERTEX >= 3 //Unflipped UVs in texcoords 3 and 4
float4 TexCoord_Unflipped;
TexCoord_Unflipped.xy = (float2(SubImageAH, SubImageAV) + Input.TexCoord.xy) * EmitterUniforms.SubImageSize.zw;
TexCoord_Unflipped.zw = (float2(SubImageBH, SubImageBV) + Input.TexCoord.xy) * EmitterUniforms.SubImageSize.zw;
#endif
// Current rotation of the sprite. Map [0,1] to one full rotation.
float RotationRate = AttributeSample.w * EmitterUniforms.RotationRateScale;
float Rotation = AttributeSample.z + RotationRate * RelativeTime;
Rotation = Rotation * 2.0f * 3.1415926535897932f;
// Transform position to (post-view-translation) world space.
FLWCMatrix Transform = DFToTileOffset(GetPrimitiveDataFromUniformBuffer().LocalToWorld);
FLWCVector3 LWCTileOffset = MakeLWCVector3(EmitterDynamicUniforms.LWCTile, 0);
FLWCVector3 ParticleWorldPosition = LWCAdd(LWCMultiply(PositionSample.xyz, Transform), LWCTileOffset);
float3 ParticleTranslatedWorldPosition = LWCToFloat(LWCAdd(ParticleWorldPosition, ResolvedView.TileOffset.PreViewTranslation));
// Compute tangents for the sprite.
float3 TangentUp, TangentRight;
GetSpriteTangents( TangentUp, TangentRight, Rotation, ParticleTranslatedWorldPosition, ParticleDirection );
// Offset of the sprite from the particle's location.
float3 VertexWorldOffset = Size.x * (Input.TexCoord.x + EmitterUniforms.PivotOffset.x) * TangentRight
+ Size.y * (Input.TexCoord.y + EmitterUniforms.PivotOffset.y) * TangentUp;
// Optional camera motion blur.
#if USES_PARTICLE_MOTION_BLUR
float3 MblurVector = -View.WorldCameraMovementSinceLastFrame;
float3 MblurDirection = normalize(MblurVector + 0.0001f);
//float MblurFactor = saturate(dot(MblurDirection,normalize(VertexWorldOffset)) * 100000.0f);
float MblurFactor = clamp(dot(MblurDirection,normalize(VertexWorldOffset)) * 100000.0f,-1.0f,1.0f);
VertexWorldOffset += (MblurFactor * MblurVector * EmitterUniforms.CameraMotionBlurAmount);
#endif // #if USES_PARTICLE_MOTION_BLUR
// Determine the world position of this vertex (one corner of the sprite).
float3 VertexWorldPosition = ParticleTranslatedWorldPosition + VertexWorldOffset;
// Build and return the set of intermediates.
FVertexFactoryIntermediates Intermediates;
Intermediates.SceneData = VF_GPUSCENE_GET_INTERMEDIATES(Input);
Intermediates.ParticleTranslatedWorldPosition = ParticleTranslatedWorldPosition;
Intermediates.VertexWorldPosition = VertexWorldPosition;
Intermediates.PositionInstanceSpace = PositionSample.xyz;
#if USE_SPHERICAL_NORMALS
// Recenter the particle position for GENERATE_SPHERICAL_PARTICLE_NORMALS to use the right center.
Intermediates.ParticleTranslatedWorldPosition += Size.x * (EmitterUniforms.PivotOffset.x + .5) * TangentRight + Size.y * (EmitterUniforms.PivotOffset.y + .5) * TangentUp;
#endif
#if USE_PARTICLE_LIGHTING_OFFSET
// Hash function based on the particle ID to generate a uniformly distributed 3d offset
float3 RandomParticleOffset = frac((ParticleIndex.x + 10) * (ParticleIndex.y + 10) * float3(1341.456345, 2633.578, 5623.983)) * 2 - 1;
Intermediates.LightingPositionOffset = .5 * RandomParticleOffset;
#endif
Intermediates.TexCoord = TexCoord;
#if NUM_MATERIAL_TEXCOORDS_VERTEX >= 3 //Unflipped UVs in texcoords 3 and 4
Intermediates.TexCoord_Unflipped = TexCoord_Unflipped;
#endif
Intermediates.TangentUp = TangentUp;
Intermediates.TangentRight = TangentRight;
Intermediates.Color = Color;
Intermediates.ParticleVelocity = float4(ParticleDirection, ParticleSpeed);
Intermediates.SubImageLerp = SubImageLerp;
Intermediates.RelativeTime = RelativeTime;
#if USES_PARTICLE_MOTION_BLUR
Intermediates.MotionBlurFade = MblurFactor;
#else // #if USES_PARTICLE_MOTION_BLUR
Intermediates.MotionBlurFade = 0.0f;
#endif // #if USES_PARTICLE_MOTION_BLUR
#if NEEDS_PARTICLE_RANDOM
Intermediates.Random = PseudoRandom(ParticleIndex.xy + float2(EmitterDynamicUniforms.EmitterInstRandom,EmitterDynamicUniforms.EmitterInstRandom));
#else
Intermediates.Random = 0.0f;
#endif
Intermediates.ParticleRadius = .5f * min(Size.x, Size.y);
Intermediates.ParticleSize = Size;
#if USE_PARTICLE_SPRITE_ROTATION
Intermediates.ParticleSpriteRotation = Rotation;
#endif
return Intermediates;
}
#if NUM_TEX_COORD_INTERPOLATORS
float2 GetUV(FVertexFactoryInterpolantsVSToPS Interpolants, int UVIndex)
{
float4 UVVector = Interpolants.TexCoords[UVIndex / 2];
return UVIndex % 2 ? UVVector.zw : UVVector.xy;
}
void SetUV(inout FVertexFactoryInterpolantsVSToPS Interpolants, int UVIndex, float2 InValue)
{
FLATTEN
if (UVIndex % 2)
{
Interpolants.TexCoords[UVIndex / 2].zw = InValue;
}
else
{
Interpolants.TexCoords[UVIndex / 2].xy = InValue;
}
}
#endif
/**
* Computes material parameterss for a given pixel.
* @param Interpolants - Attributes interpolated from the vertex shader.
* @returns per-pixel material parameters.
*/
FMaterialPixelParameters GetMaterialPixelParameters( FVertexFactoryInterpolantsVSToPS Interpolants, float4 SvPosition )
{
// GetMaterialPixelParameters is responsible for fully initializing the result
FMaterialPixelParameters Result = MakeInitializedMaterialPixelParameters();
Result.Particle.MacroUV = EmitterDynamicUniforms.MacroUVParameters;
#if NUM_TEX_COORD_INTERPOLATORS
UNROLL
for (int CoordinateIndex = 0; CoordinateIndex < NUM_TEX_COORD_INTERPOLATORS; CoordinateIndex++)
{
Result.TexCoords[CoordinateIndex] = GetUV(Interpolants, CoordinateIndex);
}
#endif
#if USE_PARTICLE_TIME
Result.Particle.RelativeTime = Interpolants.Misc.y;
#endif
#if USES_PARTICLE_MOTION_BLUR
Result.Particle.MotionBlurFade = 1.0f - abs(Interpolants.Misc.z);
#else // #if USES_PARTICLE_MOTION_BLUR
Result.Particle.MotionBlurFade = 1.0f;
#endif // #if USES_PARTICLE_MOTION_BLUR
#if NEEDS_PARTICLE_RANDOM
Result.Particle.Random = Interpolants.Misc.w;
#else
Result.Particle.Random = 0.0f;
#endif
#if USE_PARTICLE_VELOCITY
Result.Particle.Velocity = Interpolants.ParticleVelocity;
#endif
#if USE_PARTICLE_POSITION
Result.Particle.TranslatedWorldPositionAndSize = Interpolants.ParticleTranslatedWorldPositionAndSize;
Result.Particle.PrevTranslatedWorldPositionAndSize = Result.Particle.TranslatedWorldPositionAndSize;
#endif
#if USE_SPHERICAL_NORMALS
// can be optimized
{
float4 ScreenPosition = SvPositionToResolvedScreenPosition(SvPosition);
float3 TranslatedWorldPosition = SvPositionToResolvedTranslatedWorld(SvPosition);
Result.TangentToWorld = GetSphericalParticleNormal(TranslatedWorldPosition, Interpolants.ParticleTranslatedWorldPositionAndSize.xyz, Interpolants.ParticleTranslatedWorldPositionAndSize.w);
}
#else
half3 TangentToWorld0 = Interpolants.TangentToWorld0.xyz;
half4 TangentToWorld2 = Interpolants.TangentToWorld2;
Result.TangentToWorld = AssembleTangentToWorld(TangentToWorld0, TangentToWorld2);
#endif
#if NEEDS_PARTICLE_COLOR
Result.Particle.Color = Interpolants.Color;
#endif
Result.VertexColor = 1;
Result.TwoSidedSign = 1;
#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 = Interpolants.Misc.x;
#endif
#if USE_PARTICLE_SIZE
Result.Particle.Size = Interpolants.ParticleSize;
#endif
#if USE_PARTICLE_SPRITE_ROTATION
Result.Particle.SpriteRotation = Interpolants.ParticleSpriteRotation;
#endif
return Result;
}
float3 VertexFactoryGetPreviousInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates);
float3 VertexFactoryGetInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates);
/**
* Computes material parameters for a given vertex.
* @param Input - Attributes for this vertex.
* @param Intermediates - Intermediates computed for this vertex.
* @param WorldPosition - The position of this vertex in world space.
* @param TangentToLocal - The tangent basis for this vertex.
* @returns per-vertex material parameters.
*/
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 = 1;
Result.TangentToWorld = mul(TangentToLocal, GetLocalToWorld3x3());
Result.Particle.MacroUV = EmitterDynamicUniforms.MacroUVParameters;
Result.PreSkinnedPosition = WorldPosition.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_PARTICLE_TIME
Result.Particle.RelativeTime = Intermediates.RelativeTime;
#endif
#if USES_PARTICLE_MOTION_BLUR
Result.Particle.MotionBlurFade = 1.0f - abs(Intermediates.MotionBlurFade);
#else // #if USES_PARTICLE_MOTION_BLUR
Result.Particle.MotionBlurFade = 1.0f;
#endif // #if USES_PARTICLE_MOTION_BLUR
#if NEEDS_PARTICLE_RANDOM
Result.Particle.Random = Intermediates.Random;
#else
Result.Particle.Random = 0.0f;
#endif
#if USE_PARTICLE_VELOCITY
Result.Particle.Velocity = Intermediates.ParticleVelocity;
#endif
#if USE_PARTICLE_POSITION
Result.Particle.TranslatedWorldPositionAndSize = float4(Intermediates.ParticleTranslatedWorldPosition, Intermediates.ParticleRadius);
Result.Particle.PrevTranslatedWorldPositionAndSize = Result.Particle.TranslatedWorldPositionAndSize;
#endif
Result.Particle.Color = Intermediates.Color;
#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
#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;
}
/**
* Computes the world space position of this vertex.
* @param Input - Vertex attributes.
* @param Intermediates - Intermediates computed for this vertex.
* @returns the position of this vertex in world space.
*/
float4 VertexFactoryGetWorldPosition( FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates )
{
return float4(Intermediates.VertexWorldPosition,1);
}
float3 VertexFactoryGetInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
return Intermediates.PositionInstanceSpace;
}
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 = Intermediates.ParticleRadius;
return ReprojectPosition(TranslatedWorldPosition, Radius);
#else
return TranslatedWorldPosition;
#endif
}
float3 VertexFactoryGetPositionForVertexLighting(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float3 TranslatedWorldPosition)
{
return TranslatedWorldPosition;
}
/**
* Computes the tangent basis for this vertex in world space.
* @param Input - Vertex attributes.
* @param Intermediates - Intermediates computed for this vertex.
* @returns the tangent basis for this vertex in world space.
*/
float3x3 VertexFactoryGetTangentToLocal( FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates )
{
float3x3 TangentToLocal;
TangentToLocal[0] = Intermediates.TangentRight;
TangentToLocal[1] = Intermediates.TangentUp;
TangentToLocal[2] = normalize(cross(Intermediates.TangentRight.xyz , Intermediates.TangentUp.xyz));
return TangentToLocal;
}
float3 VertexFactoryGetWorldNormal(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
return VertexFactoryGetTangentToLocal(Input, Intermediates)[2].xyz;
}
/**
* Constructs values that need to be interpolated from the vertex shader to the pixel shader.
* @param Input - Vertex attributes.
* @param Intermediates - Intermediates computed for this vertex.
* @returns interpolants.
*/
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 USE_PARTICLE_SUBUVS
Interpolants.ParticleSubUVs.xy = VertexParameters.Particle.SubUVCoords[0];
Interpolants.ParticleSubUVs.zw = VertexParameters.Particle.SubUVCoords[1];
#endif
#if USE_PARTICLE_SUBUVS || USE_PARTICLE_TIME || USES_PARTICLE_MOTION_BLUR || NEEDS_PARTICLE_RANDOM
Interpolants.Misc.x = Intermediates.SubImageLerp;
Interpolants.Misc.y = Intermediates.RelativeTime;
Interpolants.Misc.z = Intermediates.MotionBlurFade;
#endif
#if NEEDS_PARTICLE_RANDOM
Interpolants.Misc.w = Intermediates.Random;
#endif
#if NEEDS_PARTICLE_COLOR
Interpolants.Color = Intermediates.Color;
#endif
#if USE_PARTICLE_POSITION
Interpolants.ParticleTranslatedWorldPositionAndSize = float4(Intermediates.ParticleTranslatedWorldPosition, Intermediates.ParticleRadius);
#endif
#if USE_PARTICLE_VELOCITY
Interpolants.ParticleVelocity = Intermediates.ParticleVelocity;
#endif
#if USE_PARTICLE_LIGHTING_OFFSET
Interpolants.LightingPositionOffset = Intermediates.LightingPositionOffset;
#endif
#if !USE_SPHERICAL_NORMALS
// Note that "local" space for particles is actually oriented in world space! Therefore no rotation is needed.
float3x3 TangentToWorld = VertexFactoryGetTangentToLocal(Input, Intermediates);
float3 TangentToWorld0 = TangentToWorld[0];
float4 TangentToWorld2 = float4(TangentToWorld[2], sign(determinant(TangentToWorld)));
Interpolants.TangentToWorld0 = float4(TangentToWorld0,0);
Interpolants.TangentToWorld2 = TangentToWorld2;
#endif
#if USE_PARTICLE_SIZE
Interpolants.ParticleSize = Intermediates.ParticleSize;
#endif
#if USE_PARTICLE_SPRITE_ROTATION
Interpolants.ParticleSpriteRotation = Intermediates.ParticleSpriteRotation;
#endif
return Interpolants;
}
/**
* Computes the position of this vertex last frame in world space.
* @param Input - Vertex attributes.
* @param Intermediates - Intermediates computed for this vertex.
* @returns the previous position of this vertex in world space.
*/
float4 VertexFactoryGetPreviousWorldPosition( FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates )
{
float3 Position = Intermediates.VertexWorldPosition;
Position.xyz -= Intermediates.ParticleVelocity.xyz * EmitterUniforms.UseVelocityForMotionBlur;
return float4(Position, 1);
}
float3 VertexFactoryGetPreviousInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
return Intermediates.PositionInstanceSpace;
}
float4 VertexFactoryGetTranslatedPrimitiveVolumeBounds(FVertexFactoryInterpolantsVSToPS Interpolants)
{
#if USE_PARTICLE_POSITION
return Interpolants.ParticleTranslatedWorldPositionAndSize;
#endif
return 0;
}
uint VertexFactoryGetPrimitiveId(FVertexFactoryInterpolantsVSToPS Interpolants)
{
return 0;
}
#include "VertexFactoryDefaultInterface.ush"