Files
UnrealEngineUWP/Engine/Shaders/PNTriangles.usf
Brian Karis 8d5c885510 Improved tessellation performance. Raised max tessellation factor to 15.
[CL 2391556 by Brian Karis in Main branch]
2014-12-17 14:57:53 -05:00

248 lines
9.9 KiB
Plaintext

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
PNTriangles.usf: Shader fragments for PN-AEN triangles tessellation
=============================================================================*/
#define IN_CONTROL_POINTS 12
#define OUT_CONTROL_POINTS 3
struct FHullShaderConstantOutput
{
// Tess factor for the FF HW block
float TessFactor[3] : SV_TessFactor;
float InsideTessFactor : SV_InsideTessFactor;
float4 WorldB111 : PN_POSITION9;
};
struct FPNTessellationHSToDS
{
FPassSpecificVSToDS PassSpecificData;
//@todo - Should be storing View position, then we can do a faster projection matrix multiply in the
//domain shader
float4 WorldPosition[3] : PN_POSITION;
// scale factors in tangent space
float3 DisplacementScale : PN_DisplacementScales;
// Tessellation multiplier.
float TessellationMultiplier : PN_TessellationMultiplier;
#if DISPLACEMENT_ANTICRACK
// dominant edge and vertex data used to avoid cracking when displacing
FHullShaderConstantDominantVertexData DominantVertex : PN_DominantVertex;
FHullShaderConstantDominantEdgeData DominantEdge : PN_DominantEdge;
#endif
};
float4 ComputeControlPoint(float4 PositionA, float4 PositionB, float4 NormalA)
{
return (2 * PositionA + PositionB - (dot((PositionB - PositionA), NormalA) * NormalA)) / 3.0f;
}
// GLSL needs those on the domain shader, HLSL on the hull shader, so we replicate them for both
#define TESSELLATION_ATTRIBUTES [partitioning("fractional_odd")][outputtopology("triangle_cw")]
#if HULLSHADER
// Note that we input all control point from AEN, but only their PassSpecificData is valid (see MainHull shader)
FHullShaderConstantOutput PNSharedConstantHull( const OutputPatch<FPNTessellationHSToDS, OUT_CONTROL_POINTS> I )
{
FHullShaderConstantOutput O = (FHullShaderConstantOutput)0;
#if DISABLE_TESSELLATION_OVERRIDE
O.TessFactor[0] = 1.0f; // 1->2 edge
O.TessFactor[1] = 1.0f; // 2->0 edge
O.TessFactor[2] = 1.0f; // 0->1 edge
O.InsideTessFactor = 1.0f;
#else // #if DISABLE_TESSELLATION_OVERRIDE
float4 B300 = I[0].WorldPosition[0],
B210 = I[0].WorldPosition[1],
B120 = I[0].WorldPosition[2],
B030 = I[1].WorldPosition[0],
B021 = I[1].WorldPosition[1],
B012 = I[1].WorldPosition[2],
B003 = I[2].WorldPosition[0],
B102 = I[2].WorldPosition[1],
B201 = I[2].WorldPosition[2];
float4 E = (B210 + B120 + B021 + B012 + B102 + B201) / 6.0f;
float4 V = (B003 + B030 + B300) / 3.0f;
O.WorldB111 = E + ((E - V) / 2.0f);
float4 TessellationMultipliers;
TessellationMultipliers.x = 0.5 * (I[0].TessellationMultiplier + I[1].TessellationMultiplier);
TessellationMultipliers.y = 0.5 * (I[1].TessellationMultiplier + I[2].TessellationMultiplier);
TessellationMultipliers.z = 0.5 * (I[2].TessellationMultiplier + I[0].TessellationMultiplier);
TessellationMultipliers.w = 0.333 * (I[0].TessellationMultiplier + I[1].TessellationMultiplier + I[2].TessellationMultiplier);
//@todo: This should be vectorized and done in the MainHull shader.
float4 CompositeTessellationFactors = TessellationMultipliers * CalculateCompositeTessellationFactors(B300.xyz,B030.xyz,B003.xyz);
CompositeTessellationFactors = clamp( CompositeTessellationFactors, 1, 15 );
O.TessFactor[0] = CompositeTessellationFactors.x; // 1->2 edge
O.TessFactor[1] = CompositeTessellationFactors.y; // 2->0 edge
O.TessFactor[2] = CompositeTessellationFactors.z; // 0->1 edge
O.InsideTessFactor = CompositeTessellationFactors.w;
#endif // #if DISABLE_TESSELLATION_OVERRIDE
return O;
}
[domain("tri")]
[patchconstantfunc("PNSharedConstantHull")]
[outputcontrolpoints(OUT_CONTROL_POINTS)]
[maxtessfactor(15)]
TESSELLATION_ATTRIBUTES
FPNTessellationHSToDS MainHull( InputPatch<FPassSpecificVSToDS, IN_CONTROL_POINTS> I, uint ControlPointID : SV_OutputControlPointID )
{
FPNTessellationHSToDS O = (FPNTessellationHSToDS) 0;
float3x3 TangentToLocal = VertexFactoryGetTangentToLocalDS(I[ControlPointID].FactoryInterpolants);
float4 Normal = GetNormalDS(TangentToLocal);
O.DisplacementScale = GetTangentSpaceNonUniformScales(TangentToLocal);
const uint NextControlPointID = ControlPointID < 2 ? ControlPointID + 1 : 0; // (ControlPointID + 1) % 3
const uint NeighborControlPointID = 3 + 2 * ControlPointID;
const uint NeighborNextControlPointID = NeighborControlPointID + 1;
O.PassSpecificData = I[ControlPointID];
O.WorldPosition[0] = I[ControlPointID].Position;
O.TessellationMultiplier = GetTessellationMultiplier( I[ControlPointID] );
#if DISPLACEMENT_ANTICRACK
// NextControlPointID is first opposite edge vert
// NextNextControlPointID is second opposite edge vert
const uint NextNextControlPointID = NextControlPointID < 2 ? NextControlPointID + 1 : 0; // (NextControlPointID + 1) % 3
// we also need the neghbors of the opposite edge verts
const uint NextNeighborControlPointID = 3 + 2 * NextControlPointID; // neighbor for first opposite edge vert
const uint NextNeighborNextControlPointID = NextNeighborControlPointID+1; // neighbor for second opposite edge vert
// Dominant vertex is provided explicitly
const uint DominantVertexControlPointID = 9 + ControlPointID;
O.DominantVertex = GenerateDominantVertexData(I[DominantVertexControlPointID]);
// we need the other verts from each prim to generate the edges, clock wise winding
O.DominantEdge = GenerateDominantEdgeData(I[NextControlPointID],I[NextNextControlPointID],I[NextNeighborControlPointID],I[NextNeighborNextControlPointID]);
#endif // DISPLACEMENT_ANTICRACK
// AEN Computations
float4 NextNormal = GetNormalDS(VertexFactoryGetTangentToLocalDS(I[NextControlPointID].FactoryInterpolants));
float4 NeighborNormal = GetNormalDS(VertexFactoryGetTangentToLocalDS(I[NeighborControlPointID].FactoryInterpolants));
float4 NeighborNextNormal = GetNormalDS(VertexFactoryGetTangentToLocalDS(I[NeighborNextControlPointID].FactoryInterpolants));
float4 ThisControlPoint, NeighborControlPoint;
ThisControlPoint = ComputeControlPoint(I[ControlPointID].Position, I[NextControlPointID].Position, Normal);
NeighborControlPoint = ComputeControlPoint(I[NeighborControlPointID].Position, I[NeighborNextControlPointID].Position, NeighborNormal);
O.WorldPosition[1] = (ThisControlPoint + NeighborControlPoint) / 2;
ThisControlPoint = ComputeControlPoint(I[NextControlPointID].Position, I[ControlPointID].Position, NextNormal);
NeighborControlPoint = ComputeControlPoint(I[NeighborNextControlPointID].Position, I[NeighborControlPointID].Position, NeighborNextNormal);
O.WorldPosition[2] = (ThisControlPoint + NeighborControlPoint) / 2;
return O;
}
#endif
#if DOMAINSHADER
[domain("tri")]
TESSELLATION_ATTRIBUTES
FPassSpecificVSToPS MainDomain(
FHullShaderConstantOutput HSConstantData,
const OutputPatch<FPNTessellationHSToDS, OUT_CONTROL_POINTS> I,
float3 BarycentricCoords : SV_DomainLocation
)
{
// Get the barycentric coords
float U = BarycentricCoords.x;
float V = BarycentricCoords.y;
float W = BarycentricCoords.z;
// Precompute squares and squares * 3
float UU = U * U;
float VV = V * V;
float WW = W * W;
float UU3 = UU * 3.0f;
float VV3 = VV * 3.0f;
float WW3 = WW * 3.0f;
float4 B300 = I[0].WorldPosition[0],
B210 = I[0].WorldPosition[1],
B120 = I[0].WorldPosition[2],
B030 = I[1].WorldPosition[0],
B021 = I[1].WorldPosition[1],
B012 = I[1].WorldPosition[2],
B003 = I[2].WorldPosition[0],
B102 = I[2].WorldPosition[1],
B201 = I[2].WorldPosition[2];
// Compute position from cubic control points and barycentric coords
float4 WorldPosition =
B300 * UU * U +
B030 * VV * V +
B003 * WW * W +
B210 * UU3 * V +
B120 * VV3 * U +
B021 * VV3 * W +
B012 * WW3 * V +
B102 * WW3 * U +
B201 * UU3 * W +
HSConstantData.WorldB111 * 6.0f * W * U * V;
// Interp remaining attributes (construct barycentric interp from bilerp primitives)
// NB: the HLSL compiler resolves the 1.f to something efficient (i.e. no-op)
FPassSpecificVSToDS Interp = PassInterpolate( PassInterpolate(I[0].PassSpecificData, U, I[1].PassSpecificData, V), 1.f, I[2].PassSpecificData, W );
// get our base material params that we will potentially override
FMaterialTessellationParameters MaterialParameters = GetMaterialTessellationParameters(Interp.FactoryInterpolants, WorldPosition.xyz); // VF ops
#if DISPLACEMENT_ANTICRACK
float2 DisplacementTexCoords = 0;
#if NUM_MATERIAL_TEXCOORDS
DisplacementTexCoords = GetTextureCoordinateDS( Interp );
#endif
FMaterialTessellationParameters DisplacementMaterialParameters = MaterialParameters;
float3x3 TangentToLocal = VertexFactoryGetTangentToLocalDS(Interp.FactoryInterpolants);
// Override to dominant data if along edge or at control vert. This is necessary to avoid cracks causes by primitives
// from disjoint locations in UV space abutting each other.
ApplyDominantData( DisplacementTexCoords, DisplacementMaterialParameters.TangentToWorld,
GetNormalDS( TangentToLocal ), GetTangentDS( TangentToLocal ),
U, V, W,
I[0].DominantEdge, I[1].DominantEdge, I[2].DominantEdge,
I[0].DominantVertex, I[1].DominantVertex, I[2].DominantVertex
);
// Override texture coordinate 0.
#if NUM_MATERIAL_TEXCOORDS
DisplacementMaterialParameters.TexCoords[0].xy = DisplacementTexCoords.xy;
#endif
#else
FMaterialTessellationParameters DisplacementMaterialParameters = MaterialParameters;
#endif // DISPLACEMENT_ANTICRACK
// tangent space displacement scaling
DisplacementMaterialParameters.TangentToWorldPreScale = I[0].DisplacementScale*U + I[1].DisplacementScale*V + I[2].DisplacementScale*W;
// world space displacement value. This will likely use the tangent2world transform.
float3 WorldDisplacement = GetMaterialWorldDisplacement(DisplacementMaterialParameters);
WorldPosition.xyz += WorldDisplacement;
return PassFinalizeTessellationOutput(Interp, WorldPosition, MaterialParameters);
}
#endif