浏览代码

Merge pull request #99 from Unity-Technologies/Improve-tessellation-efficiency

HDRenderPipeline: Add distance and screen adapation + backface culling
/main
GitHub 8 年前
当前提交
9a72bdd8
共有 7 个文件被更改,包括 165 次插入116 次删除
  1. 9
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/LayeredLit/LayeredLitTessellation.shader
  2. 22
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Editor/BaseLitUI.cs
  3. 3
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/LitProperties.hlsl
  4. 41
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/LitTessellation.hlsl
  5. 9
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/LitTessellation.shader
  6. 55
      Assets/ScriptableRenderLoop/HDRenderPipeline/ShaderPass/TessellationShare.hlsl
  7. 142
      Assets/ScriptableRenderLoop/ShaderLibrary/Tessellation.hlsl

9
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/LayeredLit/LayeredLitTessellation.shader


// Tesselation specific
[Enum(Phong, 0, Displacement, 1, DisplacementPhong, 2)] _TessellationMode("Tessellation mode", Float) = 1
_TessellationFactorFixed("Tessellation Factor", Float) = 4.0 // if != -1.0 force fixed factor
_TessellationFactorMaxDistance("Tessellation max distance factor", Float) = 12.0
_TessellationFactorTriangleSize("Tessellation triangle size", Float) = 20.0
_TessellationFactor("Tessellation Factor", Range(0.0, 15.0)) = 4.0
_TessellationFactorMinDistance("Tessellation start fading distance", Float) = 20.0
_TessellationFactorMaxDistance("Tessellation end fading distance", Float) = 50.0
_TessellationFactorTriangleSize("Tessellation triangle size", Float) = 100.0
_TessellationBackFaceCullEpsilon("Tessellation back face epsilon", Range(-1.0, 1.0)) = 0.25
_TessellationBackFaceCullEpsilon("Tessellation back face epsilon", Range(-1.0, 0.0)) = -0.25
[ToggleOff] _TessellationObjectScale("Tessellation object scale", Float) = 0.0
// TODO: Handle culling mode for backface culling
}

22
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Editor/BaseLitUI.cs


public static readonly string[] tessellationModeNames = Enum.GetNames(typeof(TessellationMode));
public static GUIContent tessellationText = new GUIContent("Tessellation options", "Tessellation options");
public static GUIContent tessellationFactorFixedText = new GUIContent("Fixed tessellation factor", "If non negative, this value is a fixed tessellation factor use for tessellation");
public static GUIContent tessellationFactorMaxDistanceText = new GUIContent("Max Distance", "Maximun distance to the camera where triangle are tesselated");
public static GUIContent tessellationFactorTriangleSizeText = new GUIContent("Triangle size", "Desired screen space sized of triangle. Smaller value mean smaller triangle.");
public static GUIContent tessellationFactorText = new GUIContent("Tessellation factor", "This value is the tessellation factor use for tessellation, higher mean more tessellated");
public static GUIContent tessellationFactorMinDistanceText = new GUIContent("Start fade distance", "Distance (in unity unit) at which the tessellation start to fade out. Must be inferior at Max distance");
public static GUIContent tessellationFactorMaxDistanceText = new GUIContent("End fade distance", "Maximum distance (in unity unit) to the camera where triangle are tessellated");
public static GUIContent tessellationFactorTriangleSizeText = new GUIContent("Triangle size", "Desired screen space sized of triangle (in pixel). Smaller value mean smaller triangle.");
public static GUIContent tessellationBackFaceCullEpsilonText = new GUIContent("Triangle culling Epsilon", "If non zero, backface culling is enabled for tessellation, smaller number mean more aggressive culling and better performance");
public static GUIContent tessellationBackFaceCullEpsilonText = new GUIContent("Triangle culling Epsilon", "If -1.0 back face culling is enabled for tessellation, higher number mean more aggressive culling and better performance");
public static GUIContent tessellationObjectScaleText = new GUIContent("Enable object scale", "Scale displacement taking into account the object scale");
}

