标签:opengl opengl二十面体 光照 法线向量 多次细分
绘制一个规则的二十面体,通过多边形近似模拟法来构建表面。
在绘制前,我们需要初始化一些数据:
#define X .525731112119133606 //使原点到每个顶点的距离均为1.0 #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} };
然后再进行绘制:
glBegin(GL_TRIANGLES); for (int i = 0; i < 20; i++) { glVertex3fv(&vdata[tindices[i][0]][0]); glVertex3fv(&vdata[tindices[i][1]][0]); glVertex3fv(&vdata[tindices[i][2]][0]); } glEnd();
但是光光是这样,是看不出它是个什么鬼。
但是可以通过调用glPolygonMode函数,来设置多边形的显示模式。现在把它设置为GL_LINE模式:
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); //多边形的显示方式
然后我们看下效果:
现在能看出点什么了把?
现在,我想让这货看起来更加有立体感,我要给它添加一个光照(先取消GL_LINE模式)。在添加光照前,需要计算一下它的一些法线向量。计算方法,我参照了OpenGL编程指南上的代码:
GLfloat d1[3], d2[3], norm[3];//这一段代码是生成表面的法线向量的 for (int j = 0; j < 3; j++) { d1[j] = vdata[tindices[i][0]][j] - vdata[tindices[i][1]][j]; d2[j] = vdata[tindices[i][1]][j] - vdata[tindices[i][2]][j]; } normcrossprod(d1, d2, norm); glNormal3fv(norm); void normalize(float v[3]) { GLfloat d = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);//三个向量的模 if (0 == d) { printf("zero length vector!"); return; } //单位向量 v[0] /= d; v[1] /= d; v[2] /= d; } void normcrossprod(float v1[3], float v2[3], float out[3]) { out[0] = v1[1] * v2[2] - v1[2] * v1[1]; out[1] = v1[2] * v2[0] - v1[0] * v1[2]; out[2] = v1[0] * v2[1] - v1[1] * v1[0]; normalize(out); }
法线弄好了,那么就来添加一下光照,首先,需要一些初始化数据:
GLfloat LightPosition[] = {5.0f, 5.0f, 0.0f, 1.0f}; //光源位置 GLfloat LightAmbient[] = {0.5f, 0.5f, 0.5f, 1.0f}; //环境光参数 GLfloat LightDiffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f }; //漫射光参数
参数可以根据自己需求设定,上面是白光。
然后进行光源的设置:
glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient); //设置环境光 glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse); //设置漫射光 glLightfv(GL_LIGHT1, GL_POSITION, LightPosition); //设置光源位置 glEnable(GL_LIGHT1); //启用1号光源 glEnable(GL_LIGHTING); //开启光源
OK,这些设置好后,把上面绘制那部分修改一下就可以看到一个比较不错的二十面体了。修改如下:
glBegin(GL_TRIANGLES); glNormal3fv(&vdata[tindices[i][0]][0]); glVertex3fv(&vdata[tindices[i][0]][0]); glNormal3fv(&vdata[tindices[i][1]][0]); glVertex3fv(&vdata[tindices[i][1]][0]); glNormal3fv(&vdata[tindices[i][2]][0]); glVertex3fv(&vdata[tindices[i][2]][0]); glEnd();
看下效果:
有时候,我们需要绘制一个球体,那么就可以通过这个二十面体进行绘制,只不过需要做一些额外的事情。
我们可以想象,这个二十面体,我可以在一个球体上进行切割得到,同样,如果我切的多一点,细一点,那是不是很接近球体,那么,也就是说,我们可以通过这个二十面体进行多次细分,这样,就可以得到一个球体。
下面是细分的代码:
void subdivide(float *v1, float *v2, float *v3) //非递归的单次细分 { GLfloat v12[3], v23[3], v31[3]; GLint i; 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); drawtriangle( v1, v12, v31); drawtriangle( v2, v23, v12); drawtriangle( v3, v31, v23); drawtriangle(v12, v23, v31); } void subdivide(float *v1, float *v2, float *v3, long depth) //递归的depth次细分 { GLfloat v12[3], v23[3], v31[3]; GLint i; if (0 == depth) { 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); }
在上面代码中,看到了drawtriangle的函数,其代码如下:
void drawtriangle(float *v1, float *v2, float *v3) { glBegin(GL_TRIANGLES); glNormal3fv(v1); glVertex3fv(v1); glNormal3fv(v2); glVertex3fv(v2); glNormal3fv(v3); glVertex3fv(v3); glEnd(); }
然后,再修改绘制那一部分代码即可。修改如下:
subdivide(&vdata[tindices[i][0]][0], &vdata[tindices[i][1]][0], &vdata[tindices[i][2]][0], 0 //细分次数 );
OK,我们现在来看一下1次细分,2次细分。。。的一些效果:
可以看到,细分的次数越多,则越趋向于一个球体。
最后,我贴一下整个程序的源代码:
#include <GL/glut.h> #include <stdio.h> #include <stdlib.h> #include <math.h> #define X .525731112119133606 //使原点到每个顶点的距离均为1.0 #define Z .850650808352039932 GLfloat LightPosition[] = {5.0f, 5.0f, 0.0f, 1.0f}; //光源位置 GLfloat LightAmbient[] = {0.5f, 0.5f, 0.5f, 1.0f}; //环境光参数 GLfloat LightDiffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f }; //漫射光参数 GLfloat xrot; //绕轴旋转 GLfloat yrot; GLfloat zrot; int width; //窗口大小 int height; //顶点坐标 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} }; void normalize(float v[3]); //计算两个向量的规范化向量积 void normcrossprod(float v1[3], float v2[3], float out[3]); void drawtriangle(float *v1, float *v2, float *v3); //画三角形 void subdivide(float *v1, float *v2, float *v3); //单次划分三角形 void subdivide(float *v1, float *v2, float *v3, long depth); //递归求解,多次划分三角形(通过depth控制) void normalize(float v[3]) { GLfloat d = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);//三个向量的模 if (0 == d) { printf("zero length vector!"); return; } //单位向量 v[0] /= d; v[1] /= d; v[2] /= d; } void normcrossprod(float v1[3], float v2[3], float out[3]) { out[0] = v1[1] * v2[2] - v1[2] * v1[1]; out[1] = v1[2] * v2[0] - v1[0] * v1[2]; out[2] = v1[0] * v2[1] - v1[1] * v1[0]; normalize(out); } void drawtriangle(float *v1, float *v2, float *v3) { glBegin(GL_TRIANGLES); glNormal3fv(v1); glVertex3fv(v1); glNormal3fv(v2); glVertex3fv(v2); glNormal3fv(v3); glVertex3fv(v3); glEnd(); } void subdivide(float *v1, float *v2, float *v3) //非递归的单次细分 { GLfloat v12[3], v23[3], v31[3]; GLint i; 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); drawtriangle( v1, v12, v31); drawtriangle( v2, v23, v12); drawtriangle( v3, v31, v23); drawtriangle(v12, v23, v31); } void subdivide(float *v1, float *v2, float *v3, long depth) //递归的depth次细分 { GLfloat v12[3], v23[3], v31[3]; GLint i; if (0 == depth) { 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() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除颜色和深度缓存 glLoadIdentity(); //重置当前的模型观察矩阵 glTranslatef(0.0f, 0.0f, -5.0f); //移入屏幕5个单位 //glRotatef(yrot, 0.0f, 1.0f, 0.0f); //饶轴旋转 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); //多边形的显示方式 glColor3f(0.5f, 0.5f, 0.5f); //灰色 /*glBegin(GL_TRIANGLES); for (int i = 0; i < 20; i++) { glVertex3fv(&vdata[tindices[i][0]][0]); glVertex3fv(&vdata[tindices[i][1]][0]); glVertex3fv(&vdata[tindices[i][2]][0]); } glEnd();*/ for (int i = 0; i < 20; i++) { /* *生成表面的法线向量 */ GLfloat d1[3], d2[3], norm[3]; for (int j = 0; j < 3; j++) { d1[j] = vdata[tindices[i][0]][j] - vdata[tindices[i][1]][j]; d2[j] = vdata[tindices[i][1]][j] - vdata[tindices[i][2]][j]; } normcrossprod(d1, d2, norm); glNormal3fv(norm); /*drawtriangle(&vdata[tindices[i][0]][0], &vdata[tindices[i][1]][0], &vdata[tindices[i][2]][0]);*/ /*glBegin(GL_TRIANGLES); glNormal3fv(&vdata[tindices[i][0]][0]); glVertex3fv(&vdata[tindices[i][0]][0]); glNormal3fv(&vdata[tindices[i][1]][0]); glVertex3fv(&vdata[tindices[i][1]][0]); glNormal3fv(&vdata[tindices[i][2]][0]); glVertex3fv(&vdata[tindices[i][2]][0]); glEnd();*/ subdivide(&vdata[tindices[i][0]][0], &vdata[tindices[i][1]][0], &vdata[tindices[i][2]][0], 3 //划分次数 ); } yrot += 0.5f; glutPostRedisplay(); glFlush(); } void init() { glMatrixMode(GL_PROJECTION); //选择投影矩阵 glLoadIdentity(); //重置投影矩阵 gluPerspective(45.0f, (GLfloat)width / (GLfloat)height, 0.1f, 100.0f); //透视效果 glMatrixMode(GL_MODELVIEW); //选择模型观察矩阵 glLoadIdentity(); //重置模型观察矩阵 glShadeModel(GL_SMOOTH); //启用阴影平滑 glClearColor(0.0f, 0.0f, 0.0f, 0.0f); //设置背景颜色 glClearDepth(1.0f); //设置深度缓存 glEnable(GL_DEPTH_TEST); //启用深度测试 glDepthFunc(GL_LESS); //所作测试的类型 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); //透视修正 glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient); //设置环境光 glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse); //设置漫射光 glLightfv(GL_LIGHT1, GL_POSITION, LightPosition); //设置光源位置 glEnable(GL_LIGHT1); //启用1号光源 glEnable(GL_LIGHTING); //开启光源 } void reshape(int w, int h) //当窗体大小改变时调用 { width = w; height = h; glViewport(0, 0, (GLsizei)w, (GLsizei)h); //设置窗体的视口大小 init(); gluOrtho2D(0.0f, (GLdouble)w, 0.0, (GLdouble)h); //建立二维可视区域 } int main(int argv, char **argc) { glutInit(&argv, argc); //glut初始化 glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); //设置显示模式 glutInitWindowSize(300, 300); glutInitWindowPosition(300, 200); glutCreateWindow("二十面体"); init(); glutDisplayFunc(display); //测试绘图函数 glutReshapeFunc(reshape); //注册窗体大小改变函数 glutMainLoop(); //GLUT事件处理循环 return 0; }
To be continued~
标签:opengl opengl二十面体 光照 法线向量 多次细分
原文地址:http://blog.csdn.net/huai814586181/article/details/46273869