如何使用多边形在 opengl 中对椭圆体建模
How to model ellipsoid in opengl using polygons
我想使用三角形和细分来模拟椭圆体。
下面的代码,引用自 OpenGL Programming guide,models sphere 但我不知道如何修改它以模拟 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();
}
只要椭球轴对齐,并不比球体难多少。您拥有的代码计算单位球体上的顶点。对于半径为 r
的球体,将这些单位球体点 (vx, vy, vz)
乘以半径:
sx = r * vx
sy = r * vy
sz = r * vz
椭球是一种概括,3个坐标方向的半径可以不同。使用 3 个半径 rx
、ry
和 rz
,然后计算点为:
sx = rx * vx
sy = ry * vy
sz = rz * vz
使用法线会稍微有趣一些。球体具有方便的 属性 位置和法线向量相同。这不适用于椭圆体。对于法线,您必须除以相应的半径(请参阅 了解数学背景)。所以椭圆体的法线计算为:
nx = vx / rx
ny = vy / ry
nz = vz / rz
要以最少的更改将其纳入您的代码,您可以将 drawtriangle()
函数更改为:
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();
通过这些计算,法向量通常不再被归一化。您可以通过将此调用添加到您的初始化代码来要求 OpenGL 为您规范化它们:
glEnable(GL_NORMALIZE);
如果您完全关心性能,那么每次要渲染球体时都计算点数将会非常低效。您需要计算一次,然后将它们存储起来以供渲染。当你这样做时,你可以将它们存储在顶点缓冲区中,并摆脱即时模式渲染。
我想使用三角形和细分来模拟椭圆体。 下面的代码,引用自 OpenGL Programming guide,models sphere 但我不知道如何修改它以模拟 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();
}
只要椭球轴对齐,并不比球体难多少。您拥有的代码计算单位球体上的顶点。对于半径为 r
的球体,将这些单位球体点 (vx, vy, vz)
乘以半径:
sx = r * vx
sy = r * vy
sz = r * vz
椭球是一种概括,3个坐标方向的半径可以不同。使用 3 个半径 rx
、ry
和 rz
,然后计算点为:
sx = rx * vx
sy = ry * vy
sz = rz * vz
使用法线会稍微有趣一些。球体具有方便的 属性 位置和法线向量相同。这不适用于椭圆体。对于法线,您必须除以相应的半径(请参阅
nx = vx / rx
ny = vy / ry
nz = vz / rz
要以最少的更改将其纳入您的代码,您可以将 drawtriangle()
函数更改为:
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();
通过这些计算,法向量通常不再被归一化。您可以通过将此调用添加到您的初始化代码来要求 OpenGL 为您规范化它们:
glEnable(GL_NORMALIZE);
如果您完全关心性能,那么每次要渲染球体时都计算点数将会非常低效。您需要计算一次,然后将它们存储起来以供渲染。当你这样做时,你可以将它们存储在顶点缓冲区中,并摆脱即时模式渲染。