GUILayout.Label(Styles.tessellationText, EditorStyles.boldLabel);
EditorGUI.indentLevel++;
TessellationModePopup();
m_MaterialEditor.ShaderProperty(tessellationFactorFixed, Styles.tessellationFactorFixedText);
m_MaterialEditor.ShaderProperty(tessellationFactor, Styles.tessellationFactorText);
m_MaterialEditor.ShaderProperty(tessellationFactorMinDistance, Styles.tessellationFactorMinDistanceText);
tessellationFactorMinDistance.floatValue = Math.Min(tessellationFactorMaxDistance.floatValue, tessellationFactorMinDistance.floatValue);
m_MaterialEditor.ShaderProperty(tessellationFactorTriangleSize, Styles.tessellationFactorTriangleSizeText);
if ((TessellationMode)tessellationMode.floatValue == TessellationMode.Phong ||
(TessellationMode)tessellationMode.floatValue == TessellationMode.DisplacementPhong)

// tessellation specific, silent if not found
tessellationMode = FindProperty(kTessellationMode, props, false);
tessellationFactorFixed = FindProperty(kTessellationFactorFixed, props, false);
tessellationFactor = FindProperty(kTessellationFactor, props, false);
tessellationFactorMinDistance = FindProperty(kTessellationFactorMinDistance, props, false);
tessellationFactorMaxDistance = FindProperty(kTessellationFactorMaxDistance, props, false);
tessellationFactorTriangleSize = FindProperty(kTessellationFactorTriangleSize, props, false);
tessellationShapeFactor = FindProperty(kTessellationShapeFactor, props, false);

// tessellation params
MaterialProperty tessellationMode = null;
const string kTessellationMode = "_TessellationMode";
MaterialProperty tessellationFactorFixed = null;
const string kTessellationFactorFixed = "_TessellationFactorFixed";
MaterialProperty tessellationFactor = null;
const string kTessellationFactor = "_TessellationFactor";
MaterialProperty tessellationFactorMinDistance = null;
const string kTessellationFactorMinDistance = "_TessellationFactorMinDistance";
MaterialProperty tessellationFactorMaxDistance = null;
const string kTessellationFactorMaxDistance = "_TessellationFactorMaxDistance";
MaterialProperty tessellationFactorTriangleSize = null;

3
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/LitProperties.hlsl


// Tessellation specific
#ifdef TESSELLATION_ON
float _TessellationFactorFixed;
float _TessellationFactor;
float _TessellationFactorMinDistance;
float _TessellationFactorMaxDistance;
float _TessellationFactorTriangleSize;
float _TessellationShapeFactor;

41
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/LitTessellation.hlsl


float4 TessellationEdge(float3 p0, float3 p1, float3 p2, float3 n0, float3 n1, float3 n2)
{
// if (_TessellationFactorFixed >= 0.0f)
// TODO: Handle inverse culling (for mirror)!
if (_TessellationBackFaceCullEpsilon > -0.99) // Is backface culling enabled ?
{
if (BackFaceCullTriangle(p0, p1, p2, _TessellationBackFaceCullEpsilon, _WorldSpaceCameraPos))
{
return float4(0.0, 0.0, 0.0, 0.0);
}
}
float3 tessFactor = float3(1.0, 1.0, 1.0);
// Aaptive tessellation
if (_TessellationFactorTriangleSize > 0.0)
// return _TessellationFactorFixed.xxxx;
// return a value between 0 and 1
tessFactor *= GetScreenSpaceTessFactor( p0, p1, p2, GetWorldToHClipMatrix(), _ScreenParams, _TessellationFactorTriangleSize);
}
if (_TessellationFactorMaxDistance > 0.0)
{
tessFactor *= GetDistanceBasedTessFactor(p0, p1, p2, _WorldSpaceCameraPos, _TessellationFactorMinDistance, _TessellationFactorMaxDistance);
return DistanceBasedTess(p0, p1, p2, 0.0, _TessellationFactorMaxDistance, _WorldSpaceCameraPos) * _TessellationFactorFixed.xxxx;
tessFactor *= _TessellationFactor;
// Clamp to be minimun 0.01
tessFactor.xyz = float3(max(0.01, tessFactor.x), max(0.01, tessFactor.y), max(0.01, tessFactor.z));
return CalcTriEdgeTessFactors(tessFactor);
}
float3 GetDisplacement(VaryingsMeshToDS input)

