mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-11-26 21:40:07 +00:00
67cc2a3810
Updated the Gregory patch tessellation shaders to account for transition edges. Changed the implementation of OsdGetTessParameterizationTriangle() to take the input parametric location as a 3-component value. This allows the edge parameterization to be more numerically robust and consistent. Also, corrected some minor discrepancies between the various tessellation shader configurations.
733 lines
28 KiB
GLSL
733 lines
28 KiB
GLSL
//
|
|
// Copyright 2013-2018 Pixar
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "Apache License")
|
|
// with the following modification; you may not use this file except in
|
|
// compliance with the Apache License and the following modification to it:
|
|
// Section 6. Trademarks. is deleted and replaced with:
|
|
//
|
|
// 6. Trademarks. This License does not grant permission to use the trade
|
|
// names, trademarks, service marks, or product names of the Licensor
|
|
// and its affiliates, except as required to comply with Section 4(c) of
|
|
// the License and to reproduce the content of the NOTICE file.
|
|
//
|
|
// You may obtain a copy of the Apache License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the Apache License with the above modification is
|
|
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the Apache License for the specific
|
|
// language governing permissions and limitations under the Apache License.
|
|
//
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Tessellation
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// For now, fractional spacing is supported only with screen space tessellation
|
|
#ifndef OSD_ENABLE_SCREENSPACE_TESSELLATION
|
|
#undef OSD_FRACTIONAL_EVEN_SPACING
|
|
#undef OSD_FRACTIONAL_ODD_SPACING
|
|
#endif
|
|
|
|
#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
|
|
|
|
//
|
|
// Organization of B-spline and Bezier control points.
|
|
//
|
|
// Each patch is defined by 16 control points (labeled 0-15).
|
|
//
|
|
// The patch will be evaluated across the domain from (0,0) at
|
|
// the lower-left to (1,1) at the upper-right. When computing
|
|
// adaptive tessellation metrics, we consider refined vertex-vertex
|
|
// and edge-vertex points along the transition edges of the patch
|
|
// (labeled vv* and ev* respectively).
|
|
//
|
|
// The two segments of each transition edge are labeled Lo and Hi,
|
|
// with the Lo segment occurring before the Hi segment along the
|
|
// transition edge's domain parameterization. These Lo and Hi segment
|
|
// tessellation levels determine how domain evaluation coordinates
|
|
// are remapped along transition edges. The Hi segment value will
|
|
// be zero for a non-transition edge.
|
|
//
|
|
// (0,1) (1,1)
|
|
//
|
|
// vv3 ev23 vv2
|
|
// | Lo3 | Hi3 |
|
|
// --O-----------O-----+-----O-----------O--
|
|
// | 12 | 13 14 | 15 |
|
|
// | | | |
|
|
// | | | |
|
|
// Hi0 | | | | Hi2
|
|
// | | | |
|
|
// O-----------O-----------O-----------O
|
|
// | 8 | 9 10 | 11 |
|
|
// | | | |
|
|
// ev03 --+ | | +-- ev12
|
|
// | | | |
|
|
// | 4 | 5 6 | 7 |
|
|
// O-----------O-----------O-----------O
|
|
// | | | |
|
|
// Lo0 | | | | Lo2
|
|
// | | | |
|
|
// | | | |
|
|
// | 0 | 1 2 | 3 |
|
|
// --O-----------O-----+-----O-----------O--
|
|
// | Lo1 | Hi1 |
|
|
// vv0 ev01 vv1
|
|
//
|
|
// (0,0) (1,0)
|
|
//
|
|
|
|
#define OSD_MAX_TESS_LEVEL gl_MaxTessGenLevel
|
|
|
|
float OsdComputePostProjectionSphereExtent(vec3 center, float diameter)
|
|
{
|
|
vec4 p = OsdProjectionMatrix() * vec4(center, 1.0);
|
|
return abs(diameter * OsdProjectionMatrix()[1][1] / p.w);
|
|
}
|
|
|
|
float OsdComputeTessLevel(vec3 p0, vec3 p1)
|
|
{
|
|
// Adaptive factor can be any computation that depends only on arg values.
|
|
// Project the diameter of the edge's bounding sphere instead of using the
|
|
// length of the projected edge itself to avoid problems near silhouettes.
|
|
p0 = (OsdModelViewMatrix() * vec4(p0, 1.0)).xyz;
|
|
p1 = (OsdModelViewMatrix() * vec4(p1, 1.0)).xyz;
|
|
vec3 center = (p0 + p1) / 2.0;
|
|
float diameter = distance(p0, p1);
|
|
float projLength = OsdComputePostProjectionSphereExtent(center, diameter);
|
|
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
|
|
// halves and the sum of the two corresponding levels must not exceed
|
|
// the device maximum. We impose this limit even for non-transition
|
|
// edges because a non-transition edge must be able to match up with
|
|
// one half of the transition edge of an adjacent transition patch.
|
|
return min(tessLevel, OSD_MAX_TESS_LEVEL / 2);
|
|
}
|
|
|
|
void
|
|
OsdGetTessLevelsUniform(ivec3 patchParam,
|
|
out vec4 tessOuterLo, out vec4 tessOuterHi)
|
|
{
|
|
// Uniform factors are simple powers of two for each level.
|
|
// The maximum here can be increased if we know the maximum
|
|
// refinement level of the mesh:
|
|
// min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
|
|
int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
|
|
float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
|
|
pow(2, refinementLevel-1);
|
|
|
|
// tessLevels of transition edge should be clamped to 2.
|
|
int transitionMask = OsdGetPatchTransitionMask(patchParam);
|
|
vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 8) >> 3),
|
|
((transitionMask & 1) >> 0),
|
|
((transitionMask & 2) >> 1),
|
|
((transitionMask & 4) >> 2));
|
|
|
|
tessOuterLo = max(vec4(tessLevel), tessLevelMin);
|
|
tessOuterHi = vec4(0);
|
|
}
|
|
|
|
void
|
|
OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
|
|
out vec4 tessOuterLo, out vec4 tessOuterHi)
|
|
{
|
|
// Uniform factors are simple powers of two for each level.
|
|
// The maximum here can be increased if we know the maximum
|
|
// refinement level of the mesh:
|
|
// min(OSD_MAX_TESS_LEVEL, pow(2, MaximumRefinementLevel-1)
|
|
int refinementLevel = OsdGetPatchRefinementLevel(patchParam);
|
|
float tessLevel = min(OsdTessLevel(), OSD_MAX_TESS_LEVEL) /
|
|
pow(2, refinementLevel-1);
|
|
|
|
// tessLevels of transition edge should be clamped to 2.
|
|
int transitionMask = OsdGetPatchTransitionMask(patchParam);
|
|
vec4 tessLevelMin = vec4(1) + vec4(((transitionMask & 4) >> 2),
|
|
((transitionMask & 1) >> 0),
|
|
((transitionMask & 2) >> 1),
|
|
0);
|
|
|
|
tessOuterLo = max(vec4(tessLevel), tessLevelMin);
|
|
tessOuterHi = vec4(0);
|
|
}
|
|
|
|
void
|
|
OsdGetTessLevelsRefinedPoints(vec3 cp[16], ivec3 patchParam,
|
|
out vec4 tessOuterLo, out vec4 tessOuterHi)
|
|
{
|
|
// Each edge of a transition patch is adjacent to one or two patches
|
|
// at the next refined level of subdivision. We compute the corresponding
|
|
// vertex-vertex and edge-vertex refined points along the edges of the
|
|
// patch using Catmull-Clark subdivision stencil weights.
|
|
// For simplicity, we let the optimizer discard unused computation.
|
|
|
|
vec3 vv0 = (cp[0] + cp[2] + cp[8] + cp[10]) * 0.015625 +
|
|
(cp[1] + cp[4] + cp[6] + cp[9]) * 0.09375 + cp[5] * 0.5625;
|
|
vec3 ev01 = (cp[1] + cp[2] + cp[9] + cp[10]) * 0.0625 +
|
|
(cp[5] + cp[6]) * 0.375;
|
|
|
|
vec3 vv1 = (cp[1] + cp[3] + cp[9] + cp[11]) * 0.015625 +
|
|
(cp[2] + cp[5] + cp[7] + cp[10]) * 0.09375 + cp[6] * 0.5625;
|
|
vec3 ev12 = (cp[5] + cp[7] + cp[9] + cp[11]) * 0.0625 +
|
|
(cp[6] + cp[10]) * 0.375;
|
|
|
|
vec3 vv2 = (cp[5] + cp[7] + cp[13] + cp[15]) * 0.015625 +
|
|
(cp[6] + cp[9] + cp[11] + cp[14]) * 0.09375 + cp[10] * 0.5625;
|
|
vec3 ev23 = (cp[5] + cp[6] + cp[13] + cp[14]) * 0.0625 +
|
|
(cp[9] + cp[10]) * 0.375;
|
|
|
|
vec3 vv3 = (cp[4] + cp[6] + cp[12] + cp[14]) * 0.015625 +
|
|
(cp[5] + cp[8] + cp[10] + cp[13]) * 0.09375 + cp[9] * 0.5625;
|
|
vec3 ev03 = (cp[4] + cp[6] + cp[8] + cp[10]) * 0.0625 +
|
|
(cp[5] + cp[9]) * 0.375;
|
|
|
|
tessOuterLo = vec4(0);
|
|
tessOuterHi = vec4(0);
|
|
|
|
int transitionMask = OsdGetPatchTransitionMask(patchParam);
|
|
|
|
if ((transitionMask & 8) != 0) {
|
|
tessOuterLo[0] = OsdComputeTessLevel(vv0, ev03);
|
|
tessOuterHi[0] = OsdComputeTessLevel(vv3, ev03);
|
|
} else {
|
|
tessOuterLo[0] = OsdComputeTessLevel(cp[5], cp[9]);
|
|
}
|
|
if ((transitionMask & 1) != 0) {
|
|
tessOuterLo[1] = OsdComputeTessLevel(vv0, ev01);
|
|
tessOuterHi[1] = OsdComputeTessLevel(vv1, ev01);
|
|
} else {
|
|
tessOuterLo[1] = OsdComputeTessLevel(cp[5], cp[6]);
|
|
}
|
|
if ((transitionMask & 2) != 0) {
|
|
tessOuterLo[2] = OsdComputeTessLevel(vv1, ev12);
|
|
tessOuterHi[2] = OsdComputeTessLevel(vv2, ev12);
|
|
} else {
|
|
tessOuterLo[2] = OsdComputeTessLevel(cp[6], cp[10]);
|
|
}
|
|
if ((transitionMask & 4) != 0) {
|
|
tessOuterLo[3] = OsdComputeTessLevel(vv3, ev23);
|
|
tessOuterHi[3] = OsdComputeTessLevel(vv2, ev23);
|
|
} else {
|
|
tessOuterLo[3] = OsdComputeTessLevel(cp[9], cp[10]);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Patch boundary corners are ordered counter-clockwise from the first
|
|
// corner while patch boundary edges and their midpoints are similarly
|
|
// ordered counter-clockwise beginning at the edge preceding corner[0].
|
|
//
|
|
void
|
|
Osd_GetTessLevelsFromPatchBoundaries4(vec3 corners[4], vec3 midpoints[4],
|
|
ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
|
|
{
|
|
tessOuterLo = vec4(0);
|
|
tessOuterHi = vec4(0);
|
|
|
|
int transitionMask = OsdGetPatchTransitionMask(patchParam);
|
|
|
|
if ((transitionMask & 8) != 0) {
|
|
tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
|
|
tessOuterHi[0] = OsdComputeTessLevel(corners[3], midpoints[0]);
|
|
} else {
|
|
tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[3]);
|
|
}
|
|
if ((transitionMask & 1) != 0) {
|
|
tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
|
|
tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
|
|
} else {
|
|
tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
|
|
}
|
|
if ((transitionMask & 2) != 0) {
|
|
tessOuterLo[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
|
|
tessOuterHi[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
|
|
} else {
|
|
tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
|
|
}
|
|
if ((transitionMask & 4) != 0) {
|
|
tessOuterLo[3] = OsdComputeTessLevel(corners[3], midpoints[3]);
|
|
tessOuterHi[3] = OsdComputeTessLevel(corners[2], midpoints[3]);
|
|
} else {
|
|
tessOuterLo[3] = OsdComputeTessLevel(corners[3], corners[2]);
|
|
}
|
|
}
|
|
|
|
void
|
|
Osd_GetTessLevelsFromPatchBoundaries3(vec3 corners[3], vec3 midpoints[3],
|
|
ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
|
|
{
|
|
tessOuterLo = vec4(0);
|
|
tessOuterHi = vec4(0);
|
|
|
|
int transitionMask = OsdGetPatchTransitionMask(patchParam);
|
|
|
|
if ((transitionMask & 4) != 0) {
|
|
tessOuterLo[0] = OsdComputeTessLevel(corners[0], midpoints[0]);
|
|
tessOuterHi[0] = OsdComputeTessLevel(corners[2], midpoints[0]);
|
|
} else {
|
|
tessOuterLo[0] = OsdComputeTessLevel(corners[0], corners[2]);
|
|
}
|
|
if ((transitionMask & 1) != 0) {
|
|
tessOuterLo[1] = OsdComputeTessLevel(corners[0], midpoints[1]);
|
|
tessOuterHi[1] = OsdComputeTessLevel(corners[1], midpoints[1]);
|
|
} else {
|
|
tessOuterLo[1] = OsdComputeTessLevel(corners[0], corners[1]);
|
|
}
|
|
if ((transitionMask & 2) != 0) {
|
|
tessOuterLo[2] = OsdComputeTessLevel(corners[2], midpoints[2]);
|
|
tessOuterHi[2] = OsdComputeTessLevel(corners[1], midpoints[2]);
|
|
} else {
|
|
tessOuterLo[2] = OsdComputeTessLevel(corners[1], corners[2]);
|
|
}
|
|
}
|
|
|
|
vec3
|
|
Osd_EvalBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3)
|
|
{
|
|
// Coefficients for the midpoint are { 1/8, 3/8, 3/8, 1/8 }:
|
|
return 0.125 * (p0 + p3) + 0.375 * (p1 + p2);
|
|
}
|
|
|
|
vec3
|
|
Osd_EvalQuarticBezierCurveMidPoint(vec3 p0, vec3 p1, vec3 p2, vec3 p3, vec3 p4)
|
|
{
|
|
// Coefficients for the midpoint are { 1/16, 1/4, 3/8, 1/4, 1/16 }:
|
|
return 0.0625 * (p0 + p4) + 0.25 * (p1 + p3) + 0.375 * p2;
|
|
}
|
|
|
|
void
|
|
OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
|
|
ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
|
|
{
|
|
// Each edge of a transition patch is adjacent to one or two patches
|
|
// at the next refined level of subdivision. When the patch control
|
|
// points have been converted to the Bezier basis, the control points
|
|
// at the four corners are on the limit surface (since a Bezier patch
|
|
// interpolates its corner control points). We can compute an adaptive
|
|
// tessellation level for transition edges on the limit surface by
|
|
// evaluating a limit position at the mid point of each transition edge.
|
|
|
|
tessOuterLo = vec4(0);
|
|
tessOuterHi = vec4(0);
|
|
|
|
vec3 corners[4];
|
|
vec3 midpoints[4];
|
|
|
|
int transitionMask = OsdGetPatchTransitionMask(patchParam);
|
|
|
|
#if defined OSD_PATCH_ENABLE_SINGLE_CREASE
|
|
corners[0] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.0));
|
|
corners[1] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.0));
|
|
corners[2] = OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 1.0));
|
|
corners[3] = OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 1.0));
|
|
|
|
midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
|
|
OsdEvalBezier(cpBezier, patchParam, vec2(0.0, 0.5));
|
|
midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
|
|
OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 0.0));
|
|
midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
|
|
OsdEvalBezier(cpBezier, patchParam, vec2(1.0, 0.5));
|
|
midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
|
|
OsdEvalBezier(cpBezier, patchParam, vec2(0.5, 1.0));
|
|
#else
|
|
corners[0] = cpBezier[ 0].P;
|
|
corners[1] = cpBezier[ 3].P;
|
|
corners[2] = cpBezier[15].P;
|
|
corners[3] = cpBezier[12].P;
|
|
|
|
midpoints[0] = ((transitionMask & 8) == 0) ? vec3(0) :
|
|
Osd_EvalBezierCurveMidPoint(
|
|
cpBezier[0].P, cpBezier[4].P, cpBezier[8].P, cpBezier[12].P);
|
|
midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
|
|
Osd_EvalBezierCurveMidPoint(
|
|
cpBezier[0].P, cpBezier[1].P, cpBezier[2].P, cpBezier[3].P);
|
|
midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
|
|
Osd_EvalBezierCurveMidPoint(
|
|
cpBezier[3].P, cpBezier[7].P, cpBezier[11].P, cpBezier[15].P);
|
|
midpoints[3] = ((transitionMask & 4) == 0) ? vec3(0) :
|
|
Osd_EvalBezierCurveMidPoint(
|
|
cpBezier[12].P, cpBezier[13].P, cpBezier[14].P, cpBezier[15].P);
|
|
#endif
|
|
|
|
Osd_GetTessLevelsFromPatchBoundaries4(corners, midpoints,
|
|
patchParam, tessOuterLo, tessOuterHi);
|
|
}
|
|
|
|
void
|
|
OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
|
|
ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
|
|
{
|
|
// Each edge of a transition patch is adjacent to one or two patches
|
|
// at the next refined level of subdivision. When the patch control
|
|
// points have been converted to the Bezier basis, the control points
|
|
// at the corners are on the limit surface (since a Bezier patch
|
|
// interpolates its corner control points). We can compute an adaptive
|
|
// tessellation level for transition edges on the limit surface by
|
|
// evaluating a limit position at the mid point of each transition edge.
|
|
|
|
tessOuterLo = vec4(0);
|
|
tessOuterHi = vec4(0);
|
|
|
|
int transitionMask = OsdGetPatchTransitionMask(patchParam);
|
|
|
|
vec3 corners[3];
|
|
corners[0] = cv[0];
|
|
corners[1] = cv[4];
|
|
corners[2] = cv[14];
|
|
|
|
vec3 midpoints[3];
|
|
midpoints[0] = ((transitionMask & 4) == 0) ? vec3(0) :
|
|
Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[5], cv[9], cv[12], cv[14]);
|
|
midpoints[1] = ((transitionMask & 1) == 0) ? vec3(0) :
|
|
Osd_EvalQuarticBezierCurveMidPoint(cv[0], cv[1], cv[2], cv[3], cv[4]);
|
|
midpoints[2] = ((transitionMask & 2) == 0) ? vec3(0) :
|
|
Osd_EvalQuarticBezierCurveMidPoint(cv[4], cv[8], cv[11], cv[13], cv[14]);
|
|
|
|
Osd_GetTessLevelsFromPatchBoundaries3(corners, midpoints,
|
|
patchParam, tessOuterLo, tessOuterHi);
|
|
}
|
|
|
|
// 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
|
|
OsdComputeTessLevelsTriangle(inout vec4 tessOuterLo, inout vec4 tessOuterHi,
|
|
out vec4 tessLevelOuter, out vec2 tessLevelInner)
|
|
{
|
|
OsdComputeTessLevels(tessOuterLo, tessOuterHi,
|
|
tessLevelOuter, tessLevelInner);
|
|
|
|
// Inner level is the max of the three outer levels.
|
|
tessLevelInner[0] = max(max(tessLevelOuter[0],
|
|
tessLevelOuter[1]),
|
|
tessLevelOuter[2]);
|
|
}
|
|
|
|
void
|
|
OsdGetTessLevelsUniform(ivec3 patchParam,
|
|
out vec4 tessLevelOuter, out vec2 tessLevelInner,
|
|
out vec4 tessOuterLo, out vec4 tessOuterHi)
|
|
{
|
|
OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
|
|
|
|
OsdComputeTessLevels(tessOuterLo, tessOuterHi,
|
|
tessLevelOuter, tessLevelInner);
|
|
}
|
|
|
|
void
|
|
OsdGetTessLevelsUniformTriangle(ivec3 patchParam,
|
|
out vec4 tessLevelOuter, out vec2 tessLevelInner,
|
|
out vec4 tessOuterLo, out vec4 tessOuterHi)
|
|
{
|
|
OsdGetTessLevelsUniformTriangle(patchParam, tessOuterLo, tessOuterHi);
|
|
|
|
OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
|
|
tessLevelOuter, tessLevelInner);
|
|
}
|
|
|
|
void
|
|
OsdEvalPatchBezierTessLevels(OsdPerPatchVertexBezier cpBezier[16],
|
|
ivec3 patchParam,
|
|
out vec4 tessLevelOuter, out vec2 tessLevelInner,
|
|
out vec4 tessOuterLo, out vec4 tessOuterHi)
|
|
{
|
|
OsdEvalPatchBezierTessLevels(cpBezier, patchParam,
|
|
tessOuterLo, tessOuterHi);
|
|
|
|
OsdComputeTessLevels(tessOuterLo, tessOuterHi,
|
|
tessLevelOuter, tessLevelInner);
|
|
}
|
|
|
|
void
|
|
OsdEvalPatchBezierTriangleTessLevels(vec3 cv[15],
|
|
ivec3 patchParam,
|
|
out vec4 tessLevelOuter, out vec2 tessLevelInner,
|
|
out vec4 tessOuterLo, out vec4 tessOuterHi)
|
|
{
|
|
OsdEvalPatchBezierTriangleTessLevels(cv, patchParam,
|
|
tessOuterLo, tessOuterHi);
|
|
|
|
OsdComputeTessLevelsTriangle(tessOuterLo, tessOuterHi,
|
|
tessLevelOuter, tessLevelInner);
|
|
}
|
|
|
|
void
|
|
OsdGetTessLevelsAdaptiveRefinedPoints(vec3 cpRefined[16], ivec3 patchParam,
|
|
out vec4 tessLevelOuter, out vec2 tessLevelInner,
|
|
out vec4 tessOuterLo, out vec4 tessOuterHi)
|
|
{
|
|
OsdGetTessLevelsRefinedPoints(cpRefined, patchParam,
|
|
tessOuterLo, tessOuterHi);
|
|
|
|
OsdComputeTessLevels(tessOuterLo, tessOuterHi,
|
|
tessLevelOuter, tessLevelInner);
|
|
}
|
|
|
|
// Deprecated -- prefer use of newer Bezier patch equivalent:
|
|
void
|
|
OsdGetTessLevelsLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
|
|
ivec3 patchParam, out vec4 tessOuterLo, out vec4 tessOuterHi)
|
|
{
|
|
OsdEvalPatchBezierTessLevels(cpBezier, patchParam, tessOuterLo, tessOuterHi);
|
|
}
|
|
|
|
// Deprecated -- prefer use of newer Bezier patch equivalent:
|
|
void
|
|
OsdGetTessLevelsAdaptiveLimitPoints(OsdPerPatchVertexBezier cpBezier[16],
|
|
ivec3 patchParam,
|
|
out vec4 tessLevelOuter, out vec2 tessLevelInner,
|
|
out vec4 tessOuterLo, out vec4 tessOuterHi)
|
|
{
|
|
OsdGetTessLevelsLimitPoints(cpBezier, patchParam,
|
|
tessOuterLo, tessOuterHi);
|
|
|
|
OsdComputeTessLevels(tessOuterLo, tessOuterHi,
|
|
tessLevelOuter, tessLevelInner);
|
|
}
|
|
|
|
// Deprecated -- prefer use of newer Bezier patch equivalent:
|
|
void
|
|
OsdGetTessLevels(vec3 cp0, vec3 cp1, vec3 cp2, vec3 cp3,
|
|
ivec3 patchParam,
|
|
out vec4 tessLevelOuter, out vec2 tessLevelInner)
|
|
{
|
|
vec4 tessOuterLo = vec4(0);
|
|
vec4 tessOuterHi = vec4(0);
|
|
|
|
#if defined OSD_ENABLE_SCREENSPACE_TESSELLATION
|
|
tessOuterLo[0] = OsdComputeTessLevel(cp0, cp1);
|
|
tessOuterLo[1] = OsdComputeTessLevel(cp0, cp3);
|
|
tessOuterLo[2] = OsdComputeTessLevel(cp2, cp3);
|
|
tessOuterLo[3] = OsdComputeTessLevel(cp1, cp2);
|
|
tessOuterHi = vec4(0);
|
|
#else
|
|
OsdGetTessLevelsUniform(patchParam, tessOuterLo, tessOuterHi);
|
|
#endif
|
|
|
|
OsdComputeTessLevels(tessOuterLo, tessOuterHi,
|
|
tessLevelOuter, tessLevelInner);
|
|
}
|
|
|
|
#if defined OSD_FRACTIONAL_EVEN_SPACING || defined OSD_FRACTIONAL_ODD_SPACING
|
|
float
|
|
OsdGetTessFractionalSplit(float t, float level, float levelUp)
|
|
{
|
|
// 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 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 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 p, vec4 tessOuterLo, vec4 tessOuterHi)
|
|
{
|
|
vec2 UV = p;
|
|
if (p.x == 0 && tessOuterHi[0] > 0) {
|
|
UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
|
|
} else
|
|
if (p.y == 0 && tessOuterHi[1] > 0) {
|
|
UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
|
|
} else
|
|
if (p.x == 1 && tessOuterHi[2] > 0) {
|
|
UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[2], tessOuterHi[2]);
|
|
} else
|
|
if (p.y == 1 && tessOuterHi[3] > 0) {
|
|
UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[3], tessOuterHi[3]);
|
|
}
|
|
return UV;
|
|
}
|
|
|
|
vec2
|
|
OsdGetTessParameterizationTriangle(vec3 p, vec4 tessOuterLo, vec4 tessOuterHi)
|
|
{
|
|
vec2 UV = p.xy;
|
|
if (p.x == 0 && tessOuterHi[0] > 0) {
|
|
UV.y = OsdGetTessTransitionSplit(UV.y, tessOuterLo[0], tessOuterHi[0]);
|
|
} else
|
|
if (p.y == 0 && tessOuterHi[1] > 0) {
|
|
UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[1], tessOuterHi[1]);
|
|
} else
|
|
if (p.z == 0 && tessOuterHi[2] > 0) {
|
|
UV.x = OsdGetTessTransitionSplit(UV.x, tessOuterLo[2], tessOuterHi[2]);
|
|
UV.y = 1.0 - UV.x;
|
|
}
|
|
return UV;
|
|
}
|