Restored support for fractional tessellation

This change includes support for both fractional_even_spacing
and fractional_odd_spacing.

The implementation follows the existing pattern of re-parameterizing
the tessellation domain only along transition boundary edges. This
allows for crack-free tessellation, but it might be better to
consistently re-parameterize all of the outer edges of all patches,
which also would be required for numerically watertight tessellation.

This is implemented in a way that requires no changes to the client
shader API. It should be more efficient to move some computations to
the control/hull shaders and reduce divergence in the execution of
eval/domain shaders.
This commit is contained in:
David G. Yu 2015-10-20 19:48:51 -07:00
parent c8a538cd43
commit 8987e09102
2 changed files with 376 additions and 86 deletions

View File

@ -72,14 +72,10 @@
// mix(input[c].var, input[d].var, UV.x), UV.y)
#endif
// XXXdyu-patch-drawing support for fractional spacing
#undef OSD_FRACTIONAL_ODD_SPACING
#undef OSD_FRACTIONAL_EVEN_SPACING
#if defined OSD_FRACTIONAL_ODD_SPACING
#define OSD_SPACING fractional_odd_spacing
#elif defined OSD_FRACTIONAL_EVEN_SPACING
#if defined OSD_FRACTIONAL_EVEN_SPACING
#define OSD_SPACING fractional_even_spacing
#elif defined OSD_FRACTIONAL_ODD_SPACING
#define OSD_SPACING fractional_odd_spacing
#else
#define OSD_SPACING equal_spacing
#endif
@ -621,7 +617,7 @@ float OsdComputeTessLevel(vec3 p0, vec3 p1)
vec3 center = (p0 + p1) / 2.0;
float diameter = distance(p0, p1);
float projLength = OsdComputePostProjectionSphereExtent(center, diameter);
float tessLevel = round(max(1.0, OsdTessLevel() * projLength));
float tessLevel = max(1.0, OsdTessLevel() * projLength);
// We restrict adaptive tessellation levels to half of the device
// supported maximum because transition edges are split into two
@ -799,6 +795,97 @@ OsdGetTessLevelsLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
#endif
}
// Round up to the nearest even integer
float OsdRoundUpEven(float x) {
return 2*ceil(x/2);
}
// Round up to the nearest odd integer
float OsdRoundUpOdd(float x) {
return 2*ceil((x+1)/2)-1;
}
// Compute outer and inner tessellation levels taking into account the
// current tessellation spacing mode.
void
OsdComputeTessLevels(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
out vec4 tessLevelOuter, out vec2 tessLevelInner)
{
// Outer levels are the sum of the Lo and Hi segments where the Hi
// segments will have lengths of zero for non-transition edges.
#if defined OSD_FRACTIONAL_EVEN_SPACING
// Combine fractional outer transition edge levels before rounding.
vec4 combinedOuter = tessOuterLo + tessOuterHi;
// Round the segments of transition edges separately. We will recover the
// fractional parameterization of transition edges after tessellation.
tessLevelOuter = combinedOuter;
if (tessOuterHi[0] > 0) {
tessLevelOuter[0] =
OsdRoundUpEven(tessOuterLo[0]) + OsdRoundUpEven(tessOuterHi[0]);
}
if (tessOuterHi[1] > 0) {
tessLevelOuter[1] =
OsdRoundUpEven(tessOuterLo[1]) + OsdRoundUpEven(tessOuterHi[1]);
}
if (tessOuterHi[2] > 0) {
tessLevelOuter[2] =
OsdRoundUpEven(tessOuterLo[2]) + OsdRoundUpEven(tessOuterHi[2]);
}
if (tessOuterHi[3] > 0) {
tessLevelOuter[3] =
OsdRoundUpEven(tessOuterLo[3]) + OsdRoundUpEven(tessOuterHi[3]);
}
#elif defined OSD_FRACTIONAL_ODD_SPACING
// Combine fractional outer transition edge levels before rounding.
vec4 combinedOuter = tessOuterLo + tessOuterHi;
// Round the segments of transition edges separately. We will recover the
// fractional parameterization of transition edges after tessellation.
//
// The sum of the two outer odd segment lengths will be an even number
// which the tessellator will increase by +1 so that there will be a
// total odd number of segments. We clamp the combinedOuter tess levels
// (used to compute the inner tess levels) so that the outer transition
// edges will be sampled without degenerate triangles.
tessLevelOuter = combinedOuter;
if (tessOuterHi[0] > 0) {
tessLevelOuter[0] =
OsdRoundUpOdd(tessOuterLo[0]) + OsdRoundUpOdd(tessOuterHi[0]);
combinedOuter = max(vec4(3), combinedOuter);
}
if (tessOuterHi[1] > 0) {
tessLevelOuter[1] =
OsdRoundUpOdd(tessOuterLo[1]) + OsdRoundUpOdd(tessOuterHi[1]);
combinedOuter = max(vec4(3), combinedOuter);
}
if (tessOuterHi[2] > 0) {
tessLevelOuter[2] =
OsdRoundUpOdd(tessOuterLo[2]) + OsdRoundUpOdd(tessOuterHi[2]);
combinedOuter = max(vec4(3), combinedOuter);
}
if (tessOuterHi[3] > 0) {
tessLevelOuter[3] =
OsdRoundUpOdd(tessOuterLo[3]) + OsdRoundUpOdd(tessOuterHi[3]);
combinedOuter = max(vec4(3), combinedOuter);
}
#else
// Round equally spaced transition edge levels before combining.
tessOuterLo = round(tessOuterLo);
tessOuterHi = round(tessOuterHi);
vec4 combinedOuter = tessOuterLo + tessOuterHi;
tessLevelOuter = combinedOuter;
#endif
// Inner levels are the averages the corresponding outer levels.
tessLevelInner[0] = (combinedOuter[1] + combinedOuter[3]) * 0.5;
tessLevelInner[1] = (combinedOuter[0] + combinedOuter[2]) * 0.5;
}
void
OsdGetTessLevelsUniform(ivec3 patchParam,
out vec4 tessLevelOuter, out vec2 tessLevelInner,
@ -807,13 +894,8 @@ OsdGetTessLevelsUniform(ivec3 patchParam,
// uniform tessellation
OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
// Outer levels are the sum of the Lo and Hi segments where the Hi
// segments will have a length of zero for non-transition edges.
tessLevelOuter = tessOuterLo + tessOuterHi;
// Inner levels are the average the corresponding outer levels.
tessLevelInner[0] = (tessLevelOuter[1] + tessLevelOuter[3]) * 0.5;
tessLevelInner[1] = (tessLevelOuter[0] + tessLevelOuter[2]) * 0.5;
OsdComputeTessLevels(tessOuterLo, tessOuterHi,
tessLevelOuter, tessLevelInner);
}
void
@ -821,15 +903,11 @@ OsdGetTessLevelsAdaptiveRefinedPoints(vec3 cpRefined[16], ivec3 patchParam,
out vec4 tessLevelOuter, out vec2 tessLevelInner,
out vec4 tessOuterLo, out vec4 tessOuterHi)
{
OsdGetTessLevelsRefinedPoints(cpRefined, patchParam, tessOuterLo, tessOuterHi);
OsdGetTessLevelsRefinedPoints(cpRefined, patchParam,
tessOuterLo, tessOuterHi);
// Outer levels are the sum of the Lo and Hi segments where the Hi
// segments will have a length of zero for non-transition edges.
tessLevelOuter = tessOuterLo + tessOuterHi;
// Inner levels are the average the corresponding outer levels.
tessLevelInner[0] = (tessLevelOuter[1] + tessLevelOuter[3]) * 0.5;
tessLevelInner[1] = (tessLevelOuter[0] + tessLevelOuter[2]) * 0.5;
OsdComputeTessLevels(tessOuterLo, tessOuterHi,
tessLevelOuter, tessLevelInner);
}
void
@ -838,15 +916,11 @@ OsdGetTessLevelsAdaptiveLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
out vec4 tessLevelOuter, out vec2 tessLevelInner,
out vec4 tessOuterLo, out vec4 tessOuterHi)
{
OsdGetTessLevelsLimitPoints(cpBezier, patchParam, tessOuterLo, tessOuterHi);
OsdGetTessLevelsLimitPoints(cpBezier, patchParam,
tessOuterLo, tessOuterHi);
// Outer levels are the sum of the Lo and Hi segments where the Hi
// segments will have a length of zero for non-transition edges.
tessLevelOuter = tessOuterLo + tessOuterHi;
// Inner levels are the average the corresponding outer levels.
tessLevelInner[0] = (tessLevelOuter[1] + tessLevelOuter[3]) * 0.5;
tessLevelInner[1] = (tessLevelOuter[0] + tessLevelOuter[2]) * 0.5;
OsdComputeTessLevels(tessOuterLo, tessOuterHi,
tessLevelOuter, tessLevelInner);
}
void
@ -867,26 +941,97 @@ OsdGetTessLevels(vec3 cp0, vec3 cp1, vec3 cp2, vec3 cp3,
OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
#endif
// Outer levels are the sum of the Lo and Hi segments where the Hi
// segments will have a length of zero for non-transition edges.
tessLevelOuter = tessOuterLo + tessOuterHi;
// Inner levels are the average the corresponding outer levels.
tessLevelInner[0] = (tessLevelOuter[1] + tessLevelOuter[3]) * 0.5;
tessLevelInner[1] = (tessLevelOuter[0] + tessLevelOuter[2]) * 0.5;
OsdComputeTessLevels(tessOuterLo, tessOuterHi,
tessLevelOuter, tessLevelInner);
}
#if defined OSD_FRACTIONAL_EVEN_SPACING || defined OSD_FRACTIONAL_ODD_SPACING
float
OsdGetTessTransitionSplit(float t, float n0, float n1)
OsdGetTessFractionalSplit(float t, float level, float levelUp)
{
float ti = round(t * (n0 + n1));
// Fractional tessellation of an edge will produce n segments where n
// is the tessellation level of the edge (level) rounded up to the
// nearest even or odd integer (levelUp). There will be n-2 segments of
// equal length (dx1) and two additional segments of equal length (dx0)
// that are typically shorter than the other segments. The two additional
// segments should be placed symmetrically on opposite sides of the
// edge (offset).
if (ti <= n0) {
return 0.5 * (ti / n0);
#if defined OSD_FRACTIONAL_EVEN_SPACING
if (level <= 2) return t;
float base = pow(2.0,floor(log2(levelUp)));
float offset = 1.0/(int(2*base-levelUp)/2 & int(base/2-1));
#elif defined OSD_FRACTIONAL_ODD_SPACING
if (level <= 1) return t;
float base = pow(2.0,floor(log2(levelUp)));
float offset = 1.0/(((int(2*base-levelUp)/2+1) & int(base/2-1))+1);
#endif
float dx0 = (1.0 - (levelUp-level)/2) / levelUp;
float dx1 = (1.0 - 2.0*dx0) / (levelUp - 2.0*ceil(dx0));
if (t < 0.5) {
float x = levelUp/2 - round(t*levelUp);
return 0.5 - (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
} else if (t > 0.5) {
float x = round(t*levelUp) - levelUp/2;
return 0.5 + (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
} else {
return 0.5 * ((ti - n0) / n1) + 0.5;
return t;
}
}
#endif
float
OsdGetTessTransitionSplit(float t, float lo, float hi)
{
#if defined OSD_FRACTIONAL_EVEN_SPACING
float loRoundUp = OsdRoundUpEven(lo);
float hiRoundUp = OsdRoundUpEven(hi);
// Convert the parametric t into a segment index along the combined edge.
float ti = round(t * (loRoundUp + hiRoundUp));
if (ti <= loRoundUp) {
float t0 = ti / loRoundUp;
return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
} else {
float t1 = (ti - loRoundUp) / hiRoundUp;
return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
}
#elif defined OSD_FRACTIONAL_ODD_SPACING
float loRoundUp = OsdRoundUpOdd(lo);
float hiRoundUp = OsdRoundUpOdd(hi);
// Convert the parametric t into a segment index along the combined edge.
// The +1 below is to account for the extra segment produced by the
// tessellator since the sum of two odd tess levels will be rounded
// up by one to the next odd integer tess level.
float ti = round(t * (loRoundUp + hiRoundUp + 1));
if (ti <= loRoundUp) {
float t0 = ti / loRoundUp;
return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
} else if (ti > (loRoundUp+1)) {
float t1 = (ti - (loRoundUp+1)) / hiRoundUp;
return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
} else {
return 0.5;
}
#else
// Convert the parametric t into a segment index along the combined edge.
float ti = round(t * (lo + hi));
if (ti <= lo) {
return (ti / lo) * 0.5;
} else {
return ((ti - lo) / hi) * 0.5 + 0.5;
}
#endif
}
vec2
OsdGetTessParameterization(vec2 uv, vec4 tessOuterLo, vec4 tessOuterHi)

View File

@ -26,14 +26,10 @@
// Patches.Common
//----------------------------------------------------------
// XXXdyu-patch-drawing support for fractional spacing
#undef OSD_FRACTIONAL_ODD_SPACING
#undef OSD_FRACTIONAL_EVEN_SPACING
#if defined OSD_FRACTIONAL_ODD_SPACING
#define OSD_PARTITIONING "fractional_odd"
#elif defined OSD_FRACTIONAL_EVEN_SPACING
#if defined OSD_FRACTIONAL_EVEN_SPACING
#define OSD_PARTITIONING "fractional_even"
#elif defined OSD_FRACTIONAL_ODD_SPACING
#define OSD_PARTITIONING "fractional_odd"
#else
#define OSD_PARTITIONING "integer"
#endif
@ -494,7 +490,7 @@ float OsdComputeTessLevel(float3 p0, float3 p1)
float3 center = (p0 + p1) / 2.0;
float diameter = distance(p0, p1);
float projLength = OsdComputePostProjectionSphereExtent(center, diameter);
float tessLevel = round(max(1.0, OsdTessLevel() * projLength));
float tessLevel = max(1.0, OsdTessLevel() * projLength);
// We restrict adaptive tessellation levels to half of the device
// supported maximum because transition edges are split into two
@ -674,6 +670,97 @@ OsdGetTessLevelsLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
#endif
}
// Round up to the nearest even integer
float OsdRoundUpEven(float x) {
return 2*ceil(x/2);
}
// Round up to the nearest odd integer
float OsdRoundUpOdd(float x) {
return 2*ceil((x+1)/2)-1;
}
// Compute outer and inner tessellation levels taking into account the
// current tessellation spacing mode.
void
OsdComputeTessLevels(inout float4 tessOuterLo, inout float4 tessOuterHi,
out float4 tessLevelOuter, out float2 tessLevelInner)
{
// Outer levels are the sum of the Lo and Hi segments where the Hi
// segments will have lengths of zero for non-transition edges.
#if defined OSD_FRACTIONAL_EVEN_SPACING
// Combine fractional outer transition edge levels before rounding.
float4 combinedOuter = tessOuterLo + tessOuterHi;
// Round the segments of transition edges separately. We will recover the
// fractional parameterization of transition edges after tessellation.
tessLevelOuter = combinedOuter;
if (tessOuterHi[0] > 0) {
tessLevelOuter[0] =
OsdRoundUpEven(tessOuterLo[0]) + OsdRoundUpEven(tessOuterHi[0]);
}
if (tessOuterHi[1] > 0) {
tessLevelOuter[1] =
OsdRoundUpEven(tessOuterLo[1]) + OsdRoundUpEven(tessOuterHi[1]);
}
if (tessOuterHi[2] > 0) {
tessLevelOuter[2] =
OsdRoundUpEven(tessOuterLo[2]) + OsdRoundUpEven(tessOuterHi[2]);
}
if (tessOuterHi[3] > 0) {
tessLevelOuter[3] =
OsdRoundUpEven(tessOuterLo[3]) + OsdRoundUpEven(tessOuterHi[3]);
}
#elif defined OSD_FRACTIONAL_ODD_SPACING
// Combine fractional outer transition edge levels before rounding.
float4 combinedOuter = tessOuterLo + tessOuterHi;
// Round the segments of transition edges separately. We will recover the
// fractional parameterization of transition edges after tessellation.
//
// The sum of the two outer odd segment lengths will be an even number
// which the tessellator will increase by +1 so that there will be a
// total odd number of segments. We clamp the combinedOuter tess levels
// (used to compute the inner tess levels) so that the outer transition
// edges will be sampled without degenerate triangles.
tessLevelOuter = combinedOuter;
if (tessOuterHi[0] > 0) {
tessLevelOuter[0] =
OsdRoundUpOdd(tessOuterLo[0]) + OsdRoundUpOdd(tessOuterHi[0]);
combinedOuter = max(float4(3,3,3,3), combinedOuter);
}
if (tessOuterHi[1] > 0) {
tessLevelOuter[1] =
OsdRoundUpOdd(tessOuterLo[1]) + OsdRoundUpOdd(tessOuterHi[1]);
combinedOuter = max(float4(3,3,3,3), combinedOuter);
}
if (tessOuterHi[2] > 0) {
tessLevelOuter[2] =
OsdRoundUpOdd(tessOuterLo[2]) + OsdRoundUpOdd(tessOuterHi[2]);
combinedOuter = max(float4(3,3,3,3), combinedOuter);
}
if (tessOuterHi[3] > 0) {
tessLevelOuter[3] =
OsdRoundUpOdd(tessOuterLo[3]) + OsdRoundUpOdd(tessOuterHi[3]);
combinedOuter = max(float4(3,3,3,3), combinedOuter);
}
#else
// Round equally spaced transition edge levels before combining.
tessOuterLo = round(tessOuterLo);
tessOuterHi = round(tessOuterHi);
float4 combinedOuter = tessOuterLo + tessOuterHi;
tessLevelOuter = combinedOuter;
#endif
// Inner levels are the averages the corresponding outer levels.
tessLevelInner[0] = (combinedOuter[1] + combinedOuter[3]) * 0.5;
tessLevelInner[1] = (combinedOuter[0] + combinedOuter[2]) * 0.5;
}
void
OsdGetTessLevelsUniform(int3 patchParam,
out float4 tessLevelOuter, out float2 tessLevelInner,
@ -681,13 +768,8 @@ OsdGetTessLevelsUniform(int3 patchParam,
{
OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
// Outer levels are the sum of the Lo and Hi segments where the Hi
// segments will have a length of zero for non-transition edges.
tessLevelOuter = tessOuterLo + tessOuterHi;
// Inner levels are the average the corresponding outer levels.
tessLevelInner[0] = (tessLevelOuter[1] + tessLevelOuter[3]) * 0.5;
tessLevelInner[1] = (tessLevelOuter[0] + tessLevelOuter[2]) * 0.5;
OsdComputeTessLevels(tessOuterLo, tessOuterHi,
tessLevelOuter, tessLevelInner);
}
void
@ -695,15 +777,11 @@ OsdGetTessLevelsAdaptiveRefinedPoints(float3 cpRefined[16], int3 patchParam,
out float4 tessLevelOuter, out float2 tessLevelInner,
out float4 tessOuterLo, out float4 tessOuterHi)
{
OsdGetTessLevelsRefinedPoints(cpRefined, patchParam, tessOuterLo, tessOuterHi);
OsdGetTessLevelsRefinedPoints(cpRefined, patchParam,
tessOuterLo, tessOuterHi);
// Outer levels are the sum of the Lo and Hi segments where the Hi
// segments will have a length of zero for non-transition edges.
tessLevelOuter = tessOuterLo + tessOuterHi;
// Inner levels are the average the corresponding outer levels.
tessLevelInner[0] = (tessLevelOuter[1] + tessLevelOuter[3]) * 0.5;
tessLevelInner[1] = (tessLevelOuter[0] + tessLevelOuter[2]) * 0.5;
OsdComputeTessLevels(tessOuterLo, tessOuterHi,
tessLevelOuter, tessLevelInner);
}
void
@ -712,15 +790,11 @@ OsdGetTessLevelsAdaptiveLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
out float4 tessLevelOuter, out float2 tessLevelInner,
out float4 tessOuterLo, out float4 tessOuterHi)
{
OsdGetTessLevelsLimitPoints(cpBezier, patchParam, tessOuterLo, tessOuterHi);
OsdGetTessLevelsLimitPoints(cpBezier, patchParam,
tessOuterLo, tessOuterHi);
// Outer levels are the sum of the Lo and Hi segments where the Hi
// segments will have a length of zero for non-transition edges.
tessLevelOuter = tessOuterLo + tessOuterHi;
// Inner levels are the average the corresponding outer levels.
tessLevelInner[0] = (tessLevelOuter[1] + tessLevelOuter[3]) * 0.5;
tessLevelInner[1] = (tessLevelOuter[0] + tessLevelOuter[2]) * 0.5;
OsdComputeTessLevels(tessOuterLo, tessOuterHi,
tessLevelOuter, tessLevelInner);
}
void
@ -741,26 +815,97 @@ OsdGetTessLevels(float3 cp0, float3 cp1, float3 cp2, float3 cp3,
OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
#endif
// Outer levels are the sum of the Lo and Hi segments where the Hi
// segments will have a length of zero for non-transition edges.
tessLevelOuter = tessOuterLo + tessOuterHi;
// Inner levels are the average the corresponding outer levels.
tessLevelInner[0] = (tessLevelOuter[1] + tessLevelOuter[3]) * 0.5;
tessLevelInner[1] = (tessLevelOuter[0] + tessLevelOuter[2]) * 0.5;
OsdComputeTessLevels(tessOuterLo, tessOuterHi,
tessLevelOuter, tessLevelInner);
}
#if defined OSD_FRACTIONAL_EVEN_SPACING || defined OSD_FRACTIONAL_ODD_SPACING
float
OsdGetTessTransitionSplit(float t, float n0, float n1)
OsdGetTessFractionalSplit(float t, float level, float levelUp)
{
float ti = round(t * (n0 + n1));
// Fractional tessellation of an edge will produce n segments where n
// is the tessellation level of the edge (level) rounded up to the
// nearest even or odd integer (levelUp). There will be n-2 segments of
// equal length (dx1) and two additional segments of equal length (dx0)
// that are typically shorter than the other segments. The two additional
// segments should be placed symmetrically on opposite sides of the
// edge (offset).
if (ti <= n0) {
return 0.5 * (ti / n0);
#if defined OSD_FRACTIONAL_EVEN_SPACING
if (level <= 2) return t;
float base = pow(2.0,floor(log2(levelUp)));
float offset = 1.0/(int(2*base-levelUp)/2 & int(base/2-1));
#elif defined OSD_FRACTIONAL_ODD_SPACING
if (level <= 1) return t;
float base = pow(2.0,floor(log2(levelUp)));
float offset = 1.0/(((int(2*base-levelUp)/2+1) & int(base/2-1))+1);
#endif
float dx0 = (1.0 - (levelUp-level)/2) / levelUp;
float dx1 = (1.0 - 2.0*dx0) / (levelUp - 2.0*ceil(dx0));
if (t < 0.5) {
float x = levelUp/2 - round(t*levelUp);
return 0.5 - (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
} else if (t > 0.5) {
float x = round(t*levelUp) - levelUp/2;
return 0.5 + (x*dx1 + int(x*offset > 1) * (dx0 - dx1));
} else {
return 0.5 * ((ti - n0) / n1) + 0.5;
return t;
}
}
#endif
float
OsdGetTessTransitionSplit(float t, float lo, float hi)
{
#if defined OSD_FRACTIONAL_EVEN_SPACING
float loRoundUp = OsdRoundUpEven(lo);
float hiRoundUp = OsdRoundUpEven(hi);
// Convert the parametric t into a segment index along the combined edge.
float ti = round(t * (loRoundUp + hiRoundUp));
if (ti <= loRoundUp) {
float t0 = ti / loRoundUp;
return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
} else {
float t1 = (ti - loRoundUp) / hiRoundUp;
return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
}
#elif defined OSD_FRACTIONAL_ODD_SPACING
float loRoundUp = OsdRoundUpOdd(lo);
float hiRoundUp = OsdRoundUpOdd(hi);
// Convert the parametric t into a segment index along the combined edge.
// The +1 below is to account for the extra segment produced by the
// tessellator since the sum of two odd tess levels will be rounded
// up by one to the next odd integer tess level.
float ti = round(t * (loRoundUp + hiRoundUp + 1));
if (ti <= loRoundUp) {
float t0 = ti / loRoundUp;
return OsdGetTessFractionalSplit(t0, lo, loRoundUp) * 0.5;
} else if (ti > (loRoundUp+1)) {
float t1 = (ti - (loRoundUp+1)) / hiRoundUp;
return OsdGetTessFractionalSplit(t1, hi, hiRoundUp) * 0.5 + 0.5;
} else {
return 0.5;
}
#else
// Convert the parametric t into a segment index along the combined edge.
float ti = round(t * (lo + hi));
if (ti <= lo) {
return (ti / lo) * 0.5;
} else {
return ((ti - lo) / hi) * 0.5 + 0.5;
}
#endif
}
float2
OsdGetTessParameterization(float2 uv, float4 tessOuterLo, float4 tessOuterHi)