// TODO: For now just use Layer0, but we are suppose to apply the same heightmap blending than in the pixel shader
#ifdef _HEIGHTMAP
float height = (SAMPLE_LAYER_TEXTURE2D_LOD(ADD_ZERO_IDX(_HeightMap), ADD_ZERO_IDX(sampler_HeightMap), ADD_ZERO_IDX(layerTexCoord.base), 0).r - ADD_ZERO_IDX(_HeightCenter)) * ADD_ZERO_IDX(_HeightAmplitude);
// TODO test mip lod to reduce texture cache miss
// TODO: Move to camera relative and change distance to length
//float dist = distance(input.positionWS, cameraPosWS);
// No ddx/ddy to calculate LOD, use camera distance instead
//float fadeDist = _TessellationFactorMaxDistance - _TessellationFactorMinDistance;
//float heightMapLod = saturate((dist - _TessellationFactorMinDistance) / min(fadeDist, 0.01)) * 6; // 6 is an arbitrary number here
float heightMapLod = 0.0;
float height = (SAMPLE_LAYER_TEXTURE2D_LOD(ADD_ZERO_IDX(_HeightMap), ADD_ZERO_IDX(sampler_HeightMap), ADD_ZERO_IDX(layerTexCoord.base), heightMapLod).r - ADD_ZERO_IDX(_HeightCenter)) * ADD_ZERO_IDX(_HeightAmplitude);
#else
float height = 0.0;
#endif

9
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/LitTessellation.shader


// Tessellation specific
[Enum(Phong, 0, Displacement, 1, DisplacementPhong, 2)] _TessellationMode("Tessellation mode", Float) = 0
_TessellationFactorFixed("Tessellation Factor", Float) = 4.0 // if != -1.0 force fixed factor
_TessellationFactorMaxDistance("Tessellation max distance factor", Float) = 12.0
_TessellationFactorTriangleSize("Tessellation triangle size", Float) = 20.0
_TessellationFactor("Tessellation Factor", Range(0.0, 15.0)) = 4.0
_TessellationFactorMinDistance("Tessellation start fading distance", Float) = 20.0
_TessellationFactorMaxDistance("Tessellation end fading distance", Float) = 50.0
_TessellationFactorTriangleSize("Tessellation triangle size", Float) = 100.0
_TessellationBackFaceCullEpsilon("Tessellation back face epsilon", Range(-1.0, 1.0)) = 0.25
_TessellationBackFaceCullEpsilon("Tessellation back face epsilon", Range(-1.0, 0.0)) = -0.25
[ToggleOff] _TessellationObjectScale("Tessellation object scale", Float) = 0.0
// TODO: Handle culling mode for backface culling
}

55
Assets/ScriptableRenderLoop/HDRenderPipeline/ShaderPass/TessellationShare.hlsl


VaryingsToDS varying1 = UnpackVaryingsToDS(input[1]);
VaryingsToDS varying2 = UnpackVaryingsToDS(input[2]);
float4 tf = TessellationEdge( varying0.vmesh.positionWS, varying1.vmesh.positionWS, varying2.vmesh.positionWS,
#ifdef VARYINGS_DS_NEED_NORMAL
varying0.vmesh.normalWS, varying1.vmesh.normalWS, varying2.vmesh.normalWS
#else
float3(0.0, 0.0, 1.0), float3(0.0, 0.0, 1.0), float3(0.0, 0.0, 1.0)
#endif
);
float3 p0 = varying0.vmesh.positionWS;
float3 p1 = varying1.vmesh.positionWS;
float3 p2 = varying2.vmesh.positionWS;
TessellationFactors ouput;
ouput.edge[0] = tf.x;
ouput.edge[1] = tf.y;
ouput.edge[2] = tf.z;
ouput.inside = tf.w;
#ifdef VARYINGS_DS_NEED_NORMAL
float3 n0 = varying0.vmesh.normalWS;
float3 n1 = varying1.vmesh.normalWS;
float3 n2 = varying2.vmesh.normalWS;
#else
float3 n0 = float3(0.0, 0.0, 0.0);
float3 n1 = float3(0.0, 0.0, 0.0);
float3 n2 = float3(0.0, 0.0, 0.0);
#endif
float4 tf = TessellationEdge(p0, p1, p2, n0, n1, n2);
TessellationFactors output;
output.edge[0] = tf.x;
output.edge[1] = tf.y;
output.edge[2] = tf.z;
output.inside = tf.w;
return ouput;
return output;
}
[maxtessfactor(15.0)] // AMD recommand this value for GCN http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/05/GCNPerformanceTweets.pdf

