浏览代码

Merge pull request #570 from EvgeniiG/Unity-2017.3

Fix cracks in tessellation
/Yibing-Project-2
GitHub 7 年前
当前提交
72c20791
共有 10 个文件被更改,包括 128 次插入104 次删除
  1. 6
      ScriptableRenderPipeline/Core/ShaderLibrary/Common.hlsl
  2. 74
      ScriptableRenderPipeline/Core/ShaderLibrary/GeometricTools.hlsl
  3. 74
      ScriptableRenderPipeline/Core/ShaderLibrary/Tessellation.hlsl
  4. 10
      ScriptableRenderPipeline/HDRenderPipeline/Camera/HDCamera.cs
  5. 1
      ScriptableRenderPipeline/HDRenderPipeline/Material/LayeredLit/LayeredLitTessellation.shader
  6. 54
      ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/LitDataMeshModification.hlsl
  7. 1
      ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/LitTessellation.shader
  8. 2
      ScriptableRenderPipeline/HDRenderPipeline/ShaderPass/TessellationShare.hlsl
  9. 4
      ScriptableRenderPipeline/HDRenderPipeline/ShaderVariables.hlsl
  10. 6
      ScriptableRenderPipeline/HDRenderPipeline/ShaderVariablesFunctions.hlsl

6
ScriptableRenderPipeline/Core/ShaderLibrary/Common.hlsl


return positionSS;
}
float2 ComputeScreenSpacePosition(float3 positionWS, float4x4 viewProjectionMatrix)
{
float4 positionCS = mul(viewProjectionMatrix, float4(positionWS, 1.0));
return ComputeScreenSpacePosition(positionCS);
}
float3 ComputeViewSpacePosition(float2 positionSS, float depthRaw, float4x4 invProjMatrix)
{
float4 positionCS = ComputeClipSpacePosition(positionSS, depthRaw);

74
ScriptableRenderPipeline/Core/ShaderLibrary/GeometricTools.hlsl


}
//-----------------------------------------------------------------------------
// Distance functions
// Miscellaneous functions
//-----------------------------------------------------------------------------
// Box is AABB

}
float3 ProjectPointOnPlane(float3 position, float3 planePosition, float3 planeNormal)
{
return position - (dot(position - planePosition, planeNormal) * planeNormal);
}
// Plane equation: {(a, b, c) = N, d = -dot(N, P)}.
// Returns the distance from the plane to the point 'p' along the normal.
// Positive -> in front (above), negative -> behind (below).
float DistanceFromPlane(float3 p, float4 plane)
{
return dot(float4(p, 1.0), plane);
}
// Returns 'true' if the triangle is outside of the frustum.
// 'epsilon' is the (negative) distance to (outside of) the frustum below which we cull the triangle.
bool CullTriangleFrustum(float3 p0, float3 p1, float3 p2, float epsilon, float4 frustumPlanes[6], int numPlanes)
{
bool outside = false;
for (int i = 0; i < numPlanes; i++)
{
// If all 3 points are behind any of the planes, we cull.
outside = outside || Max3(DistanceFromPlane(p0, frustumPlanes[i]),
DistanceFromPlane(p1, frustumPlanes[i]),
DistanceFromPlane(p2, frustumPlanes[i])) < epsilon;
}
return outside;
}
// Returns 'true' if the edge of the triangle is outside of the frustum.
// The edges are defined s.t. they are on the opposite side of the point with the given index.
// 'epsilon' is the (negative) distance to (outside of) the frustum below which we cull the triangle.
bool3 CullTriangleEdgesFrustum(float3 p0, float3 p1, float3 p2, float epsilon, float4 frustumPlanes[6], int numPlanes)
{
bool3 edgesOutside = false;
for (int i = 0; i < numPlanes; i++)
{
bool3 pointsOutside = bool3(DistanceFromPlane(p0, frustumPlanes[i]) < epsilon,
DistanceFromPlane(p1, frustumPlanes[i]) < epsilon,
DistanceFromPlane(p2, frustumPlanes[i]) < epsilon);
// If both points of the edge are behind any of the planes, we cull.
edgesOutside.x = edgesOutside.x || (pointsOutside.y && pointsOutside.z);
edgesOutside.y = edgesOutside.y || (pointsOutside.x && pointsOutside.z);
edgesOutside.z = edgesOutside.z || (pointsOutside.x && pointsOutside.y);
}
return edgesOutside;
}
// Returns 'true' if a triangle defined by 3 vertices is back-facing.
// 'epsilon' is the (negative) value of dot(N, V) below which we cull the triangle.
// 'winding' can be used to change the order: pass 1 for (p0 -> p1 -> p2), or -1 for (p0 -> p2 -> p1).
bool CullTriangleBackFace(float3 p0, float3 p1, float3 p2, float epsilon, float3 viewPos, float winding)
{
float3 edge1 = p1 - p0;
float3 edge2 = p2 - p0;
float3 N = cross(edge1, edge2);
float3 V = viewPos - p0;
float NdotV = dot(N, V) * winding;
// Optimize:
// NdotV / (length(N) * length(V)) < Epsilon
// NdotV < Epsilon * length(N) * length(V)
// NdotV < Epsilon * sqrt(dot(N, N)) * sqrt(dot(V, V))
// NdotV < Epsilon * sqrt(dot(N, N) * dot(V, V))
return NdotV < epsilon * sqrt(dot(N, N) * dot(V, V));
}
#endif // UNITY_GEOMETRICTOOLS_INCLUDED

