TESSELLATION 1
OUTLINE Tessellation in OpenGL Tessellation of Bezier Surfaces Tessellation for Terrain/Height Maps Level of Detail 2
THE EXAMPLE
TESSELLATION SHADING Instead of specifying vertices, you specify a patch Just an ordered set of vertices Tessellation control shader determines how much geometry is generated from patch Other primitives are processed by vertex, fragment and geometry shaders and bypass tessellation You specify the total number of vertices in the patch (with primitives, OpenGL already knows how many vertices to expect) glpatchparameteri(glenum pname, Glint value); pname is set to GL_PATCH_VERTICES value is number of vertices glpatchparameteri(gl_patch_vertices, 4); gldrawarrays(gl_patches, 0, 8);
VERTEX SHADER #version 430 uniform mat4 mvp; void main(void) { }
TESSELLATION CONTROL SHADER #version 430 uniform mat4 mvp; layout (vertices = 1) out; void main(void) { gl_tesslevelouter[0] = 6; gl_tesslevelouter[2] = 6; gl_tesslevelouter[1] = 6; gl_tesslevelouter[3] = 6; gl_tesslevelinner[0] = 12; gl_tesslevelinner[1] = 12; } Specifies the number of output-patch vertices and specifies how many times the TCS will execute once for each output vertex.
TESSELLATION LEVELS
QUAD TESSELLATION
TRIANGLE TESSELLATION
TESSELLATION EVALUATION SHADER Executed once for each tessellation coordinate that the tessellator generates Determines the position of the vertex derived from the coordinate Looks similar to vertex shader Transforming vertices into screen positions (Unless we will use a geometry shader but that s at least one lecture away)
TESSELLATION EVALUATION SHADER #version 430 Primitive type can be quads, triangles or isolines layout (quads, equal_spacing, ccw) in; uniform mat4 mvp; cw or ccw for winding order void main (void) { } float u = gl_tesscoord.x; float v = gl_tesscoord.y; gl_position = mvp * vec4(u,0,v,1); equal_spacing, fractional_even_spacing or fractional_odd_spacing
SPACING equal_spacing is the default It subdivides the perimeter into the number of segments you specified Looks best here, but has the disadvantage that it rounds up to the nearest integer so if your object changes size you can get visible differences fractional_even and fractional_odd round down but you are left with a fractional triangle somewhere
FRAGMENT SHADER #version 430 out vec4 color; uniform mat4 mvp; void main(void) { color = vec4(1.0, 1.0, 0.0, 1.0); }
TESSELLATION FOR BEZIER SURFACES
VERTEX SHADER #version 430 out vec2 tc; uniform mat4 mvp; layout (binding = 0) uniform sampler2d tex_color; void main(void) { const vec4 vertices[] = vec4[] (vec4(-1.0, 0.5, -1.0, 1.0), vec4(-0.5, 0.5, -1.0, 1.0), vec4( 0.5, 0.5, -1.0, 1.0), vec4( 1.0, 0.5, -1.0, 1.0), vec4(-1.0, 0.0, -0.5, 1.0), vec4(-0.5, 0.0, -0.5, 1.0), vec4( 0.5, 0.0, -0.5, 1.0), vec4( 1.0, 0.0, -0.5, 1.0), vec4(-1.0, 0.0, 0.5, 1.0), vec4(-0.5, 0.0, 0.5, 1.0), vec4( 0.5, 0.0, 0.5, 1.0), vec4( 1.0, 0.0, 0.5, 1.0), vec4(-1.0,-0.5, 1.0, 1.0), vec4(-0.5, 0.3, 1.0, 1.0), vec4( 0.5, 0.3, 1.0, 1.0), vec4( 1.0, 0.3, 1.0, 1.0)); tc = vec2((vertices[gl_vertexid].x + 1.0)/2.0, (vertices[gl_vertexid].z + 1.0)/2.0); gl_position = vertices[gl_vertexid]; }
TESSELLATION CONTROL SHADER #version 430 in vec2 tc[]; out vec2 tcs_out[]; uniform mat4 mvp; layout (binding=0) uniform sampler2d tex_color; layout (vertices = 16) out; void main(void) { int TL = 32; // tessellation levels if (gl_invocationid ==0) { gl_tesslevelouter[0] = TL; gl_tesslevelouter[2] = TL; gl_tesslevelouter[1] = TL; gl_tesslevelouter[3] = TL; gl_tesslevelinner[0] = TL; gl_tesslevelinner[1] = TL; } 16 control points per patch tcs_out[gl_invocationid] = tc[gl_invocationid]; gl_out[gl_invocationid].gl_position = gl_in[gl_invocationid].gl_position; }
TESSELLATION EVALUATION SHADER #version 430 layout (quads, equal_spacing,ccw) in; uniform mat4 mvp; layout (binding = 0) uniform sampler2d tex_color; in vec2 tcs_out[]; out vec2 tes_out;
TESSELLATION EVALUATION SHADER void main (void) { vec3 p00 = (gl_in[0].gl_position).xyz; vec3 p10 = (gl_in[1].gl_position).xyz; vec3 p20 = (gl_in[2].gl_position).xyz; vec3 p30 = (gl_in[3].gl_position).xyz; vec3 p01 = (gl_in[4].gl_position).xyz; vec3 p11 = (gl_in[5].gl_position).xyz; vec3 p21 = (gl_in[6].gl_position).xyz; vec3 p31 = (gl_in[7].gl_position).xyz; vec3 p02 = (gl_in[8].gl_position).xyz; vec3 p12 = (gl_in[9].gl_position).xyz; vec3 p22 = (gl_in[10].gl_position).xyz; vec3 p32 = (gl_in[11].gl_position).xyz; vec3 p03 = (gl_in[12].gl_position).xyz; vec3 p13 = (gl_in[13].gl_position).xyz; vec3 p23 = (gl_in[14].gl_position).xyz; vec3 p33 = (gl_in[15].gl_position).xyz;
TESSELLATION EVALUATION SHADER float u = gl_tesscoord.x; float v = gl_tesscoord.y; // cubic Bezier basis functions float bu0 = (1.0-u) * (1.0-u) * (1.0-u); float bu1 = 3.0 * u * (1.0-u) * (1.0-u); float bu2 = 3. * u * u * (1.0-u); float bu3 = u * u * u; float bv0 = (1.0-v) * (1.0-v) * (1.0-v); float bv1 = 3.0 * v * (1.0-v) * (1.0-v); float bv2 = 3. * v * v * (1.0-v); float bv3 = v * v * v; //(1-u)^3 //3u(1-u)^2 //3u^2(1-u) //u^3 //(1-v)^3 //3v(1-v)^2 //3v^2(1-v) //v^3
TESSELLATION EVALUATION SHADER // output the position of this vertex in the tessellated patch vec3 outputposition = bu0 * ( bv0*p00 + bv1*p01 + bv2*p02 + bv3*p03 ) + bu1 * ( bv0*p10 + bv1*p11 + bv2*p12 + bv3*p13 ) + bu2 * ( bv0*p20 + bv1*p21 + bv2*p22 + bv3*p23 ) + bu3 * ( bv0*p30 + bv1*p31 + bv2*p32 + bv3*p33 ); gl_position = mvp * vec4(outputposition,1.0f); // shows bezier curve // gl_position = mvp * vec4(u,0,v,1); // shows original grid (pick one) // output the interpolated texture coordinates vec2 tc1 = mix(tcs_out[0], tcs_out[3], gl_tesscoord.x); vec2 tc2 = mix(tcs_out[12], tcs_out[15], gl_tesscoord.x); vec2 tc = mix(tc2, tc1, gl_tesscoord.y); tes_out = tc; }
FRAGMENT SHADER #version 430 in vec2 tes_out; out vec4 color; uniform mat4 mvp; layout (binding=0) uniform sampler2d tex_color; void main(void) { color = texture(tex_color, tes_out); }
TESSELLATION FOR TERRAIN / HEIGHT MAPS Height mapping from the vertex shader can lose detail Tesselation shaders introduce additional vertices Can use this to flesh out the detail Matches object geometry better Improves silhouette / edge detail Strategy: Place a tesselated grid in the x-z plane Use height map to set y coordinates Doesn t require any patches Use grey scale image for both texture and height map Initial result?
TESSELLATION FOR TERRAIN / HEIGHT MAPS White areas should be higher and black areas lower Does not correspond on the result Even by adding vertices with tessellation, the resolution is too low to capture details
TESSELLATION FOR TERRAIN / HEIGHT MAPS The solution: Use instancing Remember instancing from way early on? Rendering multiple Java objects with a single Java call Build a patch in the vertex shader Instance the picture with 64x64 patches - results in over 4 million vertices
VERTEX SHADER #version 430 out vec2 tc; uniform mat4 mvp; layout (binding = 0) uniform sampler2d tex_color; void main(void) { vec2 patchtexcoords[] = vec2[] (vec2(0,0), vec2(1,0), vec2(0,1), vec2(1,1)); // compute an offset for coordinates based on which instance this is int x = gl_instanceid % 64; int y = gl_instanceid / 64; // texture coordinates are distributed across 64 patches tc = vec2( (x+patchtexcoords[gl_vertexid].x)/64.0, (y+patchtexcoords[gl_vertexid].y)/64.0 ); } // vertex locations range from -0.5 to +0.5 gl_position = vec4(tc.x-0.5, 0.0, (1.0-tc.y)-0.5, 1.0);
TESSELLATION CONTROL SHADER #version 430 layout (vertices = 4) out; in vec2 tc[]; out vec2 tcs_out[]; uniform mat4 mvp; layout (binding=0) uniform sampler2d tex_color; void main(void) { int TL = 32; if (gl_invocationid == 0) { gl_tesslevelouter[0] = TL; gl_tesslevelouter[2] = TL; gl_tesslevelouter[1] = TL; gl_tesslevelouter[3] = TL; gl_tesslevelinner[0] = TL; gl_tesslevelinner[1] = TL; } } tcs_out[gl_invocationid] = tc[gl_invocationid]; gl_out[gl_invocationid].gl_position = gl_in[gl_invocationid].gl_position;
TESSELLATION EVALUATION SHADER #version 430 layout (quads, equal_spacing,ccw) in; uniform mat4 mvp; layout (binding = 0) uniform sampler2d tex_color; in vec2 tcs_out[]; out vec2 tes_out; void main (void) { // map the texture coordinates onto the sub-grid specified by the incoming control points vec2 tc = vec2(tcs_out[0].x+(gl_tesscoord.x)/64.0, tcs_out[0].y+(1.0-gl_tesscoord.y)/64.0); // map the tessellated grid onto the sub-grid specified by the incoming control points vec4 tessellatedpoint = vec4(gl_in[0].gl_position.x + gl_tesscoord.x / 64.0, 0.0, gl_in[0].gl_position.z + gl_tesscoord.y / 64.0, 1.0); // add the height from the height map to the vertex: tessellatedpoint.y += (texture(tex_color, tc).r) / 40.0; } gl_position = mvp * tessellatedpoint; tes_out = tc;
FRAGMENT SHADER #version 430 in vec2 tes_out; out vec4 color; uniform mat4 mvp; layout (binding=0) uniform sampler2d tex_color; void main(void) { color = texture(tex_color, tes_out); } And the result?
OVERLY JAGGED
ANOTHER ATTEMPT Height mapping detail is somewhat correct, but areas not quite right One problem is using a greyscale texture image for height doesn t quite work Not all variations in greyscale images are due to height- some are just color Another problem we don t have normals so we can do the lighting Solution use a true height map and a normal map Bonus the image now responds to lighting
HEIGHT MAP AND NORMAL MAP
WITH NORMAL MAP AND LIGHTING
SO WHAT S THE DRAWBACK? Imagery is great, but we have over 4 million vertices just to draw the moon surface Any additional graphic objects are going to start taxing the system Solution Only use high resolution in the areas that matter Those that are closer to the viewer Level of Detail: Changing the number of vertices based on the distance from the camera One catch changing detail level, as the object/camera moves can make parts of the image pop or wiggle The fix? Use fractional_even_spacing instead of equal_spacing
LEVEL OF DETAIL
SUMMARY Tessellation in OpenGL Tessellation of Bezier Surfaces Tessellation for Terrain/Height Maps Level of Detail 35