// We have Phong tessellation in all case where we don't have displacement only
#ifndef _TESSELLATION_DISPLACEMENT
float3 p0 = varying0.vmesh.positionWS;
float3 p1 = varying1.vmesh.positionWS;
float3 p2 = varying2.vmesh.positionWS;
#ifdef VARYINGS_DS_NEED_NORMAL
float3 n0 = varying0.vmesh.normalWS;
float3 n1 = varying1.vmesh.normalWS;
float3 n2 = varying2.vmesh.normalWS;
#else
float3 n0 = float3(0.0, 0.0, 0.0);
float3 n1 = float3(0.0, 0.0, 0.0);
float3 n2 = float3(0.0, 0.0, 0.0);
#endif
varying0.vmesh.positionWS, varying1.vmesh.positionWS, varying2.vmesh.positionWS,
#ifdef VARYINGS_DS_NEED_NORMAL
varying0.vmesh.normalWS, varying1.vmesh.normalWS, varying2.vmesh.normalWS,
#else
float3(0.0, 0.0, 1.0), float3(0.0, 0.0, 1.0), float3(0.0, 0.0, 1.0),
#endif
p0, p1, p2, n0, n1, n2,
baryCoords, _TessellationShapeFactor);
#endif

142
Assets/ScriptableRenderLoop/ShaderLibrary/Tessellation.hlsl


