1/* 2The MIT License (MIT) 3 4Copyright (c) 2022 Sascha Willems 5 6Permission is hereby granted, free of charge, to any person obtaining a copy 7of this software and associated documentation files (the "Software"), to deal 8in the Software without restriction, including without limitation the rights 9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10copies of the Software, and to permit persons to whom the Software is 11furnished to do so, subject to the following conditions: 12 13The above copyright notice and this permission notice shall be included in all 14copies or substantial portions of the Software. 15 16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22SOFTWARE. 23*/ 24 25#version 450 26 27layout(set = 0, binding = 0) uniform UBO 28{ 29 mat4 projection; 30 mat4 modelview; 31 vec4 lightPos; 32 vec4 frustumPlanes[6]; 33 float displacementFactor; 34 float tessellationFactor; 35 vec2 viewportDim; 36 float tessellatedEdgeSize; 37} ubo; 38 39layout(set = 0, binding = 1) uniform sampler2D samplerHeight; 40 41layout (vertices = 4) out; 42 43layout (location = 0) in vec3 inNormal[]; 44layout (location = 1) in vec2 inUV[]; 45 46layout (location = 0) out vec3 outNormal[4]; 47layout (location = 1) out vec2 outUV[4]; 48 49// Calculate the tessellation factor based on screen space 50// dimensions of the edge 51float screenSpaceTessFactor(vec4 p0, vec4 p1) 52{ 53 // Calculate edge mid point 54 vec4 midPoint = 0.5 * (p0 + p1); 55 // Sphere radius as distance between the control points 56 float radius = distance(p0, p1) / 2.0; 57 58 // View space 59 vec4 v0 = ubo.modelview * midPoint; 60 61 // Project into clip space 62 vec4 clip0 = (ubo.projection * (v0 - vec4(radius, vec3(0.0)))); 63 vec4 clip1 = (ubo.projection * (v0 + vec4(radius, vec3(0.0)))); 64 65 // Get normalized device coordinates 66 clip0 /= clip0.w; 67 clip1 /= clip1.w; 68 69 // Convert to viewport coordinates 70 clip0.xy *= ubo.viewportDim; 71 clip1.xy *= ubo.viewportDim; 72 73 // Return the tessellation factor based on the screen size 74 // given by the distance of the two edge control points in screen space 75 // and a reference (min.) tessellation size for the edge set by the application 76 return clamp(distance(clip0, clip1) / ubo.tessellatedEdgeSize * ubo.tessellationFactor, 1.0, 64.0); 77} 78 79// Checks the current's patch visibility against the frustum using a sphere check 80// Sphere radius is given by the patch size 81bool frustumCheck() 82{ 83 // Fixed radius (increase if patch size is increased in example) 84 const float radius = 8.0f; 85 vec4 pos = gl_in[gl_InvocationID].gl_Position; 86 pos.y -= textureLod(samplerHeight, inUV[0], 0.0).r * ubo.displacementFactor; 87 88 // Check sphere against frustum planes 89 for (int i = 0; i < 6; i++) { 90 if (dot(pos, ubo.frustumPlanes[i]) + radius < 0.0) 91 { 92 return false; 93 } 94 } 95 return true; 96} 97 98void main() 99{ 100 if (gl_InvocationID == 0) 101 { 102 if (!frustumCheck()) 103 { 104 gl_TessLevelInner[0] = 0.0; 105 gl_TessLevelInner[1] = 0.0; 106 gl_TessLevelOuter[0] = 0.0; 107 gl_TessLevelOuter[1] = 0.0; 108 gl_TessLevelOuter[2] = 0.0; 109 gl_TessLevelOuter[3] = 0.0; 110 } 111 else 112 { 113 if (ubo.tessellationFactor > 0.0) 114 { 115 gl_TessLevelOuter[0] = screenSpaceTessFactor(gl_in[3].gl_Position, gl_in[0].gl_Position); 116 gl_TessLevelOuter[1] = screenSpaceTessFactor(gl_in[0].gl_Position, gl_in[1].gl_Position); 117 gl_TessLevelOuter[2] = screenSpaceTessFactor(gl_in[1].gl_Position, gl_in[2].gl_Position); 118 gl_TessLevelOuter[3] = screenSpaceTessFactor(gl_in[2].gl_Position, gl_in[3].gl_Position); 119 gl_TessLevelInner[0] = mix(gl_TessLevelOuter[0], gl_TessLevelOuter[3], 0.5); 120 gl_TessLevelInner[1] = mix(gl_TessLevelOuter[2], gl_TessLevelOuter[1], 0.5); 121 } 122 else 123 { 124 // Tessellation factor can be set to zero by example 125 // to demonstrate a simple passthrough 126 gl_TessLevelInner[0] = 1.0; 127 gl_TessLevelInner[1] = 1.0; 128 gl_TessLevelOuter[0] = 1.0; 129 gl_TessLevelOuter[1] = 1.0; 130 gl_TessLevelOuter[2] = 1.0; 131 gl_TessLevelOuter[3] = 1.0; 132 } 133 } 134 135 } 136 137 gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; 138 outNormal[gl_InvocationID] = inNormal[gl_InvocationID]; 139 outUV[gl_InvocationID] = inUV[gl_InvocationID]; 140} 141