990 lines
37 KiB
HLSL
990 lines
37 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
StrandHairFactory.usf
|
|
=============================================================================*/
|
|
#include "../VertexFactoryCommon.ush"
|
|
#include "../LocalVertexFactoryCommon.ush"
|
|
#include "HairStrandsVisibilityCommon.ush"
|
|
#include "HairStrandsVertexFactoryCommon.ush"
|
|
#include "HairStrandsAttributeCommon.ush"
|
|
|
|
#include "/Engine/Generated/UniformBuffers/PrecomputedLightingBuffer.ush"
|
|
|
|
#define USE_HAIR_COMPLEX_TRANSMITTANCE 1
|
|
|
|
#ifndef USE_HAIR_TRIANGLE_STRIP
|
|
#error Hair triangle geometry type needs to be defined
|
|
#endif
|
|
|
|
#if MANUAL_VERTEX_FETCH
|
|
Buffer<float4> VertexFetch_InstanceOriginBuffer;
|
|
Buffer<float4> VertexFetch_InstanceTransformBuffer;
|
|
#endif
|
|
|
|
// Enable cluster culling by default as the output of the strands interpolation output culled data,
|
|
// and this allows to support other rendered (e.g., hit proxy, shadow rendering), for which it is not
|
|
// possible to provide custom defines
|
|
#ifndef USE_CULLED_CLUSTER
|
|
#define USE_CULLED_CLUSTER 1
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// HAIR_STRAND_MESH_FACTORY
|
|
// Define used in certain shader like DeepShadow for running specific code (coverage computation for instance)
|
|
// when vertices are produced by the strand hair vertex factory
|
|
// This is set by compilation enviromenent of the vertex factory
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
// ## A quad is made of the following topology and indexing:
|
|
//
|
|
// 0__2 4
|
|
// | / /|
|
|
// |/ /_|
|
|
// 1 5 3
|
|
//
|
|
// ## Control point identification
|
|
// Here is an example of two consecutive strands. The control point:
|
|
// * .a=1 starting
|
|
// * .a=0 within
|
|
// * .a=2 ending
|
|
//
|
|
// _O .a == 1 Strand 0
|
|
// | / /|
|
|
// |/ /_|
|
|
// _O .a == 0
|
|
// | / /|
|
|
// |/ /_|
|
|
// _O .a == 0
|
|
// | / /|
|
|
// |/ /_|
|
|
// O .a == 2
|
|
//
|
|
//
|
|
// O .a == 1 Strand 1
|
|
// | / /|
|
|
// |/ /_|
|
|
// O .a == 2
|
|
//
|
|
// ...
|
|
|
|
/**
|
|
* Per-vertex inputs from bound vertex buffers
|
|
*/
|
|
bool UseStableRasterization()
|
|
{
|
|
return HasHairFlags(HairStrandsVF.Flags, HAIR_FLAGS_STABLE_RASTER);
|
|
}
|
|
|
|
bool UseScatterSceneLighting()
|
|
{
|
|
return HasHairFlags(HairStrandsVF.Flags,HAIR_FLAGS_SCATTER_SCENE_LIGHT);
|
|
}
|
|
|
|
struct FVertexFactoryInput
|
|
{
|
|
#if RAYHITGROUPSHADER
|
|
float4 Position;
|
|
uint TriangleId; // from RayHit
|
|
uint TriangleVertexId; // 0,1,2
|
|
#endif
|
|
|
|
// Dynamic instancing related attributes with InstanceIdOffset : ATTRIBUTE13
|
|
VF_GPUSCENE_DECLARE_INPUT_BLOCK(13)
|
|
|
|
// Stereo rendering related params
|
|
VF_INSTANCED_STEREO_DECLARE_INPUT_BLOCK()
|
|
VF_MOBILE_MULTI_VIEW_DECLARE_INPUT_BLOCK()
|
|
|
|
uint VertexId : SV_VertexID;
|
|
};
|
|
|
|
struct FVertexInfo
|
|
{
|
|
uint VertexIndex;
|
|
uint HairControlPointId;
|
|
uint IsTip; // The vertex is on the quad side toward the tip of the strand.
|
|
#if !RAYHITGROUPSHADER
|
|
uint IsLeft;
|
|
bool bForceInvalidQuad;
|
|
#endif
|
|
};
|
|
FVertexInfo GetVertexInfo(FVertexFactoryInput Input)
|
|
{
|
|
uint VertexId = Input.VertexId;
|
|
|
|
FVertexInfo VertexInfo;
|
|
#if RAYHITGROUPSHADER
|
|
uint BaseIndex = VertexId / 4;
|
|
// TODO: is there any easy way we could handle end-caps?
|
|
VertexInfo.IsTip = (Input.TriangleId % 2) == 0 ? Input.TriangleVertexId != 2 : Input.TriangleVertexId == 0;
|
|
#elif USE_HAIR_TRIANGLE_STRIP
|
|
uint QuadIndex = VertexId % 2;
|
|
uint BaseIndex = VertexId / 2;
|
|
VertexInfo.IsTip = 0;
|
|
VertexInfo.IsLeft = QuadIndex == 0 ? 1 : 0;
|
|
#else
|
|
uint QuadIndex = VertexId % 6;
|
|
uint BaseIndex = VertexId / 6;
|
|
VertexInfo.IsTip = QuadIndex == 0 || QuadIndex == 2 || QuadIndex == 4 ? 0 : 1;
|
|
VertexInfo.IsLeft = QuadIndex == 0 || QuadIndex == 1 || QuadIndex == 5 ? 1 : 0;
|
|
#endif
|
|
VertexInfo.HairControlPointId = BaseIndex;
|
|
VertexInfo.VertexIndex = BaseIndex + VertexInfo.IsTip;
|
|
#if !RAYHITGROUPSHADER
|
|
VertexInfo.bForceInvalidQuad = false;
|
|
#endif
|
|
|
|
#if USE_CULLED_CLUSTER == 1
|
|
if (HairStrandsVF.bCullingEnable)
|
|
{
|
|
VertexInfo.VertexIndex = HairStrandsVF.CullingIndexBuffer[BaseIndex + VertexInfo.IsTip];
|
|
VertexInfo.HairControlPointId = VertexInfo.VertexIndex - VertexInfo.IsTip;
|
|
}
|
|
#endif
|
|
|
|
return VertexInfo;
|
|
}
|
|
|
|
#if RAYHITGROUPSHADER
|
|
FVertexFactoryInput LoadVertexFactoryInputForHGS(uint TriangleIndex, int VertexIndex)
|
|
{
|
|
FVertexFactoryInput Input = (FVertexFactoryInput)0;
|
|
|
|
#if !ENABLE_PROCEDURAL_INTERSECTOR
|
|
FTriangleBaseAttributes TriangleBase = LoadTriangleBaseAttributes(TriangleIndex);
|
|
|
|
Input.Position = float4(TriangleBase.LocalPositions[VertexIndex], 1.0f);
|
|
Input.VertexId = TriangleBase.Indices[VertexIndex];
|
|
Input.TriangleId = TriangleIndex;
|
|
Input.TriangleVertexId = VertexIndex;
|
|
#endif // !ENABLE_PROCEDURAL_INTERSECTOR
|
|
|
|
// Note: GetInstanceUserData() stores the GPU-Scene instance ID
|
|
VF_GPUSCENE_SET_INPUT_FOR_RT(Input, GetInstanceUserData(), 0U);
|
|
|
|
return Input;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Caches intermediates that would otherwise have to be computed multiple times. Avoids relying on the compiler to optimize out redundant operations.
|
|
*/
|
|
struct FVertexFactoryIntermediates
|
|
{
|
|
half3x3 TangentToLocal;
|
|
half3x3 TangentToWorld;
|
|
half TangentToWorldSign;
|
|
uint HairControlPointId;
|
|
float2 HairPrimitiveUV;
|
|
float2 HairDimensions; // This is kept on the intermediate as this is used in several vertex shader for the actual coverage.
|
|
float HairDensity;
|
|
/** Cached primitive and instance data */
|
|
FSceneDataIntermediates SceneData;
|
|
};
|
|
|
|
float3 GetPositionOffset()
|
|
{
|
|
return ReadRenPositionOffset(HairStrandsVF.PositionOffsetBuffer, HairStrandsVF.RegisteredIndex);
|
|
}
|
|
|
|
float3 GetPrevPositionOffset()
|
|
{
|
|
return ReadRenPrevPositionOffset(HairStrandsVF.PreviousPositionOffsetBuffer, HairStrandsVF.RegisteredIndex);
|
|
}
|
|
|
|
uint GetHairControlPointId(FVertexFactoryInput Input)
|
|
{
|
|
return GetVertexInfo(Input).HairControlPointId;
|
|
}
|
|
|
|
FHairControlPoint GetVertexPosition(const FVertexInfo VertexInfo, bool bInvalidJointVertex=true)
|
|
{
|
|
const float3 PositionOffset = GetPositionOffset();
|
|
|
|
FHairControlPoint Out = ReadHairControlPoint(
|
|
HairStrandsVF.PositionBuffer,
|
|
VertexInfo.VertexIndex,
|
|
PositionOffset,
|
|
HairStrandsVF.Radius,
|
|
HairStrandsVF.RootScale,
|
|
HairStrandsVF.TipScale);
|
|
|
|
// Create a degenerated quad the end of each strand to cut between each strands
|
|
// #hair_todo: This is not efficient for short strand like fur for instance. Need to revisit that at some point
|
|
#if RAYHITGROUPSHADER
|
|
const bool bIsInvalidQuad = false; // can't hit an invalid quad in raytracing (filtered by the acceleration structure)
|
|
#elif USE_HAIR_TRIANGLE_STRIP
|
|
const bool bIsInvalidQuad = (bInvalidJointVertex && Out.Type == HAIR_CONTROLPOINT_END) || VertexInfo.bForceInvalidQuad;
|
|
#else
|
|
const bool bIsInvalidQuad = (bInvalidJointVertex && Out.Type == HAIR_CONTROLPOINT_START && VertexInfo.IsTip == 1) || (bInvalidJointVertex && Out.Type == HAIR_CONTROLPOINT_END && VertexInfo.IsTip == 0) || VertexInfo.bForceInvalidQuad;
|
|
#endif
|
|
Out.Position = bIsInvalidQuad ? float3(INFINITE_FLOAT, INFINITE_FLOAT, INFINITE_FLOAT) : Out.Position;
|
|
|
|
return Out;
|
|
}
|
|
|
|
FHairControlPoint GetVertexPosition(FVertexFactoryInput Input, bool bInvalidJointVertex=true)
|
|
{
|
|
const FVertexInfo VertexInfo = GetVertexInfo(Input);
|
|
return GetVertexPosition(VertexInfo, bInvalidJointVertex);
|
|
}
|
|
|
|
float GetWorldStrandRadius(FVertexFactoryInput Input)
|
|
{
|
|
return GetVertexPosition(Input).WorldRadius;
|
|
}
|
|
|
|
float3 GetVertexPreviousPosition(FVertexFactoryInput Input, bool bInvalidJointVertex = true)
|
|
{
|
|
const float3 PreviousPositionOffset = GetPrevPositionOffset();
|
|
|
|
FVertexInfo VertexInfo = GetVertexInfo(Input);
|
|
FHairControlPoint Out = ReadHairControlPoint(
|
|
HairStrandsVF.PreviousPositionBuffer,
|
|
VertexInfo.VertexIndex,
|
|
PreviousPositionOffset,
|
|
HairStrandsVF.Radius,
|
|
HairStrandsVF.RootScale,
|
|
HairStrandsVF.TipScale);
|
|
|
|
// Create a degenerated quad the end of each strand to cut between each strands
|
|
// #hair_todo: This is not efficient for short strand like fur for instance. Need to revisit that at some point
|
|
#if RAYHITGROUPSHADER
|
|
const bool bIsInvalidQuad = false; // can't hit an invalid quad in raytracing (filtered by the acceleration structure)
|
|
#elif USE_HAIR_TRIANGLE_STRIP
|
|
const bool bIsInvalidQuad = (bInvalidJointVertex && Out.Type == HAIR_CONTROLPOINT_END) || VertexInfo.bForceInvalidQuad;
|
|
#else
|
|
const bool bIsInvalidQuad = (bInvalidJointVertex && Out.Type == HAIR_CONTROLPOINT_START && VertexInfo.IsTip == 1) || (bInvalidJointVertex && Out.Type == HAIR_CONTROLPOINT_END && VertexInfo.IsTip == 0) || VertexInfo.bForceInvalidQuad;
|
|
#endif
|
|
Out.Position = bIsInvalidQuad ? float3(INFINITE_FLOAT, INFINITE_FLOAT, INFINITE_FLOAT) : Out.Position;
|
|
|
|
return Out.Position;
|
|
}
|
|
|
|
// Segment UV coord of an hair segment. This is different from the UV coord of the hair strands
|
|
float2 GetSegmentUVCoord(FVertexFactoryInput Input)
|
|
{
|
|
FVertexInfo VertexInfo = GetVertexInfo(Input);
|
|
#if RAYHITGROUPSHADER
|
|
const float VCoord = 0.5; // TODO: find a way to define this?
|
|
#else
|
|
const float VCoord = VertexInfo.IsLeft ? 0.0f : 1.0f;
|
|
#endif
|
|
const float UCoord = VertexInfo.IsTip ? 1.0f : 0.0f;
|
|
return float2(UCoord, VCoord);
|
|
}
|
|
|
|
/** 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();
|
|
|
|
half3 TangentToWorld0 = GetTangentToWorld0(Interpolants).xyz;
|
|
half4 TangentToWorld2 = GetTangentToWorld2(Interpolants);
|
|
Result.UnMirrored = TangentToWorld2.w;
|
|
|
|
// Required for previewing materials that use ParticleColor
|
|
Result.Particle.Color = half4(1,1,1,1);
|
|
|
|
Result.TangentToWorld = AssembleTangentToWorld( TangentToWorld0, TangentToWorld2 );
|
|
#if USE_WORLDVERTEXNORMAL_CENTER_INTERPOLATION
|
|
Result.WorldVertexNormal_Center = Interpolants.TangentToWorld2_Center.xyz;
|
|
#endif
|
|
|
|
Result.TwoSidedSign = 1;
|
|
Result.PrimitiveId = ToScalarMemory(GetPrimitiveId(Interpolants));
|
|
Result.HairControlPointId = Interpolants.HairControlPointId;
|
|
Result.HairPrimitiveUV = float2(Interpolants.HairPrimitiveUV);
|
|
|
|
return Result;
|
|
}
|
|
|
|
half3x3 CalcTangentToWorldNoScale(FPrimitiveSceneData SceneData, half3x3 TangentToLocal)
|
|
{
|
|
half3x3 LocalToWorld = DFToFloat3x3(SceneData.LocalToWorld);
|
|
half3 InvScale = SceneData.InvNonUniformScale;
|
|
LocalToWorld[0] *= InvScale.x;
|
|
LocalToWorld[1] *= InvScale.y;
|
|
LocalToWorld[2] *= InvScale.z;
|
|
return mul(TangentToLocal, LocalToWorld);
|
|
}
|
|
|
|
float3 VertexFactoryGetPreviousInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, bool bInvalidJointVertex);
|
|
float3 VertexFactoryGetPreviousInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
|
|
{
|
|
return VertexFactoryGetPreviousInstanceSpacePosition(Input, Intermediates, true);
|
|
}
|
|
float3 VertexFactoryGetInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates);
|
|
float3 VertexFactoryGetInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, FHairViewInfo HairInfo);
|
|
|
|
/** 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,
|
|
float3 PositionInstanceSpace,
|
|
half3x3 TangentToLocal,
|
|
bool bIsPreviousFrame = false)
|
|
{
|
|
FMaterialVertexParameters Result = MakeInitializedMaterialVertexParameters();
|
|
Result.SceneData = Intermediates.SceneData;
|
|
|
|
Result.WorldPosition = WorldPosition;
|
|
Result.PositionInstanceSpace = PositionInstanceSpace;
|
|
Result.PositionPrimitiveSpace = Result.PositionInstanceSpace; // No support for instancing, so instance == primitive
|
|
|
|
// does not handle instancing!
|
|
Result.TangentToWorld = Intermediates.TangentToWorld;
|
|
|
|
Result.PrevFrameLocalToWorld = Intermediates.SceneData.Primitive.PreviousLocalToWorld;
|
|
Result.PreSkinnedPosition = GetVertexPreviousPosition(Input);
|
|
Result.PreSkinnedNormal = TangentToLocal[2];
|
|
|
|
Result.LWCData = MakeMaterialLWCData(Result);
|
|
|
|
return Result;
|
|
}
|
|
|
|
FMaterialVertexParameters GetMaterialVertexParameters(
|
|
FVertexFactoryInput Input,
|
|
FVertexFactoryIntermediates Intermediates,
|
|
float3 WorldPosition,
|
|
half3x3 TangentToLocal,
|
|
FHairViewInfo HairInfo,
|
|
bool bIsPreviousFrame = false)
|
|
{
|
|
float3 PositionInstanceSpace;
|
|
if (bIsPreviousFrame)
|
|
{
|
|
PositionInstanceSpace = VertexFactoryGetPreviousInstanceSpacePosition(Input, Intermediates);
|
|
}
|
|
else
|
|
{
|
|
PositionInstanceSpace = VertexFactoryGetInstanceSpacePosition(Input, Intermediates, HairInfo);
|
|
}
|
|
return GetMaterialVertexParameters(Input, Intermediates, WorldPosition, PositionInstanceSpace, TangentToLocal, bIsPreviousFrame);
|
|
}
|
|
|
|
FMaterialVertexParameters GetMaterialVertexParameters(
|
|
FVertexFactoryInput Input,
|
|
FVertexFactoryIntermediates Intermediates,
|
|
float3 WorldPosition,
|
|
half3x3 TangentToLocal,
|
|
bool bIsPreviousFrame = false)
|
|
{
|
|
float3 PositionInstanceSpace;
|
|
if (bIsPreviousFrame)
|
|
{
|
|
PositionInstanceSpace = VertexFactoryGetPreviousInstanceSpacePosition(Input, Intermediates);
|
|
}
|
|
else
|
|
{
|
|
PositionInstanceSpace = VertexFactoryGetInstanceSpacePosition(Input, Intermediates);
|
|
}
|
|
return GetMaterialVertexParameters(Input, Intermediates, WorldPosition, PositionInstanceSpace, TangentToLocal, bIsPreviousFrame);
|
|
}
|
|
|
|
float4 CalcWorldPosition(float3 Position, FDFMatrix LocalToWorld)
|
|
{
|
|
return DFTransformLocalToTranslatedWorld(Position, LocalToWorld, ResolvedView.PreViewTranslation);
|
|
}
|
|
|
|
half3x3 CalcTangentToLocal(uint VertexIndex, inout float TangentSign)
|
|
{
|
|
const half3 TangentInputX = HairStrandsVF.TangentBuffer[VertexIndex * 2 + 0].xyz;
|
|
const half4 TangentInputZ = HairStrandsVF.TangentBuffer[VertexIndex * 2 + 1];
|
|
|
|
half3 TangentX = TangentBias(TangentInputX);
|
|
half4 TangentZ = TangentBias(TangentInputZ);
|
|
TangentSign = TangentZ.w;
|
|
|
|
// derive the binormal by getting the cross product of the normal and tangent
|
|
half3 TangentY = cross(TangentZ.xyz, TangentX) * TangentZ.w;
|
|
|
|
// Recalculate TangentX off of the other two vectors
|
|
// This corrects quantization error since TangentX was passed in as a quantized vertex input
|
|
// The error shows up most in specular off of a mesh with a smoothed UV seam (normal is smooth, but tangents vary across the seam)
|
|
half3x3 Result;
|
|
Result[0] = cross(TangentY, TangentZ.xyz) * TangentZ.w;
|
|
Result[1] = TangentY;
|
|
Result[2] = TangentZ.xyz;
|
|
|
|
return Result;
|
|
}
|
|
|
|
half3x3 CalcTangentToLocal(FVertexFactoryInput Input, inout float TangentSign)
|
|
{
|
|
FVertexInfo VertexInfo = GetVertexInfo(Input);
|
|
return CalcTangentToLocal(VertexInfo.VertexIndex, TangentSign);
|
|
}
|
|
|
|
half3x3 CalcTangentToWorld(FPrimitiveSceneData SceneData, half3x3 TangentToLocal)
|
|
{
|
|
return CalcTangentToWorldNoScale(SceneData, TangentToLocal);
|
|
}
|
|
|
|
float3 GetTangent(FVertexFactoryIntermediates Intermediates)
|
|
{
|
|
// Hair shader expec the tangent to be stored in place of the normal. This is what the StrandHairComponent is filling in:
|
|
// [0]: Real normal | Should be Tangent
|
|
// [1]: Bitangent | Should be BiTangent
|
|
// [2]: Real Tangent | Should be Normal
|
|
return Intermediates.TangentToWorld[2].xyz;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Raytracing custom intersection shader
|
|
|
|
#if RAYHITGROUPSHADER
|
|
|
|
#if USE_MATERIAL_INTERSECTION_SHADER
|
|
|
|
|
|
RAY_TRACING_ENTRY_INTERSECTION(MaterialIS)
|
|
{
|
|
|
|
float3 RayOrg = ObjectRayOrigin();
|
|
float3 RayDir = ObjectRayDirection();
|
|
float TMin = RayTMin();
|
|
float TMax = RayTCurrent();
|
|
|
|
uint VertexIndex0 = PrimitiveIndex() / HairStrandsVF.RaytracingProceduralSplits;
|
|
uint VertexIndex1 = VertexIndex0 + 1;
|
|
if (HairStrandsVF.bCullingEnable)
|
|
{
|
|
VertexIndex0 = HairStrandsVF.CullingIndexBuffer[VertexIndex0];
|
|
VertexIndex1 = HairStrandsVF.CullingIndexBuffer[VertexIndex1];
|
|
}
|
|
|
|
const float EffectiveRadius = HairStrandsVF.Radius * HairStrandsVF.RaytracingRadiusScale;
|
|
const float3 PositionOffset = GetPositionOffset();
|
|
FHairControlPoint P0 = ReadHairControlPoint(
|
|
HairStrandsVF.PositionBuffer,
|
|
VertexIndex0,
|
|
PositionOffset,
|
|
EffectiveRadius,
|
|
HairStrandsVF.RootScale,
|
|
HairStrandsVF.TipScale);
|
|
FHairControlPoint P1 = ReadHairControlPoint(
|
|
HairStrandsVF.PositionBuffer,
|
|
VertexIndex1,
|
|
PositionOffset,
|
|
EffectiveRadius,
|
|
HairStrandsVF.RootScale,
|
|
HairStrandsVF.TipScale);
|
|
|
|
// ray/ribbon intersection - this is very approximate compared to a real oriented cone, but sufficient for thin strands
|
|
float3 pa = P0.Position;
|
|
float3 pb = P1.Position;
|
|
float ra = P0.WorldRadius;
|
|
float rb = P1.WorldRadius;
|
|
float3 ro = RayOrg;
|
|
float3 rd = RayDir;
|
|
float3 u = pb - pa;
|
|
float3 w0 = pa - ro;
|
|
float3 w1 = pb - ro;
|
|
float3 n = cross(rd, u);
|
|
float3 n1 = cross(rd, n);
|
|
float sc = saturate(-dot(w0, n1) / dot(u, n1));
|
|
float3 cp = lerp(w0, w1, sc);
|
|
float rc = lerp(ra, rb, sc);
|
|
float t = dot(cp, rd) / dot(rd, rd);
|
|
float3 dp = t * rd - cp;
|
|
float d2 = length2(dp);
|
|
float r2 = rc * rc;
|
|
if (d2 < r2 && t > 4. * rc)
|
|
{
|
|
// NOTE: the tangents are not exactly equal to 'u' at the extremeties due to quantization, could be avoided with better compression
|
|
float3 ta = P0.Type == HAIR_CONTROLPOINT_START ? u : HairStrandsVF.TangentBuffer[VertexIndex0 * 2 + 1].xyz;
|
|
float3 tb = P1.Type == HAIR_CONTROLPOINT_END ? u : HairStrandsVF.TangentBuffer[VertexIndex1 * 2 + 1].xyz;
|
|
// clip the rounded portion from the end each segment
|
|
if ((dot(t * rd - w0, ta) * dot(ta, u) < 0)) return;
|
|
if ((dot(t * rd - w1, tb) * dot(tb, u) > 0)) return;
|
|
|
|
float h = sign(dot(dp, n)) * sqrt(saturate(d2 / r2));
|
|
FRayTracingIntersectionAttributes Attributes = (FRayTracingIntersectionAttributes)0;
|
|
Attributes.SetBarycentrics(float2(sc, 0.5 * h + 0.5));
|
|
ReportHit(t, HIT_KIND_TRIANGLE_FRONT_FACE, Attributes);
|
|
}
|
|
}
|
|
|
|
#endif // USE_MATERIAL_INTERSECTION_SHADER
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Material evaluation for raytracing
|
|
|
|
// define this to let shaders know this alternative method is available
|
|
#define VF_SUPPORTS_RAYTRACING_PREPARE_MATERIAL_PIXEL_PARAMETERS 1
|
|
|
|
FMaterialPixelParameters GetMaterialPixelParameters(float3 RayOrigin, float3 RayDirection, float HitT, uint HitPrimitiveIndex, FRayTracingIntersectionAttributes HitAttributes, uint HitKind, float4 SvPosition, out float3 WorldGeoNormal)
|
|
{
|
|
FMaterialPixelParameters Result = MakeInitializedMaterialPixelParameters();
|
|
|
|
#if VF_USE_PRIMITIVE_SCENE_DATA
|
|
FVertexFactoryInput Input;
|
|
// Note: GetInstanceUserData() stores the GPU-Scene instance ID
|
|
VF_GPUSCENE_SET_INPUT_FOR_RT(Input, GetInstanceUserData(), 0U);
|
|
#endif
|
|
|
|
FSceneDataIntermediates SceneData = VF_GPUSCENE_GET_INTERMEDIATES(Input); // NOTE: Input is not used when VF_USE_PRIMITIVE_SCENE_DATA == 0
|
|
|
|
const float2 Barycentrics = HitAttributes.GetBarycentrics();
|
|
|
|
#if ENABLE_PROCEDURAL_INTERSECTOR
|
|
uint VertexIndex0 = HitPrimitiveIndex / HairStrandsVF.RaytracingProceduralSplits;
|
|
uint VertexIndex1 = VertexIndex0 + 1;
|
|
|
|
if (HairStrandsVF.bCullingEnable)
|
|
{
|
|
VertexIndex0 = HairStrandsVF.CullingIndexBuffer[VertexIndex0];
|
|
VertexIndex1 = HairStrandsVF.CullingIndexBuffer[VertexIndex1];
|
|
}
|
|
|
|
const float EffectiveRadius = HairStrandsVF.Radius * HairStrandsVF.RaytracingRadiusScale;
|
|
float UCoord = Barycentrics.x;
|
|
float VCoord = Barycentrics.y;
|
|
const float3 PositionOffset = GetPositionOffset();
|
|
FHairControlPoint P0 = ReadHairControlPoint(
|
|
HairStrandsVF.PositionBuffer,
|
|
VertexIndex0,
|
|
PositionOffset,
|
|
EffectiveRadius,
|
|
HairStrandsVF.RootScale,
|
|
HairStrandsVF.TipScale);
|
|
FHairControlPoint P1 = ReadHairControlPoint(
|
|
HairStrandsVF.PositionBuffer,
|
|
VertexIndex1,
|
|
PositionOffset,
|
|
EffectiveRadius,
|
|
HairStrandsVF.RootScale,
|
|
HairStrandsVF.TipScale);
|
|
|
|
#else
|
|
const float3 Weights = float3(
|
|
1 - Barycentrics.x - Barycentrics.y,
|
|
Barycentrics.x,
|
|
Barycentrics.y);
|
|
|
|
// fetch triangle
|
|
FTriangleBaseAttributes Tri = LoadTriangleBaseAttributes(HitPrimitiveIndex);
|
|
// Transform vertices into world space
|
|
const float3 WorldPositions[3] = {
|
|
CalcWorldPosition(Tri.LocalPositions[0], SceneData.Primitive.LocalToWorld).xyz,
|
|
CalcWorldPosition(Tri.LocalPositions[1], SceneData.Primitive.LocalToWorld).xyz,
|
|
CalcWorldPosition(Tri.LocalPositions[2], SceneData.Primitive.LocalToWorld).xyz
|
|
};
|
|
// Compute triangle normal
|
|
{
|
|
const float3 PA = WorldPositions[1] - WorldPositions[0];
|
|
const float3 PB = WorldPositions[2] - WorldPositions[0];
|
|
const float3 Unnormalized = cross(PB, PA);
|
|
const float InvWorldArea = rsqrt(dot(Unnormalized, Unnormalized));
|
|
WorldGeoNormal = Unnormalized * InvWorldArea;
|
|
}
|
|
|
|
// NOTE: This must match the tesselation pattern produced by SetBodySegment() in HairStrandsRaytracingGeometry.usf
|
|
// TODO: is there a way we could handle end-caps elegantly?
|
|
uint VertexIndex0 = Tri.Indices.z / 4; // last index always belongs to the "bottom" vertex of the hair segment
|
|
uint VertexIndex1 = VertexIndex0 + 1;
|
|
|
|
if (HairStrandsVF.bCullingEnable)
|
|
{
|
|
VertexIndex0 = HairStrandsVF.CullingIndexBuffer[VertexIndex0];
|
|
VertexIndex1 = HairStrandsVF.CullingIndexBuffer[VertexIndex1];
|
|
}
|
|
|
|
float UCoord = dot(float3(1, float((HitPrimitiveIndex % 2) == 0), 0), Weights);
|
|
float VCoord = 0;
|
|
{
|
|
// find central point of the hair strand
|
|
const float3 PositionOffset = GetPositionOffset();
|
|
const float EffectiveRadius = HairStrandsVF.Radius * HairStrandsVF.RaytracingRadiusScale;
|
|
FHairControlPoint P0 = ReadHairControlPoint(
|
|
HairStrandsVF.PositionBuffer,
|
|
VertexIndex0,
|
|
PositionOffset,
|
|
EffectiveRadius,
|
|
HairStrandsVF.RootScale,
|
|
HairStrandsVF.TipScale);
|
|
FHairControlPoint P1 = ReadHairControlPoint(
|
|
HairStrandsVF.PositionBuffer,
|
|
VertexIndex1,
|
|
PositionOffset,
|
|
EffectiveRadius,
|
|
HairStrandsVF.RootScale,
|
|
HairStrandsVF.TipScale);
|
|
float4 A = float4(P0.Position, P0.WorldRadius);
|
|
float4 B = float4(P1.Position, P1.WorldRadius);
|
|
float4 C = lerp(A, B, UCoord);
|
|
// approximate position along the width of the ribbon
|
|
float3 WorldC = DFDemote(TransformLocalToWorld(C.xyz, SceneData.Primitive.LocalToWorld));
|
|
float3 rd = RayDirection;
|
|
float3 cp = WorldC - RayOrigin;
|
|
float t = dot(cp, rd) / dot(rd, rd);
|
|
float3 dp = t * RayDirection - cp;
|
|
float d2 = length2(dp);
|
|
float v = saturate(sqrt(d2 / Pow2(C.w))); // TODO: account for scale of radius
|
|
// figure out sign of v
|
|
float3 WorldU = DFDemote(TransformLocalToWorld(B.xyz - A.xyz, SceneData.Primitive.LocalToWorld));
|
|
VCoord = v * sign(dot(dp, cross(RayDirection, WorldU)));
|
|
VCoord = 0.5 * VCoord + 0.5;
|
|
}
|
|
|
|
|
|
|
|
#endif // ENABLE_PROCEDURAL_INTERSECTOR
|
|
Result.HairControlPointId = VertexIndex0;
|
|
Result.HairPrimitiveUV = float2(UCoord, VCoord);
|
|
|
|
// Required for previewing materials that use ParticleColor
|
|
Result.Particle.Color = half4(1, 1, 1, 1);
|
|
|
|
// Get tangent frame from each vertex
|
|
float TangentSign_V0 = 0; half3x3 TangentToWorld_V0 = CalcTangentToWorld(SceneData.Primitive, CalcTangentToLocal(VertexIndex0, TangentSign_V0));
|
|
float TangentSign_V1 = 0; half3x3 TangentToWorld_V1 = CalcTangentToWorld(SceneData.Primitive, CalcTangentToLocal(VertexIndex1, TangentSign_V1));
|
|
// interpolate to current point within the segment
|
|
float3 TangentToWorld0 = normalize(lerp(TangentToWorld_V0[0], TangentToWorld_V1[0], UCoord));
|
|
float3 TangentToWorld2 = normalize(lerp(TangentToWorld_V0[2], TangentToWorld_V1[2], UCoord));
|
|
float TangentSign = lerp(TangentSign_V0, TangentSign_V1, UCoord) * GetPrimitive_DeterminantSign_FromFlags(SceneData.Primitive.Flags);
|
|
|
|
Result.TangentToWorld = AssembleTangentToWorld(TangentToWorld0, float4(TangentToWorld2, TangentSign));
|
|
|
|
// compute a smooth ribbon normal based on the tangent vector
|
|
float3 T = Result.TangentToWorld[2];
|
|
float3 D = RayDirection;
|
|
WorldGeoNormal = normalize(cross(T, normalize(cross(D, T))));
|
|
|
|
Result.TwoSidedSign = sign(dot(RayDirection, WorldGeoNormal));
|
|
|
|
Result.PrimitiveId = SceneData.PrimitiveId;
|
|
|
|
return Result;
|
|
}
|
|
|
|
#endif // RAYHITGROUPSHADER
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Material evaluation for regular shader
|
|
|
|
FVertexFactoryIntermediates GetVertexFactoryIntermediates(FVertexFactoryInput Input)
|
|
{
|
|
FVertexFactoryIntermediates Intermediates;
|
|
Intermediates.SceneData = VF_GPUSCENE_GET_INTERMEDIATES(Input);
|
|
Intermediates.HairControlPointId= GetHairControlPointId(Input);
|
|
Intermediates.HairPrimitiveUV = GetSegmentUVCoord(Input);
|
|
Intermediates.HairDensity = HairStrandsVF.Density;
|
|
Intermediates.HairDimensions = float2(0.f, GetWorldStrandRadius(Input));
|
|
|
|
float TangentSign;
|
|
Intermediates.TangentToLocal = CalcTangentToLocal(Input, TangentSign);
|
|
Intermediates.TangentToWorld = CalcTangentToWorld(Intermediates.SceneData.Primitive, Intermediates.TangentToLocal);
|
|
Intermediates.TangentToWorldSign = TangentSign * GetPrimitive_DeterminantSign_FromFlags(Intermediates.SceneData.Primitive.Flags);
|
|
return Intermediates;
|
|
}
|
|
|
|
/**
|
|
* Get the 3x3 tangent basis vectors for this vertex factory
|
|
* this vertex factory will calculate the binormal on-the-fly
|
|
*
|
|
* @param Input - vertex input stream structure
|
|
* @return 3x3 matrix
|
|
*/
|
|
half3x3 VertexFactoryGetTangentToLocal( FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates )
|
|
{
|
|
return Intermediates.TangentToLocal;
|
|
}
|
|
|
|
#if RAYHITGROUPSHADER
|
|
|
|
float4 VertexFactoryGetWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
|
|
{
|
|
return CalcWorldPosition(Input.Position.xyz, Intermediates.SceneData.Primitive.LocalToWorld);
|
|
}
|
|
|
|
float3 VertexFactoryGetInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
|
|
{
|
|
return Input.Position.xyz;
|
|
}
|
|
|
|
#else
|
|
|
|
float4 ComputeViewAlignedWorldPosition(FVertexFactoryInput Input, float3 WorldTangent, float4 WorldPosition, float WorldStrandRadius, FHairViewInfo HairViewInfo)
|
|
{
|
|
FVertexInfo VertexInfo = GetVertexInfo(Input);
|
|
|
|
// Minimal radius to snap the strand to a sample/pixel center (to avoid aliasing)
|
|
const float DistanceToCamera = length(HairViewInfo.TranslatedWorldCameraOrigin - WorldPosition.xyz);
|
|
const float MinStrandHairRadius = ConvertGivenDepthRadiusForProjectionType(HairViewInfo.RadiusAtDepth1, DistanceToCamera, HairViewInfo.bIsOrthoView);
|
|
|
|
const float3 ViewDir = -HairViewInfo.ViewForward;
|
|
const float3 Right = normalize(cross(WorldTangent, ViewDir));
|
|
const float3 OutWorldPosition = WorldPosition.xyz + (VertexInfo.IsLeft ? -Right : Right) * max(WorldStrandRadius, MinStrandHairRadius);
|
|
|
|
return float4(OutWorldPosition, 1);
|
|
}
|
|
|
|
// @return translated world position
|
|
// Specialized version of VertexFactoryGetWorldPosition & ComputeViewAlignedWorldPosition, for visibility VS shader.
|
|
float4 VertexFactoryGetWorldPosition_Visibility(FVertexFactoryInput Input, FHairViewInfo HairViewInfo, inout uint OutHairControlPointId)
|
|
{
|
|
const FVertexInfo VertexInfo = GetVertexInfo(Input);
|
|
OutHairControlPointId = VertexInfo.HairControlPointId;
|
|
|
|
FHairControlPoint ControlPoint = GetVertexPosition(VertexInfo);
|
|
|
|
FVertexFactoryIntermediates Intermediates = GetVertexFactoryIntermediates(Input);
|
|
const float4 WorldPosition = CalcWorldPosition(ControlPoint.Position, Intermediates.SceneData.Primitive.LocalToWorld);
|
|
const float3 WorldTangent = GetTangent(Intermediates);
|
|
|
|
// Minimal radius to snap the strand to a sample/pixel center (to avoid aliasing)
|
|
const float DistanceToCamera = length(HairViewInfo.TranslatedWorldCameraOrigin - WorldPosition.xyz);
|
|
const float MinStrandHairRadius = ConvertGivenDepthRadiusForProjectionType(HairViewInfo.RadiusAtDepth1, DistanceToCamera);
|
|
const float3 ViewDir = -HairViewInfo.ViewForward;
|
|
const float3 Right = normalize(cross(WorldTangent, ViewDir));
|
|
const float3 OutWorldPosition = WorldPosition.xyz + (VertexInfo.IsLeft ? -Right : Right) * max(ControlPoint.WorldRadius, MinStrandHairRadius);
|
|
|
|
return float4(OutWorldPosition, 1);
|
|
}
|
|
|
|
// @return translated world position
|
|
// Specialized version of VertexFactoryGetWorldPosition & ComputeViewAlignedWorldPosition, for voxelization VS shader.
|
|
float4 VertexFactoryGetWorldPosition_Voxelization(FVertexFactoryInput Input, FHairViewInfo HairViewInfo, inout float OutHairWorldRadius, inout float OutHairDensity)
|
|
{
|
|
const FVertexInfo VertexInfo = GetVertexInfo(Input);
|
|
|
|
FHairControlPoint ControlPoint = GetVertexPosition(VertexInfo);
|
|
OutHairWorldRadius = ControlPoint.WorldRadius;
|
|
OutHairDensity = HairStrandsVF.Density;
|
|
|
|
FVertexFactoryIntermediates Intermediates = GetVertexFactoryIntermediates(Input);
|
|
const float4 WorldPosition = CalcWorldPosition(ControlPoint.Position, Intermediates.SceneData.Primitive.LocalToWorld);
|
|
const float3 WorldTangent = GetTangent(Intermediates);
|
|
|
|
// Minimal radius to snap the strand to a sample/pixel center (to avoid aliasing)
|
|
const float3 ViewDir = -HairViewInfo.ViewForward;
|
|
const float3 Right = normalize(cross(WorldTangent, ViewDir));
|
|
const float3 OutWorldPosition = WorldPosition.xyz + (VertexInfo.IsLeft ? -Right : Right) * max(ControlPoint.WorldRadius, HairViewInfo.RadiusAtDepth1);
|
|
|
|
return float4(OutWorldPosition, 1);
|
|
}
|
|
|
|
// @return translated world position
|
|
float4 VertexFactoryGetWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, FHairViewInfo HairViewInfo)
|
|
{
|
|
FHairControlPoint ControlPoint = GetVertexPosition(Input);
|
|
const float3 VertexPosition = ControlPoint.Position;
|
|
const float4 WorldPosition = CalcWorldPosition(VertexPosition, Intermediates.SceneData.Primitive.LocalToWorld);
|
|
|
|
// Hair shader expect the WorldNormal to be the tangent vector
|
|
const float3 WorldTangent = GetTangent(Intermediates);
|
|
return ComputeViewAlignedWorldPosition(Input, WorldTangent, WorldPosition, ControlPoint.WorldRadius, HairViewInfo);
|
|
}
|
|
|
|
float3 VertexFactoryGetInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, FHairViewInfo HairViewInfo)
|
|
{
|
|
float4 TranslatedWorldPosition = VertexFactoryGetWorldPosition(Input, Intermediates, HairViewInfo);
|
|
float4x4 TranslatedWorldToLocal = DFFastToTranslatedWorld(Intermediates.SceneData.Primitive.WorldToLocal, ResolvedView.PreViewTranslation);
|
|
float4 LocalPosition = mul(TranslatedWorldPosition, TranslatedWorldToLocal);
|
|
return LocalPosition.xyz;
|
|
}
|
|
|
|
// This function is referenced by several "system" shaders, but none of these shaders (apart HitProxy, are actually used)
|
|
float4 VertexFactoryGetWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
|
|
{
|
|
const FHairRenderInfo HairRenderInfo = GetHairRenderInfo(ResolvedView.HairRenderInfo, ResolvedView.HairRenderInfoBits, UseStableRasterization());
|
|
|
|
FHairViewInfo HairViewInfo;
|
|
HairViewInfo.TranslatedWorldCameraOrigin = ResolvedView.TranslatedWorldCameraOrigin;
|
|
HairViewInfo.ViewForward = ResolvedView.ViewForward;
|
|
HairViewInfo.RadiusAtDepth1 = HairRenderInfo.RadiusAtDepth1Primary;
|
|
HairViewInfo.bIsOrthoView = HairRenderInfo.bIsOrthoView;
|
|
|
|
return VertexFactoryGetWorldPosition(Input, Intermediates, HairViewInfo);
|
|
}
|
|
|
|
float3 VertexFactoryGetInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
|
|
{
|
|
float4 TranslatedWorldPosition = VertexFactoryGetWorldPosition(Input, Intermediates);
|
|
float4x4 TranslatedWorldToLocal = DFFastToTranslatedWorld(Intermediates.SceneData.Primitive.WorldToLocal, ResolvedView.PreViewTranslation);
|
|
float4 LocalPosition = mul(TranslatedWorldPosition, TranslatedWorldToLocal);
|
|
return LocalPosition.xyz;
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
|
|
// Initialize the whole struct to 0
|
|
// Really only the last two components of the packed UVs have the opportunity to be uninitialized
|
|
Interpolants = (FVertexFactoryInterpolantsVSToPS)0;
|
|
|
|
SetTangents(Interpolants, Intermediates.TangentToWorld[0], Intermediates.TangentToWorld[2], Intermediates.TangentToWorldSign);
|
|
|
|
Interpolants.HairControlPointId = Intermediates.HairControlPointId;
|
|
Interpolants.HairPrimitiveUV = Intermediates.HairPrimitiveUV;
|
|
SetPrimitiveId(Interpolants, Intermediates.SceneData.PrimitiveId);
|
|
|
|
return Interpolants;
|
|
}
|
|
|
|
/** for depth-only pass (Not used by the actual hair shaders)*/
|
|
float4 VertexFactoryGetWorldPosition(FVertexFactoryInput Input)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// @return translated world position (without quad extension/reorientation).This is used only for velocity computation
|
|
float4 VertexFactoryGetWorldPositionRaw(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, bool bInvalidJointVertex)
|
|
{
|
|
return CalcWorldPosition(GetVertexPosition(Input, bInvalidJointVertex).Position, Intermediates.SceneData.Primitive.LocalToWorld);
|
|
}
|
|
float4 VertexFactoryGetWorldPositionRaw(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
|
|
{
|
|
return VertexFactoryGetWorldPositionRaw(Input, Intermediates, true);
|
|
}
|
|
|
|
// @return previous translated world position
|
|
float4 VertexFactoryGetPreviousWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, bool bInvalidJointVertex)
|
|
{
|
|
const float3 VertexPosition = GetVertexPreviousPosition(Input, bInvalidJointVertex);
|
|
return DFTransformLocalToTranslatedWorld(VertexPosition, Intermediates.SceneData.Primitive.PreviousLocalToWorld, ResolvedView.PrevPreViewTranslation);
|
|
}
|
|
float4 VertexFactoryGetPreviousWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
|
|
{
|
|
return VertexFactoryGetPreviousWorldPosition(Input, Intermediates, true);
|
|
}
|
|
|
|
float3 VertexFactoryGetPreviousInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, bool bInvalidJointVertex)
|
|
{
|
|
return GetVertexPreviousPosition(Input, bInvalidJointVertex);
|
|
}
|
|
|
|
float4 VertexFactoryGetTranslatedPrimitiveVolumeBounds(FVertexFactoryInterpolantsVSToPS Interpolants)
|
|
{
|
|
FPrimitiveSceneData PrimitiveData = GetPrimitiveData(GetPrimitiveId(Interpolants));
|
|
return float4(DFFastToTranslatedWorld(PrimitiveData.ObjectWorldPosition, ResolvedView.PreViewTranslation), PrimitiveData.ObjectRadius);
|
|
}
|
|
|
|
uint VertexFactoryGetPrimitiveId(FVertexFactoryInterpolantsVSToPS Interpolants)
|
|
{
|
|
return GetPrimitiveId(Interpolants);
|
|
}
|
|
|
|
float3 VertexFactoryGetWorldNormal(FVertexFactoryInput Input)
|
|
{
|
|
return float3(0, 0, 1);
|
|
}
|
|
|
|
float3 VertexFactoryGetWorldNormal(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
|
|
{
|
|
return float3(0,0,1);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Regular material evalution for raytracing
|
|
|
|
#if RAYHITGROUP || COMPUTESHADER
|
|
FVertexFactoryInput LoadVertexFactoryInputForDynamicUpdate(uint TriangleIndex, int VertexIndex, uint PrimitiveId, uint DrawInstanceId)
|
|
{
|
|
FVertexFactoryInput Input;
|
|
|
|
Input.VertexId = TriangleIndex * 3 + VertexIndex;
|
|
|
|
return Input;
|
|
}
|
|
|
|
uint GetNumRayTracingDynamicMeshVerticesIndirect()
|
|
{
|
|
return 0;
|
|
}
|
|
#endif // RAYHITGROUP || COMPUTESHADER
|
|
|
|
#if NEEDS_VERTEX_FACTORY_INTERPOLATION
|
|
|
|
struct FVertexFactoryRayTracingInterpolants
|
|
{
|
|
FVertexFactoryInterpolantsVSToPS InterpolantsVSToPS;
|
|
};
|
|
|
|
float2 VertexFactoryGetRayTracingTextureCoordinate(FVertexFactoryRayTracingInterpolants Interpolants)
|
|
{
|
|
#if NUM_MATERIAL_TEXCOORDS
|
|
return Interpolants.InterpolantsVSToPS.TexCoords[0].xy;
|
|
#else // #if NUM_MATERIAL_TEXCOORDS
|
|
return float2(0, 0);
|
|
#endif // #if NUM_MATERIAL_TEXCOORDS
|
|
}
|
|
|
|
FVertexFactoryInterpolantsVSToPS VertexFactoryAssignInterpolants(FVertexFactoryRayTracingInterpolants Input)
|
|
{
|
|
return Input.InterpolantsVSToPS;
|
|
}
|
|
|
|
FVertexFactoryRayTracingInterpolants VertexFactoryGetRayTracingInterpolants(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, FMaterialVertexParameters VertexParameters)
|
|
{
|
|
FVertexFactoryRayTracingInterpolants Interpolants;
|
|
Interpolants.InterpolantsVSToPS = VertexFactoryGetInterpolantsVSToPS(Input, Intermediates, VertexParameters);
|
|
return Interpolants;
|
|
}
|
|
|
|
FVertexFactoryRayTracingInterpolants VertexFactoryInterpolate(FVertexFactoryRayTracingInterpolants a, float aInterp, FVertexFactoryRayTracingInterpolants b, float bInterp)
|
|
{
|
|
// Default initialize. Otherwise, some graphics pipelines that
|
|
// couple tessellation with geometry shaders won't write to all TEXCOORD semantics,
|
|
// but read from them when <FVertexFactoryRayTracingInterpolants> is being copied as a whole.
|
|
FVertexFactoryRayTracingInterpolants O = (FVertexFactoryRayTracingInterpolants)0;
|
|
|
|
#if VF_USE_PRIMITIVE_SCENE_DATA
|
|
O.InterpolantsVSToPS.PrimitiveId = a.InterpolantsVSToPS.PrimitiveId;
|
|
#if NEEDS_LIGHTMAP_COORDINATE
|
|
O.InterpolantsVSToPS.LightmapDataIndex = a.InterpolantsVSToPS.LightmapDataIndex;
|
|
#endif
|
|
#endif
|
|
// Do we really need to interpolate TangentToWorld2 here? It should be replaced by the
|
|
// interpolated normal from 'whatever' interpolation scheme we're using
|
|
|
|
INTERPOLATE_MEMBER(InterpolantsVSToPS.TangentToWorld0.xyz);
|
|
INTERPOLATE_MEMBER(InterpolantsVSToPS.TangentToWorld2);
|
|
#if INTERPOLATE_VERTEX_COLOR
|
|
INTERPOLATE_MEMBER(InterpolantsVSToPS.Color);
|
|
#endif
|
|
#if NEEDS_PER_INSTANCE_PARAMS
|
|
INTERPOLATE_MEMBER(InterpolantsVSToPS.PerInstanceParams);
|
|
#endif
|
|
|
|
#if NEEDS_LIGHTMAP_COORDINATE
|
|
INTERPOLATE_MEMBER(InterpolantsVSToPS.LightMapCoordinate);
|
|
#endif
|
|
|
|
#if NUM_TEX_COORD_INTERPOLATORS
|
|
UNROLL
|
|
for (int tc = 0; tc < (NUM_TEX_COORD_INTERPOLATORS + 1) / 2; ++tc)
|
|
{
|
|
INTERPOLATE_MEMBER(InterpolantsVSToPS.TexCoords[tc]);
|
|
}
|
|
#elif USE_PARTICLE_SUBUVS
|
|
INTERPOLATE_MEMBER(InterpolantsVSToPS.TexCoords[0]);
|
|
#endif
|
|
|
|
O.InterpolantsVSToPS.HairControlPointId = a.InterpolantsVSToPS.HairControlPointId;
|
|
INTERPOLATE_MEMBER(InterpolantsVSToPS.HairPrimitiveUV);
|
|
return O;
|
|
}
|
|
#endif // NEEDS_VERTEX_FACTORY_INTERPOLATION
|
|
|
|
#include "/Engine/Private/VertexFactoryDefaultInterface.ush"
|