浏览代码

HDRenderPipeline: More work on tesselation

/main
sebastienlagarde 8 年前
当前提交
589fe428
共有 14 个文件被更改,包括 361 次插入37 次删除
  1. 4
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Lit.hlsl
  2. 1
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Lit.shader
  3. 13
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/LitData.hlsl
  4. 29
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/LitTesselation.hlsl
  5. 7
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/LitTessellation.shader
  6. 3
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/ShaderPass/LitDepthPass.hlsl
  7. 50
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/ShaderPass/LitDistortionPass.hlsl
  8. 76
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/ShaderPass/LitSharePass.hlsl
  9. 62
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/ShaderPass/LitVelocityPass.hlsl
  10. 10
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Tesselation/TesselationShare.hlsl
  11. 7
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Unlit/Unlit.shader
  12. 1
      Assets/ScriptableRenderLoop/ShaderLibrary/CommonLighting.hlsl
  13. 17
      Assets/ScriptableRenderLoop/ShaderLibrary/Packing.hlsl
  14. 118
      Assets/ScriptableRenderLoop/ShaderLibrary/Tesselation.hlsl

4
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Lit.hlsl


{
PreLightData preLightData;
// We do not saturate to correctly handle double-sided lighting.
// We have handle the case of NdotV being negative in GetData() function with GetShiftedNdotV.
// So we don't need to saturate or take the abs here.
// In case a material use negative normal for double sided lighting like speedtree this will be handle in the GetData() code too.
preLightData.NdotV = dot(bsdfData.normalWS, V);
preLightData.ggxLambdaV = GetSmithJointGGXLambdaV(preLightData.NdotV, bsdfData.roughness);

1
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Lit.shader


// All our shaders use same name for entry point
#pragma vertex Vert
#pragma fragment Frag
#define ATTRIBUTE_POSITION POSITION
ENDHLSL

13
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/LitData.hlsl


surfaceData.normalWS = TransformTangentToWorld(normalTS, input.tangentToWorld);
surfaceData.tangentWS = input.tangentToWorld[0].xyz;
// NdotV should not be negative for visible pixels, but it can happen due to the
// perspective projection and the normal mapping + decals. In that case, the normal
// should be modified to become valid (i.e facing the camera) to avoid weird artifacts.
// Note: certain applications (e.g. SpeedTree) make use of double-sided lighting.
// This will potentially reduce the length of the normal at edges of geometry.
// This will always produce the correct 'NdotV' value, but potentially
// reduce the length of the normal at edges of geometry.
GetShiftedNdotV(surfaceData.normalWS, V, twoSided);
// Orthonormalize the basis vectors using the Gram-Schmidt process.

surfaceData.normalWS = TransformTangentToWorld(normalTS, input.tangentToWorld);
surfaceData.tangentWS = input.tangentToWorld[0].xyz;
bool twoSided = false;
// NdotV should not be negative for visible pixels, but it can happen due to the
// perspective projection and the normal mapping + decals. In that case, the normal
// should be modified to become valid (i.e facing the camera) to avoid weird artifacts.
// Note: certain applications (e.g. SpeedTree) make use of double-sided lighting.
bool twoSided = false;
GetShiftedNdotV(surfaceData.normalWS, V, twoSided);
// Orthonormalize the basis vectors using the Gram-Schmidt process.

29
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/LitTesselation.hlsl


/*
float _Tess;
float _TessNear;
float _TessFar;
float _UseDisplacementfalloff;
float _DisplacementfalloffNear;
float _DisplacementfalloffFar;
*/
float4 TesselationEdge(Attributes input0, Attributes input1, Attributes input2)
{
float minDist = 0; // _TessNear;
float maxDist = 15; // _TessFar;
return UnityDistanceBasedTess(input0.positionOS, input1.positionOS, input2.positionOS, minDist, maxDist, 0.5 /* _Tess */, unity_ObjectToWorld, _WorldSpaceCameraPos);
}
void Displacement(inout Attributes v)
{
/*
float LengthLerp = length(ObjSpaceViewDir(v.vertex));
LengthLerp -= _DisplacementfalloffNear;
LengthLerp /= _DisplacementfalloffFar - _DisplacementfalloffNear;
LengthLerp = 1 - (saturate(LengthLerp));
float d = ((tex2Dlod(_DispTex, float4(v.texcoord.xy * _Tiling, 0, 0)).r) - _DisplacementCenter) * (_Displacement * LengthLerp);
d /= max(0.0001, _Tiling);
*/
v.positionOS.xyz += float3(0.5, 0.5, 0.5); // v.normalOS * 5.0;
}

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


HLSLINCLUDE
#pragma target 5.0
#pragma only_renderers d3d11 // TEMP: unitl we go futher in dev
#pragma only_renderers d3d11 // TEMP: until we go futher in dev
//-------------------------------------------------------------------------------------
// Variant

//-------------------------------------------------------------------------------------
#define UNITY_MATERIAL_LIT // Need to be define before including Material.hlsl
#define TESSELATION_ON
//-------------------------------------------------------------------------------------
// Include

#include "tesselation.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/ShaderConfig.cs.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/ShaderVariables.hlsl"
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Attributes.hlsl"

// DYNAMICLIGHTMAP_ON is used when we have an "enlighten lightmap" ie a lightmap updated at runtime by enlighten.This lightmap contain indirect lighting from realtime lights and realtime emissive material.Offline baked lighting(from baked material / light,
// both direct and indirect lighting) will hand up in the "regular" lightmap->LIGHTMAP_ON.
#pragma hull Hull
#pragma domain Domain
// No tesselation for Meta pass
#define SHADERPASS SHADERPASS_LIGHT_TRANSPORT
#include "../../Material/Material.hlsl"

3
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/ShaderPass/LitDepthPass.hlsl


#define NEED_TEXCOORD0 defined(_ALPHATEST_ON)
#define NEED_TANGENT_TO_WORLD NEED_TEXCOORD0 && (defined(_HEIGHTMAP) && !defined(_HEIGHTMAP_AS_DISPLACEMENT))
// When modifying this structure, update the tesselation code below
struct Attributes
{
float3 positionOS : POSITION;

Attributes AttributesTesselationToAttributes(AttributesTesselation input)
{
AttributesTesselation output;
Attributes output;
output.positionOS = input.positionOS;
#if NEED_TEXCOORD0
output.uv0 = input.uv0;

50
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/ShaderPass/LitDistortionPass.hlsl


float3 positionOS : POSITION;
float2 uv0 : TEXCOORD0;
#if NEED_TANGENT_TO_WORLD
float3 normalOS : NORMAL;
#ifdef TESSELATION_ON
// Copy paste of above struct with POSITION rename to INTERNALTESSPOS (internal of unity shader compiler)
struct AttributesTesselation
{
float3 positionOS : INTERNALTESSPOS;
float2 uv0 : TEXCOORD0;
#if NEED_TANGENT_TO_WORLD
float3 normalOS : NORMAL;
float4 tangentOS : TANGENT;
#endif
};
AttributesTesselation AttributesToAttributesTesselation(Attributes input)
{
AttributesTesselation output;
output.positionOS = input.positionOS;
output.uv0 = input.uv0;
#if NEED_TANGENT_TO_WORLD
output.normalOS = input.normalOS;
output.tangentOS = input.tangentOS;
#endif
}
Attributes AttributesTesselationToAttributes(AttributesTesselation input)
{
Attributes output;
output.positionOS = input.positionOS;
output.uv0 = input.uv0;
#if NEED_TANGENT_TO_WORLD
output.normalOS = input.normalOS;
output.tangentOS = input.tangentOS;
#endif
}
AttributesTesselation InterpolateWithBary(AttributesTesselation input0, AttributesTesselation input1, AttributesTesselation input2, float3 baryWeight)
{
AttributesTesselation ouput;
TESSELATION_INTERPOLATE_BARY(positionOS, baryWeight);
TESSELATION_INTERPOLATE_BARY(uv0, baryWeight);
#if NEED_TANGENT_TO_WORLD
TESSELATION_INTERPOLATE_BARY(normalOS, baryWeight);
TESSELATION_INTERPOLATE_BARY(tangentOS, baryWeight);
#endif
return ouput;
}
#endif // TESSELATION_ON
struct Varyings
{

76
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/ShaderPass/LitSharePass.hlsl


struct Attributes
{
float3 positionOS : ATTRIBUTE_POSITION;
float3 positionOS : POSITION;
float3 normalOS : NORMAL;
float2 uv0 : TEXCOORD0;
float2 uv1 : TEXCOORD1;

// UNITY_INSTANCE_ID
};
#ifdef TESSELATION_ON
// Copy paste of above struct with POSITION rename to INTERNALTESSPOS (internal of unity shader compiler)
struct AttributesTesselation
{
float3 positionOS : INTERNALTESSPOS;
float3 normalOS : NORMAL;
float2 uv0 : TEXCOORD0;
float2 uv1 : TEXCOORD1;
#if WANT_UV2
float2 uv2 : TEXCOORD2;
#endif
#if WANT_UV3
float2 uv3 : TEXCOORD3;
#endif
float4 tangentOS : TANGENT; // Always present as we require it also in case of anisotropic lighting
float4 color : COLOR;
};
AttributesTesselation AttributesToAttributesTesselation(Attributes input)
{
AttributesTesselation output;
output.positionOS = input.positionOS;
output.normalOS = input.normalOS;
output.uv0 = input.uv0;
output.uv1 = input.uv1;
#if WANT_UV2
output.uv2 = input.uv2;
#endif
#if WANT_UV3
output.uv3 = input.uv3;
#endif
output.tangentOS = input.tangentOS;
output.color = input.color;
}
Attributes AttributesTesselationToAttributes(AttributesTesselation input)
{
Attributes output;
output.positionOS = input.positionOS;
output.normalOS = input.normalOS;
output.uv0 = input.uv0;
output.uv1 = input.uv1;
#if WANT_UV2
output.uv2 = input.uv2;
#endif
#if WANT_UV3
output.uv3 = input.uv3;
#endif
output.tangentOS = input.tangentOS;
output.color = input.color;
}
AttributesTesselation InterpolateWithBary(AttributesTesselation input0, AttributesTesselation input1, AttributesTesselation input2, float3 baryWeight)
{
AttributesTesselation ouput;
TESSELATION_INTERPOLATE_BARY(positionOS, baryWeight);
TESSELATION_INTERPOLATE_BARY(normalOS, baryWeight);
TESSELATION_INTERPOLATE_BARY(uv0, baryWeight);
TESSELATION_INTERPOLATE_BARY(uv1, baryWeight);
#if WANT_UV2
TESSELATION_INTERPOLATE_BARY(uv2, baryWeight);
#endif
#if WANT_UV3
TESSELATION_INTERPOLATE_BARY(uv3, baryWeight);
#endif
TESSELATION_INTERPOLATE_BARY(tangentOS, baryWeight);
TESSELATION_INTERPOLATE_BARY(color, baryWeight);
return ouput;
}
#endif // TESSELATION_ON
struct Varyings
{

62
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/ShaderPass/LitVelocityPass.hlsl


float2 uv0 : TEXCOORD0;
#endif
#if NEED_TANGENT_TO_WORLD
// float3 normalOS : NORMAL; // TODO: This won't compile as we conflict with previousPositionOS. FIXME
#ifdef TESSELATION_ON
// Copy paste of above struct with POSITION rename to INTERNALTESSPOS (internal of unity shader compiler)
struct AttributesTesselation
{
float3 positionOS : INTERNALTESSPOS;
float3 previousPositionOS : NORMAL;
#if NEED_TEXCOORD0
float2 uv0 : TEXCOORD0;
#endif
#if NEED_TANGENT_TO_WORLD
// float3 normalOS : NORMAL;
float4 tangentOS : TANGENT;
#endif
};
AttributesTesselation AttributesToAttributesTesselation(Attributes input)
{
AttributesTesselation output;
output.positionOS = input.positionOS;
output.previousPositionOS = input.previousPositionOS;
#if NEED_TEXCOORD0
output.uv0 = input.uv0;
#endif
#if NEED_TANGENT_TO_WORLD
// output.normalOS = input.normalOS;
output.tangentOS = input.tangentOS;
#endif
}
Attributes AttributesTesselationToAttributes(AttributesTesselation input)
{
Attributes output;
output.positionOS = input.positionOS;
output.previousPositionOS = input.previousPositionOS;
#if NEED_TEXCOORD0
output.uv0 = input.uv0;
#endif
#if NEED_TANGENT_TO_WORLD
// output.normalOS = input.normalOS;
output.tangentOS = input.tangentOS;
#endif
}
AttributesTesselation InterpolateWithBary(AttributesTesselation input0, AttributesTesselation input1, AttributesTesselation input2, float3 baryWeight)
{
AttributesTesselation ouput;
TESSELATION_INTERPOLATE_BARY(positionOS, baryWeight);
TESSELATION_INTERPOLATE_BARY(previousPositionOS, baryWeight);
#if NEED_TEXCOORD0
TESSELATION_INTERPOLATE_BARY(uv0, baryWeight);
#endif
#if NEED_TANGENT_TO_WORLD
// TESSELATION_INTERPOLATE_BARY(normalOS, baryWeight);
TESSELATION_INTERPOLATE_BARY(tangentOS, baryWeight);
#endif
return ouput;
}
#endif // TESSELATION_ON
struct Varyings
{

10
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Tesselation/TesselationShare.hlsl


float inside : SV_InsideTessFactor;
};
UnityTessellationFactors HullConstant(InputPatch<AttributesTesselation, 3> input)
TessellationFactors HullConstant(InputPatch<AttributesTesselation, 3> input)
{
Attributes params[3];
params[0] = AttributesTesselationToAttributes(input[0]);

float4 tf = tessEdge(vi[0], vi[1], vi[2]);
float4 tf = TesselationEdge(params[0], params[1], params[2]);
TessellationFactors ouput;
ouput.edge[0] = tf.x;

}
[UNITY_domain("tri")]
PackedVaryings Domain(UnityTessellationFactors tessFactors, const OutputPatch<AttributesTesselation, 3> input, float3 baryWeight : SV_DomainLocation)
PackedVaryings Domain(TessellationFactors tessFactors, const OutputPatch<AttributesTesselation, 3> input, float3 baryWeight : SV_DomainLocation)
displacement(params);
Displacement(params);
PackedVaryings outout = Vert(v);
PackedVaryings outout = Vert(params);
return outout;
}

7
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Unlit/Unlit.shader


// DYNAMICLIGHTMAP_ON is used when we have an "enlighten lightmap" ie a lightmap updated at runtime by enlighten.This lightmap contain indirect lighting from realtime lights and realtime emissive material.Offline baked lighting(from baked material / light,
// both direct and indirect lighting) will hand up in the "regular" lightmap->LIGHTMAP_ON.
#pragma vertex Vert
#pragma fragment Frag
#define SHADERPASS SHADERPASS_LIGHT_TRANSPORT
#include "../../Material/Material.hlsl"
#include "UnlitData.hlsl"

CBUFFER_END
// This was not in constant buffer in original unity, so keep outiside. But should be in as ShaderRenderPass frequency
float unity_OneOverOutputBoost;
// This was not in constant buffer in original unity, so keep outiside. But should be in as ShaderRenderPass frequency
float unity_OneOverOutputBoost;
float unity_MaxOutputValue;
struct Attributes

1
Assets/ScriptableRenderLoop/ShaderLibrary/CommonLighting.hlsl


// perspective projection and the normal mapping + decals. In that case, the normal
// should be modified to become valid (i.e facing the camera) to avoid weird artifacts.
// Note: certain applications (e.g. SpeedTree) make use of double-sided lighting.
// This will potentially reduce the length of the normal at edges of geometry.
float GetShiftedNdotV(inout float3 N, float3 V, bool twoSided)
{
float NdotV = dot(N, V);

17
Assets/ScriptableRenderLoop/ShaderLibrary/Packing.hlsl


return max(vRGB, float3(0.0, 0.0, 0.0));
}
// Alternative...
#define RGBMRANGE (8.0)
float4 PackRGBM(float3 color)
{
float4 rgbm;
color *= (1.0 / RGBMRANGE);
rgbm.a = saturate(max(max(color.r, color.g), max(color.b, 1e-6)));
rgbm.a = ceil(rgbm.a * 255.0) / 255.0;
rgbm.rgb = color / rgbm.a;
return rgbm;
}
float3 UnpackRGBM(float4 rgbm)
{
return RGBMRANGE * rgbm.rgb * rgbm.a;
}
// The standard 32-bit HDR color format
uint PackR11G11B10f(float3 rgb)
{

118
Assets/ScriptableRenderLoop/ShaderLibrary/Tesselation.hlsl


#define TESSELATION_INTERPOLATE_BARY(name, bary) ouput.name = input0.name * bary.x + input1.name * bary.y + input2.name * bary.z
// ---- utility functions
float UnityCalcDistanceTessFactor(float3 positionOS, float minDist, float maxDist, float tess, float4x4 objectToWorld, float3 cameraPosWS)
{
float3 positionWS = mul(objectToWorld, float4(positionOS, 1.0)).xyz;
float dist = distance(positionWS, cameraPosWS);
float f = clamp(1.0 - (dist - minDist) / (maxDist - minDist), 0.01, 1.0) * tess;
return f;
}
float4 UnityCalcTriEdgeTessFactors(float3 triVertexFactors)
{
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;
}
/*
float UnityCalcEdgeTessFactor(float3 wpos0, float3 wpos1, float edgeLen)
{
// 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;
}
float UnityDistanceFromPlane(float3 pos, float4 plane)
{
float d = dot(float4(pos, 1.0f), 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 UnityWorldViewFrustumCull(float3 wpos0, float3 wpos1, float3 wpos2, float cullEps)
{
float4 planeTest;
// left
planeTest.x = ((UnityDistanceFromPlane(wpos0, unity_CameraWorldClipPlanes[0]) > -cullEps) ? 1.0f : 0.0f) +
((UnityDistanceFromPlane(wpos1, unity_CameraWorldClipPlanes[0]) > -cullEps) ? 1.0f : 0.0f) +
((UnityDistanceFromPlane(wpos2, unity_CameraWorldClipPlanes[0]) > -cullEps) ? 1.0f : 0.0f);
// right
planeTest.y = ((UnityDistanceFromPlane(wpos0, unity_CameraWorldClipPlanes[1]) > -cullEps) ? 1.0f : 0.0f) +
((UnityDistanceFromPlane(wpos1, unity_CameraWorldClipPlanes[1]) > -cullEps) ? 1.0f : 0.0f) +
((UnityDistanceFromPlane(wpos2, unity_CameraWorldClipPlanes[1]) > -cullEps) ? 1.0f : 0.0f);
// top
planeTest.z = ((UnityDistanceFromPlane(wpos0, unity_CameraWorldClipPlanes[2]) > -cullEps) ? 1.0f : 0.0f) +
((UnityDistanceFromPlane(wpos1, unity_CameraWorldClipPlanes[2]) > -cullEps) ? 1.0f : 0.0f) +
((UnityDistanceFromPlane(wpos2, unity_CameraWorldClipPlanes[2]) > -cullEps) ? 1.0f : 0.0f);
// bottom
planeTest.w = ((UnityDistanceFromPlane(wpos0, unity_CameraWorldClipPlanes[3]) > -cullEps) ? 1.0f : 0.0f) +
((UnityDistanceFromPlane(wpos1, unity_CameraWorldClipPlanes[3]) > -cullEps) ? 1.0f : 0.0f) +
((UnityDistanceFromPlane(wpos2, unity_CameraWorldClipPlanes[3]) > -cullEps) ? 1.0f : 0.0f);
// 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 UnityDistanceBasedTess(float3 positionOS0, float3 positionOS1, float3 positionOS2, float minDist, float maxDist, float tess, float4x4 objectToWorld, float3 cameraPosWS)
{
float3 f;
f.x = UnityCalcDistanceTessFactor(positionOS0, minDist, maxDist, tess, objectToWorld, cameraPosWS);
f.y = UnityCalcDistanceTessFactor(positionOS1, minDist, maxDist, tess, objectToWorld, cameraPosWS);
f.z = UnityCalcDistanceTessFactor(positionOS2, minDist, maxDist, tess, objectToWorld, 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;
}
*/
正在加载...
取消
保存