74
ScriptableRenderPipeline/Core/ShaderLibrary/Tessellation.hlsl


#define TESSELLATION_INTERPOLATE_BARY(name, bary) ouput.name = input0.name * bary.x + input1.name * bary.y + input2.name * bary.z
// TODO: Move in geomtry.hlsl
float3 ProjectPointOnPlane(float3 position, float3 planePosition, float3 planeNormal)
{
return position - (dot(position - planePosition, planeNormal) * planeNormal);
}
// p0, p1, p2 triangle world position
// p0, p1, p2 triangle world vertex normal
float3 PhongTessellation(float3 positionWS, float3 p0, float3 p1, float3 p2, float3 n0, float3 n1, float3 n2, float3 baryCoords, float shape)

// 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)
{
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;
}
float2 GetScreenSpacePosition(float3 positionWS, float4x4 viewProjectionMatrix, float4 screenSize)
{
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(screenSize.x, -screenSize.y);
}
float2 edgeScreenPosition0 = GetScreenSpacePosition(p0, viewProjectionMatrix, screenSize);
float2 edgeScreenPosition1 = GetScreenSpacePosition(p1, viewProjectionMatrix, screenSize);
float2 edgeScreenPosition2 = GetScreenSpacePosition(p2, viewProjectionMatrix, screenSize);
float2 edgeScreenPosition0 = ComputeScreenSpacePosition(p0, viewProjectionMatrix) * screenSize;
float2 edgeScreenPosition1 = ComputeScreenSpacePosition(p1, viewProjectionMatrix) * screenSize;
float2 edgeScreenPosition2 = ComputeScreenSpacePosition(p2, viewProjectionMatrix) * screenSize;
float EdgeScale = 1.0 / triangleSize; // Edge size in reality, but name is simpler
float3 tessFactor;

float3 edgePosition1 = 0.5 * (p0 + p2);
float3 edgePosition2 = 0.5 * (p0 + p1);
// TODO: Move to camera relative and change distance to length
// In case camera-relative rendering is enabled, 'cameraPosWS' is statically known to be 0,
// so the compiler will be able to optimize distance() to length().
float dist0 = distance(edgePosition0, cameraPosWS);
float dist1 = distance(edgePosition1, cameraPosWS);
float dist2 = distance(edgePosition2, cameraPosWS);

return tessFactor;
}
float4 CalcTriEdgeTessFactors(float3 triVertexFactors)
float4 CalcTriTessFactorsFromEdgeTessFactors(float3 triVertexFactors)
{
float4 tess;
tess.x = triVertexFactors.x;

return tess;
}
// TODO: Move in geomtry.hlsl
float DistanceFromPlane(float3 pos, float4 plane)
{
float d = dot(float4(pos, 1.0), plane);
return d;
}
// Returns true if triangle with given 3 world positions is outside of camera's view frustum.
// cullEps is distance outside of frustum that is still considered to be inside (i.e. max displacement)
bool WorldViewFrustumCull(float3 p0, float3 p1, float3 p2, float cullEps, float4 cameraWorldClipPlanes[4])
{
float4 planeTest;
// left
planeTest.x = ((DistanceFromPlane(p0, cameraWorldClipPlanes[0]) > -cullEps) ? 1.0 : 0.0) +
((DistanceFromPlane(p1, cameraWorldClipPlanes[0]) > -cullEps) ? 1.0 : 0.0) +
((DistanceFromPlane(p2, cameraWorldClipPlanes[0]) > -cullEps) ? 1.0 : 0.0);
// right
planeTest.y = ((DistanceFromPlane(p0, cameraWorldClipPlanes[1]) > -cullEps) ? 1.0 : 0.0) +
((DistanceFromPlane(p1, cameraWorldClipPlanes[1]) > -cullEps) ? 1.0 : 0.0) +
((DistanceFromPlane(p2, cameraWorldClipPlanes[1]) > -cullEps) ? 1.0 : 0.0);
// top
planeTest.z = ((DistanceFromPlane(p0, cameraWorldClipPlanes[2]) > -cullEps) ? 1.0 : 0.0) +
((DistanceFromPlane(p1, cameraWorldClipPlanes[2]) > -cullEps) ? 1.0 : 0.0) +
((DistanceFromPlane(p2, cameraWorldClipPlanes[2]) > -cullEps) ? 1.0 : 0.0);
// bottom
planeTest.w = ((DistanceFromPlane(p0, cameraWorldClipPlanes[3]) > -cullEps) ? 1.0 : 0.0) +
((DistanceFromPlane(p1, cameraWorldClipPlanes[3]) > -cullEps) ? 1.0 : 0.0) +
((DistanceFromPlane(p2, cameraWorldClipPlanes[3]) > -cullEps) ? 1.0 : 0.0);
// has to pass all 4 plane tests to be visible
return !all(planeTest);
}

