I want to model ellipsoid using triangles and subdivision. below code, referenced from OpenGL Programming guide, models sphere but I don't know how I can modify this to model ellipsoid
#define X .525731112119133606
#define Z .850650808352039932
static GLfloat vdata[12][3] = {
{ -X, 0.0, Z }, { X, 0.0, Z }, { -X, 0.0, -Z }, { X, 0.0, -Z },
{ 0.0, Z, X }, { 0.0, Z, -X }, { 0.0, -Z, X }, { 0.0, -Z, -X },
{ Z, X, 0.0 }, { -Z, X, 0.0 }, { Z, -X, 0.0 }, { -Z, -X, 0.0 }
};
static GLuint tindices[20][3] = {
{ 1, 4, 0 }, { 4, 9, 0 }, { 4, 5, 9 }, { 8, 5, 4 }, { 1, 8, 4 },
{ 1, 10, 8 }, { 10, 3, 8 }, { 8, 3, 5 }, { 3, 2, 5 }, { 3, 7, 2 },
{ 3, 10, 7 }, { 10, 6, 7 }, { 6, 11, 7 }, { 6, 0, 11 }, { 6, 1, 0 },
{ 10, 1, 6 }, { 11, 0, 9 }, { 2, 11, 9 }, { 5, 2, 9 }, { 11, 2, 7 },
};
//draws triangle at the specified coordinate
void drawtriangle(float *v1, float *v2, float *v3){
printf("v1 = %f, v3 = %f,v3 = %f\n", *v1, *v2, *v3);
glBegin(GL_TRIANGLES);
glNormal3fv(v1);
glVertex3fv(v1);
glNormal3fv(v2);
glVertex3fv(v2);
glNormal3fv(v3);
glVertex3fv(v3);
glEnd();
}
void normalize(float v[3]){
GLfloat d = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
if (d == 0.0){
printf("zero length vector\n");
return;
}
v[0] /= d;
v[1] /= d;
v[2] /= d;
}
void subdivide(float *v1, float *v2, float *v3, long depth){
GLfloat v12[3], v23[3], v31[3];
GLint i;
//end recursion
if (depth == 0){
drawtriangle(v1, v2, v3);
return;
}
for (i = 0; i < 3; i++){
v12[i] = (v1[i] + v2[i]) / 2.0;
v23[i] = (v2[i] + v3[i]) / 2.0;
v31[i] = (v3[i] + v1[i]) / 2.0;
}
normalize(v12);
normalize(v23);
normalize(v31);
subdivide(v1, v12, v31, depth - 1);
subdivide(v2, v23, v12, depth - 1);
subdivide(v3, v31, v23, depth - 1);
subdivide(v12, v23, v31, depth - 1);
}
void display(void){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glShadeModel(GL_FLAT);
glRotatef(300.0, 0.5, 1.0, 0.5);
for (int i = 0; i < 20; i++){
subdivide(&vdata[tindices[i][0]][0], &vdata[tindices[i][1]][0], &vdata[tindices[i][2]][0], 1);
}
glFlush();
}
As long as the ellipsoid is axis-aligned, it's not much more difficult than the sphere. The code you have calculates vertices on a unit sphere. For a sphere with radius r
, you multiply those unit sphere points (vx, vy, vz)
with the radius:
sx = r * vx
sy = r * vy
sz = r * vz
The ellipsoid is a generalization, where the radii in the 3 coordinate directions can be different. With the 3 radii rx
, ry
, and rz
, the points are then calculated as:
sx = rx * vx
sy = ry * vy
sz = rz * vz
It gets slightly more interesting with the normals. Spheres have the convenient property that the position and normal vectors are identical. This does not apply to ellipsoids. For the normals, you have to divide by the corresponding radius (see normal matrix for non uniform scaling for the mathematical background). So the normals for the ellipsoid are calculated as:
nx = vx / rx
ny = vy / ry
nz = vz / rz
To fit this into your code with minimal changes, you can change the drawtriangle()
function to:
glBegin(GL_TRIANGLES);
glNormal3f(v1[0] / rx, v1[1] / ry, v1[2] / rz);
glVertex3f(v1[0] * rx, v1[1] * ry, v1[2] * rz);
glNormal3f(v2[0] / rx, v2[1] / ry, v2[2] / rz);
glVertex3f(v2[0] * rx, v2[1] * ry, v2[2] * rz);
glNormal3f(v3[0] / rx, v3[1] / ry, v3[2] / rz);
glVertex3f(v3[0] * rx, v3[1] * ry, v3[2] * rz);
glEnd();
With these calculations, the normal vectors will generally not be normalized anymore. You can ask OpenGL to normalize them for you by adding this call to your initialization code:
glEnable(GL_NORMALIZE);
If you care about performance at all, calculating the points each time you want to render a sphere will be highly inefficient. You will want to calculate them once, and store them away for rendering. And while you're at it, you can store them in a vertex buffer, and get rid of the immediate mode rendering.