• 12
• 14
• 13
• 10
• 11

# Planet Rendering: Spherical Level-of-Detail in less than 100 lines of C++

This topic is 712 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Interesting. Thanks for sharing this.

##### Share on other sites
it's a good start, you should give it a few more iterations.

1.there is a simpler way to tessellate to a sphere, starting with a tetrahedron (4 triangles) and halving the edges of the triangles at every step.
your tessellation has the classical t-junction issue, which usually takes the most code to fix. with tetrahedrons, when you evaluate per-edge, it's actually very simple to get a closed mesh.

2.usually you would increase the tessellation at the silhouette, not at the center (but that decision probably depends on a lot of factors)

##### Share on other sites
really neat :)

you still have gaps/t-junctions, you should evaluate if the tessellation is needed on the center of the edges and based on that create triangles.
something like
uint32_t edge0 = NeedSubdivide((V0+V1)/2);
uint32_t edge1 = NeedSubdivide((V1+V2)/2);
uint32_t edge2 = NeedSubdivide((V2+V0)/2);
switch(edge0|edge1<<1|edge2<<2)
{
case 0:
//no subdivision, spit out triangle
break;
case 1:
.
.
.
default:
//the way you subdivide now
}
(there is a way more elegant way to solve this,I won't spoil this puzzle :) )


1. I think it doesn't work out of the box with all meshes. you need to normalize the distances to center, which limits this to sphere.
				p[idx[3 * i + 0]].norm(),
p[idx[3 * i + 1]].norm(),
p[idx[3 * i + 2]].norm(),

to apply it on all meshes, something simple to get running is either bezier patches (which nicely works per patch, but is not recursive).
my old attempt http://imgbox.com/qVkijDlM (timestamp says it's from 1996 :o)
or try catmul clark, which is simple, but works always on the whole mesh (closing t-junctions works similar to the tetraheder->sphere subdivision)
https://en.wikipedia.org/wiki/Catmull–Clark_subdivision_surface
that was my attempt:
http://twitpic.com/3ud6cx

2. yeah, had a feeling like it might be for planetary walking. you should make a fly-through, from space to surface, with tessellation :) Edited by Krypt0n

##### Share on other sites

Thx for the tip :)

The edge idea was good.

Just adopted it to the code.

Your bezier and subdivision samples look nice. I wonder if its easy to make it to a recursive approach (looks like uniform subdivision in the screenshots)

For the planet thats not needed at the moment.

1. Yes, due to normalizing it does not work immediately for the current code, but you could use  a displacement map and combine rendering with a tessellation shader.

2. Yes, fly though is in progress. First I needed some basic rendering code however. VBO is now already implemented and next is to find a good mapping for the heightmap to the surface. That will be another challenge to create a seamless heightmap that has same scaling all over the planet.

Results:

The new mesh now looks like the following:

[attachment=31411:Clipboard01.png]

Also using patches is possible right away without further meshes that fix LOD boundaries.

[attachment=31412:patches.png]

The new code looks like this:

static void draw_recursive(vec3f p1,vec3f p2,vec3f p3, vec3f center , float size=1)
{
float ratio_size = size * gui.screen[0].slider["lod.ratio"].val; // default : 1
float minsize = gui.screen[0].slider["detail"].val;    // default : 0.01
vec3f edge_center[3] = { (p1 + p2) / 2, (p2 + p3) / 2, (p3 + p1) / 2 };
bool edge_test[3]; double angle[3];

loopi(0, 3)
{
double dot = edge_center[i].dot(center);
angle[i] = acos(clamp(dot, -1, 1)) / M_PI;
edge_test[i] = angle[i] > ratio_size;
}

if (min(angle[0], min(angle[1], angle[2])) > 0.5) return;//culling

if ((edge_test[0] && edge_test[1] && edge_test[2]) || size < minsize)
{
if (gui.screen[0].checkbox["patches"].checked)
draw_patch(p1, p2, p3);
else
draw_triangle(p1, p2, p3);
return;
}
// Recurse
vec3f p[6] = { p1, p2, p3, edge_center[0], edge_center[1], edge_center[2] };
int idx[12] = { 0, 3, 5,    5, 3, 4,    3, 1, 4,    5, 4, 2 };
bool valid[4] = { 1, 1, 1, 1 };

if (edge_test[0]){ p[3] = p1; valid[0] = 0; } // skip triangle 0 ?
if (edge_test[1]){ p[4] = p2; valid[2] = 0; } // skip triangle 2 ?
if (edge_test[2]){ p[5] = p3; valid[3] = 0; } // skip triangle 3 ?

loopi(0, 4) if (valid[i])
{
draw_recursive(
p[idx[3 * i + 0]].norm(),
p[idx[3 * i + 1]].norm(),
p[idx[3 * i + 2]].norm(),
center,size/2 );
}
}


Edited by spacerat

##### Share on other sites

This is really cool. You've distilled the process down further than I've ever attempted :)

##### Share on other sites

Your bezier and subdivision samples look nice. I wonder if its easy to make it to a recursive approach (looks like uniform subdivision in the screenshots)

the subdivision (catmull clark) is recursive. it's used in the Reyes rendering.
it's a really elegant algorithm.

1. Yes, due to normalizing it does not work immediately for the current code, but you could use  a displacement map and combine rendering with a tessellation shader.

there goes all the challenge ;)
there was a nice paper about subdivision:
http://miciwan.com/GDC2011/GDC2011_Mega_Meshes.pdf

##### Share on other sites

@Krypt0n: Yes, mega meshes is indeed a very nice paper. I remember that one.

@rept_ai:  Very nice Demo ! I will look at the source.

Just modified the rendering. Basically it works nice, but once you go up close then float precision is not enough. What is the best solution to that other than splitting the frustum in near / far ?

http://outerra.blogspot.de/2012/11/maximizing-depth-buffer-range-and.html

and

Edit: The Github code is updated and now also renders a planet with fractal texture+heightmap

Edited by spacerat