10
ScriptableRenderPipeline/HDRenderPipeline/Camera/HDCamera.cs


var gpuNonJitteredProj = GL.GetGPUProjectionMatrix(nonJitteredCameraProj, true);
var pos = camera.transform.position;
var relPos = pos; // World-origin-relative
relPos = Vector3.zero; // Camera-relative
}
var gpuVP = gpuNonJitteredProj * gpuView;

cameraPos = pos;
screenSize = new Vector4(camera.pixelWidth, camera.pixelHeight, 1.0f / camera.pixelWidth, 1.0f / camera.pixelHeight);
// Warning: near and far planes appear to be broken.
for (int i = 0; i < 6; i++)
for (int i = 0; i < 4; i++)
// Left, right, top, bottom.
// Near, far.
frustumPlaneEquations[4] = new Vector4( camera.transform.forward.x, camera.transform.forward.y, camera.transform.forward.z, -Vector3.Dot(camera.transform.forward, relPos) - camera.nearClipPlane);
frustumPlaneEquations[5] = new Vector4(-camera.transform.forward.x, -camera.transform.forward.y, -camera.transform.forward.z, Vector3.Dot(camera.transform.forward, relPos) + camera.farClipPlane);
m_LastFrameActive = Time.frameCount;
}

1
ScriptableRenderPipeline/HDRenderPipeline/Material/LayeredLit/LayeredLitTessellation.shader


#include "../../../Core/ShaderLibrary/common.hlsl"
#include "../../../Core/ShaderLibrary/Wind.hlsl"
#include "../../../Core/ShaderLibrary/GeometricTools.hlsl"
#include "../../../Core/ShaderLibrary/tessellation.hlsl"
#include "../../ShaderPass/FragInputs.hlsl"
#include "../../ShaderPass/ShaderPass.cs.hlsl"

54
ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/LitDataMeshModification.hlsl


{
float maxDisplacement = GetMaxDisplacement();
float frustumEps = -maxDisplacement;
bool3 frustumCullEdgesMainView = CullTriangleEdgesFrustum(p0, p1, p2, frustumEps, _FrustumPlanes, 5); // Do not test the far plane
bool frustumCulledCurrentView = WorldViewFrustumCull(p0, p1, p2, maxDisplacement, (float4[4])_FrustumPlanes); // _FrustumPlanes are primary camera planes
bool frustumCulledMainView = false;
bool frustumCullCurrView = all(frustumCullEdgesMainView);
bool frustumCulledCurrentView = WorldViewFrustumCull(p0, p1, p2, maxDisplacement, (float4[4])unity_CameraWorldClipPlanes); // unity_CameraWorldClipPlanes is set by legacy Unity in case of shadow and contain shadow view plan
// In the case of shadow, we don't want to tessellate anything that is not seen by the main view frustum. It can result in minor popping of tessellation into a shadow but we can't afford it anyway.
bool frustumCulledMainView = WorldViewFrustumCull(p0, p1, p2, maxDisplacement, (float4[4])_FrustumPlanes);
// 'unity_CameraWorldClipPlanes' are camera-relative rendering aware.
bool frustumCullCurrView = CullTriangleFrustum(p0, p1, p2, frustumEps, unity_CameraWorldClipPlanes, 4); // Do not test near/far planes
if (_TessellationBackFaceCullEpsilon > -0.99) // Is backface culling enabled ?
if (_TessellationBackFaceCullEpsilon > -1.0) // Is back-face culling enabled ?
// Caution: don't change p1/p2 directly as it is use later
float3 backfaceP1 = unity_WorldTransformParams.w < 0.0 ? p2 : p1;
float3 backfaceP2 = unity_WorldTransformParams.w < 0.0 ? p1 : p2;
faceCull = BackFaceCullTriangle(p0, backfaceP1, backfaceP2, _TessellationBackFaceCullEpsilon, GetCurrentViewPosition()); // Use shadow view
float winding = unity_WorldTransformParams.w;
faceCull = CullTriangleBackFace(p0, p1, p2, _TessellationBackFaceCullEpsilon, GetCurrentViewPosition(), winding); // Use shadow view
if (frustumCulledCurrentView || faceCull)
if (frustumCullCurrView || faceCull)
return float4(0.0, 0.0, 0.0, 0.0);
}
// See comment above:
// During shadow passes, we decide that anything outside the main view frustum should not be tessellated.
if (frustumCulledMainView)
{
return float4(1.0, 1.0, 1.0, 1.0);
return 0;
// We use the parameters of the primary (scene view) camera in order
// to have identical tessellation levels for both the scene view and
// shadow views. Otherwise, depth comparisons become meaningless!
float3 tessFactor = float3(1.0, 1.0, 1.0);
// For performance reasons, we choose not to tessellate outside of the main camera view
// (we perform this test both during the regular scene rendering and the shadow pass).
// For edges not visible from the main view, our goal is to set the tessellation factor to 1.
// In this case, we set the tessellation factor to 0 here.
// That way, all scaling of this tessellation factor will still result in 0.
// Before we call CalcTriTessFactorsFromEdgeTessFactors(), all factors are clamped by max(f, 1),
// which achieves the desired effect.
float3 edgeTessFactors = float3(frustumCullEdgesMainView.x ? 0 : 1, frustumCullEdgesMainView.y ? 0 : 1, frustumCullEdgesMainView.z ? 0 : 1);
tessFactor *= GetScreenSpaceTessFactor( p0, p1, p2, _ViewProjMatrix, _ScreenSize, _TessellationFactorTriangleSize); // Use primary camera view
edgeTessFactors *= GetScreenSpaceTessFactor( p0, p1, p2, _ViewProjMatrix, _ScreenSize, _TessellationFactorTriangleSize); // Use primary camera view
}
// Distance based tessellation