return lerp(positionWS, phongPositionWS, shape);
}
// ---- utility functions
float CalcDistanceTessFactor(float3 positionWS, float minDist, float maxDist, float3 cameraPosWS)
// Reference: http://twvideo01.ubm-us.net/o1/vault/gdc10/slides/Bilodeau_Bill_Direct3D11TutorialTessellation.pdf
// Return true if the triangle must be culled
// backFaceCullEpsilon is the threshold of the dot product between view and normal ( < 0 mean we cull)
bool BackFaceCullTriangle(float3 p0, float3 p1, float3 p2, float backFaceCullEpsilon, float3 cameraPosWS)
float dist = distance(positionWS, cameraPosWS);
float f = clamp(1.0 - (dist - minDist) / (maxDist - minDist), 0.01, 1.0);
return f;
float3 edge0 = p1 - p0;
float3 edge2 = p2 - p0;
float3 N = normalize(cross(edge0, edge2));
float3 midpoint = (p0 + p1 + p2) / 3.0;
float3 V = normalize(cameraPosWS - midpoint);
return (dot(V, N) < backFaceCullEpsilon) ? true : false;
float4 UnityCalcTriEdgeTessFactors(float3 triVertexFactors)
float2 GetScreenSpacePosition(float3 positionWS, float4x4 viewProjectionMatrix, float4 screenParams)
float4 tess;
tess.x = 0.5 * (triVertexFactors.y + triVertexFactors.z);
tess.y = 0.5 * (triVertexFactors.x + triVertexFactors.z);
tess.z = 0.5 * (triVertexFactors.x + triVertexFactors.y);
tess.w = (triVertexFactors.x + triVertexFactors.y + triVertexFactors.z) / 3.0f;
return tess;
float4 positionCS = mul(viewProjectionMatrix, float4(positionWS, 1.0));
float2 positionSS = positionCS.xy / positionCS.w;
// TODO: Check if we need to invert y
return (positionSS * 0.5 + 0.5) * float2(screenParams.x, -screenParams.y);
}
// Compute both screen and distance based adaptation - return factor between 0 and 1
float3 GetScreenSpaceTessFactor(float3 p0, float3 p1, float3 p2, float4x4 viewProjectionMatrix, float4 screenParams, float triangleSize)
{
// Get screen space adaptive scale factor
float2 edgeScreenPosition0 = GetScreenSpacePosition(p0, viewProjectionMatrix, screenParams);
float2 edgeScreenPosition1 = GetScreenSpacePosition(p1, viewProjectionMatrix, screenParams);
float2 edgeScreenPosition2 = GetScreenSpacePosition(p2, viewProjectionMatrix, screenParams);
float EdgeScale = 1.0 / triangleSize; // Edge size in reality, but name is simpler
float3 tessFactor;
tessFactor.x = saturate(distance(edgeScreenPosition1, edgeScreenPosition2) * EdgeScale);
tessFactor.y = saturate(distance(edgeScreenPosition0, edgeScreenPosition2) * EdgeScale);
tessFactor.z = saturate(distance(edgeScreenPosition0, edgeScreenPosition1) * EdgeScale);
return tessFactor;
/*
float UnityCalcEdgeTessFactor(float3 wpos0, float3 wpos1, float edgeLen)
float3 GetDistanceBasedTessFactor(float3 p0, float3 p1, float3 p2, float3 cameraPosWS, float tessMinDist, float tessMaxDist)
// distance to edge center
float dist = distance(0.5 * (wpos0 + wpos1), _WorldSpaceCameraPos);
// length of the edge
float len = distance(wpos0, wpos1);
// edgeLen is approximate desired size in pixels
float f = max(len * _ScreenParams.y / (edgeLen * dist), 1.0);
return f;
float3 edgePosition0 = 0.5 * (p1 + p2);
float3 edgePosition1 = 0.5 * (p0 + p2);
float3 edgePosition2 = 0.5 * (p0 + p1);
// TODO: Move to camera relative and change distance to length
float dist0 = distance(edgePosition0, cameraPosWS);
float dist1 = distance(edgePosition1, cameraPosWS);
float dist2 = distance(edgePosition2, cameraPosWS);
// The saturate will handle the produced NaN in case min == max
float fadeDist = tessMaxDist - tessMinDist;
float3 tessFactor;
tessFactor.x = saturate(1.0 - (dist0 - tessMinDist) / fadeDist);
tessFactor.y = saturate(1.0 - (dist1 - tessMinDist) / fadeDist);
tessFactor.z = saturate(1.0 - (dist2 - tessMinDist) / fadeDist);
return tessFactor;
float4 CalcTriEdgeTessFactors(float3 triVertexFactors)
{
float4 tess;
tess.x = triVertexFactors.x;
tess.y = triVertexFactors.y;
tess.z = triVertexFactors.z;
tess.w = (triVertexFactors.x + triVertexFactors.y + triVertexFactors.z) / 3.0;
return tess;
}
/*
float UnityDistanceFromPlane(float3 pos, float4 plane)
{
float d = dot(float4(pos, 1.0f), plane);

// has to pass all 4 plane tests to be visible
return !all(planeTest);
}
*/
// ---- functions that compute tessellation factors
// Distance based tessellation:
// Tessellation level is "tess" before "minDist" from camera, and linearly decreases to 1
// up to "maxDist" from camera.
float4 DistanceBasedTess(float3 p0, float3 p1, float3 p2, float minDist, float maxDist, float3 cameraPosWS)
{
float3 f;
f.x = CalcDistanceTessFactor(p0, minDist, maxDist, cameraPosWS);
f.y = CalcDistanceTessFactor(p1, minDist, maxDist, cameraPosWS);
f.z = CalcDistanceTessFactor(p2, minDist, maxDist, cameraPosWS);
return UnityCalcTriEdgeTessFactors(f);
}
/*
// Desired edge length based tessellation:
// Approximate resulting edge length in pixels is "edgeLength".
// Does not take viewing FOV into account, just flat out divides factor by distance.
float4 UnityEdgeLengthBasedTess(float4 v0, float4 v1, float4 v2, float edgeLength)
{
float3 pos0 = mul(unity_ObjectToWorld, v0).xyz;
float3 pos1 = mul(unity_ObjectToWorld, v1).xyz;
float3 pos2 = mul(unity_ObjectToWorld, v2).xyz;
float4 tess;
tess.x = UnityCalcEdgeTessFactor(pos1, pos2, edgeLength);
tess.y = UnityCalcEdgeTessFactor(pos2, pos0, edgeLength);
tess.z = UnityCalcEdgeTessFactor(pos0, pos1, edgeLength);
tess.w = (tess.x + tess.y + tess.z) / 3.0f;
return tess;
}
// Same as UnityEdgeLengthBasedTess, but also does patch frustum culling:
// patches outside of camera's view are culled before GPU tessellation. Saves some wasted work.
float4 UnityEdgeLengthBasedTessCull(float4 v0, float4 v1, float4 v2, float edgeLength, float maxDisplacement)
{
float3 pos0 = mul(unity_ObjectToWorld, v0).xyz;
float3 pos1 = mul(unity_ObjectToWorld, v1).xyz;
float3 pos2 = mul(unity_ObjectToWorld, v2).xyz;
float4 tess;
if (UnityWorldViewFrustumCull(pos0, pos1, pos2, maxDisplacement))
{
tess = 0.0f;
}
else
{
tess.x = UnityCalcEdgeTessFactor(pos1, pos2, edgeLength);
tess.y = UnityCalcEdgeTessFactor(pos2, pos0, edgeLength);
tess.z = UnityCalcEdgeTessFactor(pos0, pos1, edgeLength);
tess.w = (tess.x + tess.y + tess.z) / 3.0f;
}
return tess;
}
*/
*/
正在加载...
取消
保存