154 lines
4.9 KiB
HLSL
154 lines
4.9 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "DualNumber.ush"
|
|
|
|
#ifndef NANITE_TESSELLATION
|
|
#define NANITE_TESSELLATION 0
|
|
#endif
|
|
|
|
typedef TDual< float3 > FBarycentrics;
|
|
|
|
template< typename T >
|
|
TDual<T> Lerp( T Value0, T Value1, T Value2, FBarycentrics Barycentrics )
|
|
{
|
|
TDual<T> Result;
|
|
Result.Value = Value0 * Barycentrics.Value.x + Value1 * Barycentrics.Value.y + Value2 * Barycentrics.Value.z;
|
|
Result.Value_dx = Value0 * Barycentrics.Value_dx.x + Value1 * Barycentrics.Value_dx.y + Value2 * Barycentrics.Value_dx.z;
|
|
Result.Value_dy = Value0 * Barycentrics.Value_dy.x + Value1 * Barycentrics.Value_dy.y + Value2 * Barycentrics.Value_dy.z;
|
|
|
|
#if NANITE_TESSELLATION
|
|
Result.Value = INVARIANT( Result.Value );
|
|
#endif
|
|
|
|
return Result;
|
|
}
|
|
|
|
/** Calculates perspective correct barycentric coordinates and partial derivatives using screen derivatives. */
|
|
FBarycentrics CalculateTriangleBarycentrics(float2 PixelClip, float4 PointClip0, float4 PointClip1, float4 PointClip2, float2 ViewInvSize)
|
|
{
|
|
FBarycentrics Barycentrics;
|
|
|
|
const float3 RcpW = rcp(float3(PointClip0.w, PointClip1.w, PointClip2.w));
|
|
const float3 Pos0 = PointClip0.xyz * RcpW.x;
|
|
const float3 Pos1 = PointClip1.xyz * RcpW.y;
|
|
const float3 Pos2 = PointClip2.xyz * RcpW.z;
|
|
|
|
const float3 Pos120X = float3(Pos1.x, Pos2.x, Pos0.x);
|
|
const float3 Pos120Y = float3(Pos1.y, Pos2.y, Pos0.y);
|
|
const float3 Pos201X = float3(Pos2.x, Pos0.x, Pos1.x);
|
|
const float3 Pos201Y = float3(Pos2.y, Pos0.y, Pos1.y);
|
|
|
|
const float3 C_dx = Pos201Y - Pos120Y;
|
|
const float3 C_dy = Pos120X - Pos201X;
|
|
|
|
const float3 C = C_dx * (PixelClip.x - Pos120X) + C_dy * (PixelClip.y - Pos120Y); // Evaluate the 3 edge functions
|
|
const float3 G = C * RcpW;
|
|
|
|
const float H = dot(C, RcpW);
|
|
const float RcpH = rcp(H);
|
|
|
|
// UVW = C * RcpW / dot(C, RcpW)
|
|
Barycentrics.Value = G * RcpH;
|
|
|
|
// Texture coordinate derivatives:
|
|
// UVW = G / H where G = C * RcpW and H = dot(C, RcpW)
|
|
// UVW' = (G' * H - G * H') / H^2
|
|
// float2 TexCoordDX = UVW_dx.y * TexCoord10 + UVW_dx.z * TexCoord20;
|
|
// float2 TexCoordDY = UVW_dy.y * TexCoord10 + UVW_dy.z * TexCoord20;
|
|
const float3 G_dx = C_dx * RcpW;
|
|
const float3 G_dy = C_dy * RcpW;
|
|
|
|
const float H_dx = dot(C_dx, RcpW);
|
|
const float H_dy = dot(C_dy, RcpW);
|
|
|
|
Barycentrics.Value_dx = (G_dx * H - G * H_dx) * (RcpH * RcpH) * ( 2.0f * ViewInvSize.x);
|
|
Barycentrics.Value_dy = (G_dy * H - G * H_dy) * (RcpH * RcpH) * (-2.0f * ViewInvSize.y);
|
|
|
|
return Barycentrics;
|
|
}
|
|
|
|
float3 RayTriangleIntersectBarycentrics( float3 o, float3 rd, float3 v0, float3 v1, float3 v2 )
|
|
{
|
|
// Muller-Trumbore ray triangle intersect
|
|
float3 Edge01 = v1 - v0;
|
|
float3 Edge02 = v2 - v0;
|
|
float3 Origin0 = o - v0;
|
|
|
|
float3 Dirx02 = cross( rd, Edge02 );
|
|
float InvDet = 1.0 / dot( Edge01, Dirx02 );
|
|
|
|
float3 UVW;
|
|
UVW.y = InvDet * dot( Origin0, Dirx02 );
|
|
UVW.z = InvDet * dot( rd, cross( Origin0, Edge01) );
|
|
UVW.x = 1.0 - UVW.y - UVW.z;
|
|
return UVW;
|
|
}
|
|
|
|
float3 ClosestPointBarycentrics( float3 p, float3 v0, float3 v1, float3 v2 )
|
|
{
|
|
// Project on to triangle along normal
|
|
float3 TriNormal = cross( v2 - v0, v1 - v0 );
|
|
|
|
return RayTriangleIntersectBarycentrics(p, TriNormal, v0, v1, v2);
|
|
}
|
|
|
|
FBarycentrics CalculateTriangleBarycentrics(
|
|
float3 CameraLocal,
|
|
float3 PositionPixel,
|
|
float3 PositionPixelX,
|
|
float3 PositionPixelY,
|
|
float3 Position0,
|
|
float3 Position1,
|
|
float3 Position2,
|
|
float3 Normal0,
|
|
float3 Normal1,
|
|
float3 Normal2 )
|
|
{
|
|
/*
|
|
Displacement mapping
|
|
|
|
PositionPixel =
|
|
U * ( Position0 + Normal0 * Height ) +
|
|
V * ( Position1 + Normal1 * Height ) +
|
|
W * ( Position2 + Normal2 * Height );
|
|
|
|
Solve for Height
|
|
*/
|
|
|
|
// 4 looks perfect but 2 seems good enough
|
|
uint NumIterations = 2;
|
|
for( uint j = 0; j < NumIterations; j++ )
|
|
{
|
|
float3 UVW = ClosestPointBarycentrics( PositionPixel, Position0, Position1, Position2 );
|
|
UVW = max( 0, UVW );
|
|
UVW /= dot( UVW, 1 );
|
|
|
|
float3 ClosestPoint;
|
|
ClosestPoint = Position0 * UVW.x;
|
|
ClosestPoint += Position1 * UVW.y;
|
|
ClosestPoint += Position2 * UVW.z;
|
|
|
|
float3 ClosestNormal;
|
|
ClosestNormal = Normal0 * UVW.x;
|
|
ClosestNormal += Normal1 * UVW.y;
|
|
ClosestNormal += Normal2 * UVW.z;
|
|
|
|
float Displace = dot( PositionPixel - ClosestPoint, ClosestNormal ) / dot( ClosestNormal, ClosestNormal );
|
|
|
|
Position0 += Normal0 * Displace;
|
|
Position1 += Normal1 * Displace;
|
|
Position2 += Normal2 * Displace;
|
|
}
|
|
|
|
FBarycentrics Barycentrics;
|
|
Barycentrics.Value = ClosestPointBarycentrics( PositionPixel, Position0, Position1, Position2 );
|
|
|
|
// Take estimate for position at dx and dy and project onto the displaced triangle along the view ray
|
|
// We do this to correct for not including any the depth delta in our original estimation
|
|
Barycentrics.Value_dx = RayTriangleIntersectBarycentrics( CameraLocal, PositionPixelX - CameraLocal, Position0, Position1, Position2 ) - Barycentrics.Value;
|
|
Barycentrics.Value_dy = RayTriangleIntersectBarycentrics( CameraLocal, PositionPixelY - CameraLocal, Position0, Position1, Position2 ) - Barycentrics.Value;
|
|
|
|
return Barycentrics;
|
|
} |