// We square the disance factor as it allow a better percptual descrease of vertex density.
tessFactor *= distFactor * distFactor;
edgeTessFactors *= distFactor * distFactor;
tessFactor *= _TessellationFactor;
edgeTessFactors *= _TessellationFactor;
tessFactor.xyz = float3(max(1.0, tessFactor.x), max(1.0, tessFactor.y), max(1.0, tessFactor.z));
edgeTessFactors = max(edgeTessFactors, float3(1.0, 1.0, 1.0));
return CalcTriEdgeTessFactors(tessFactor);
return CalcTriTessFactorsFromEdgeTessFactors(edgeTessFactors);
}
// tessellationFactors

1
ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/LitTessellation.shader


#include "../../../Core/ShaderLibrary/common.hlsl"
#include "../../../Core/ShaderLibrary/Wind.hlsl"
#include "../../../Core/ShaderLibrary/GeometricTools.hlsl"
#include "../../../Core/ShaderLibrary/tessellation.hlsl"
#include "../../ShaderPass/FragInputs.hlsl"
#include "../../ShaderPass/ShaderPass.cs.hlsl"

2
ScriptableRenderPipeline/HDRenderPipeline/ShaderPass/TessellationShare.hlsl


output.edge[0] = min(tf.x, MAX_TESSELLATION_FACTORS);
output.edge[1] = min(tf.y, MAX_TESSELLATION_FACTORS);
output.edge[2] = min(tf.z, MAX_TESSELLATION_FACTORS);
output.inside = min(tf.w, MAX_TESSELLATION_FACTORS);
output.inside = min(tf.w, MAX_TESSELLATION_FACTORS);
return output;
}

4
ScriptableRenderPipeline/HDRenderPipeline/ShaderVariables.hlsl


float4x4 _InvViewMatrix;
float4x4 _InvProjMatrix;
float4 _InvProjParam;
float4 _ScreenSize; // (w, h, 1/w, 1/h)
float4 _FrustumPlanes[6]; // (N, -dot(N, P))
float4 _ScreenSize; // {w, h, 1/w, 1/h}
float4 _FrustumPlanes[6]; // {(a, b, c) = N, d = -dot(N, P)} [L, R, T, B, N, F]
CBUFFER_END
#ifdef USE_LEGACY_UNITY_MATRIX_VARIABLES

6
ScriptableRenderPipeline/HDRenderPipeline/ShaderVariablesFunctions.hlsl


// Note: '_WorldSpaceCameraPos' is set by the legacy Unity code.
float3 GetPrimaryCameraPosition()
{
return GetCameraRelativePositionWS(_WorldSpaceCameraPos);
#if (SHADEROPTIONS_CAMERA_RELATIVE_RENDERING != 0)
return float3(0, 0, 0);
#else
return _WorldSpaceCameraPos;
#endif
}
// Could be e.g. the position of a primary camera or a shadow-casting light.

正在加载...
取消
保存