绘制一个规则的二十面体,通过多边形近似模拟法来构建表面。

在绘制前,我们需要初始化一些数据:

#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 .850650808352039932GLfloat 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二十面体及多次细分成球体相关推荐

  1. OpenGL 地形LOD的镶嵌细分的用法

    OpenGL 地形LOD的镶嵌细分的用法 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <GLXW/glxw.h> #include ...

  2. OpenGL进阶(四)-用参数方程绘制椭球体

    首先参考这篇文章绘制一个球体:OpenGL 用参数方程绘制球 我们知道球体的参数方程是这样的: x=r·sin(α)·cos(β) y=r·sin(α)·sin(β) z=r·cos(α) 椭圆的参数 ...

  3. Qt OpenGL(07)递归细分四面体法绘制球体

    文章目录 Qt OpenGL通过递归细分逼近球面 思路 下面就是绘制的代码: Widget.cpp 顶点着色器 片段着色器 Qt OpenGL通过递归细分逼近球面 在OpenGL中绘制球面,不是太简单 ...

  4. 技美知识学习3300:TESS and GS

    学习教程来自:[技术美术百人计划]图形 3.3 曲面细分与几何着色器 大规模草渲染 Unity版本:Unity 2019.3.15f1 感谢老师的传道授业解惑,本篇用于总结复习和实践 1. 知识基础 ...

  5. android opengl把经纬度点用红色点显示在3d地球上,android OpenGL ES 地球仪绘制——球体绘制及纹理映射——源码...

    [实例简介] 支持如下: (1)opengl es绘制三角形拼成球体 (2)图片作为纹理映射到整个球面上 (3)双点触控缩放球体 (4)拖动旋转球体 [实例截图] [核心代码] OpenGLESTut ...

  6. [OpenGL] 曲面细分特性实践

    参考资料: https://www.nvidia.cn/object/tessellation_cn.html https://www.opengl.org/wiki/Tessellation 背景 ...

  7. OpenGL深入探索——曲面细分

    转载自:曲面细分[对翻译欠妥的地方进行了修正,增加了程序运行结果图,并进行补充说明] 原文链接:英文原文​​​​​ 背景 曲面细分( Tessellation )是 OpenGL4.x 中的一个令人兴 ...

  8. OpenGL如何画球体?

    在 OpenGL 中,可以使用三角形带来画球体. 球体可以由许多三角形组成,而每个三角形都可以由三个顶点构成.为了画出一个精细的球体,需要使用很多三角形,这样才能够使球体看起来光滑. 要画出一个球体, ...

  9. OpenGL原理介绍

    1. OpenGL简介 OpenGL是Open Graphics Library的缩写[2],是个定义了一个跨编程语言.跨平台的编程接口的标准,显卡通常有OpenGL的实现,不同显卡上的OpenGL实 ...

  10. OpenGL基本图元

    任何复杂的三维模型都是由基本的几何图元:点.线段和多边形组成的,有了这些图元,就可以建立比较复杂的模型.因此这部分内容是学习OpenGL编程的基础. 一.基本图元的描述及定义 OpenGL图元是抽象的 ...

最新文章

  1. (原创)VS2017 C# 运行 Javasrcipt RSA 加密用户名登录 Java开发的服务器
  2. 如何使java中double类型不以科学计数法表示
  3. java安装找不到uri,【找不到与请求 URI匹配的 HTTP 资源】(转)
  4. OpenShift 4 - 用KubeletConfig和ContainerRuntimeConfig分别修改集群节点的Kubelet和cri-o的配置
  5. 【Flutter】Dart的方法中的可选参数、方法作为参数传递
  6. 巴宝莉(Burberry)换等线体LOGO了!
  7. 麦肯锡卓越工作方法 读书体会
  8. 风力发电仿真系列-基于Simulink搭建的变速恒频双馈风力发电模型
  9. Error:algorithms should be set错误
  10. cad重新加载php命令,cad刷新命令是什么?
  11. 关于weight decay
  12. 如何在Word2010中设置不一样的稿纸
  13. c语言常用延时程序,新手常用单片机延时程序
  14. emlog mysql 设置_EMLOG模板自定义首页
  15. Python练习:简单的登陆注册的信息管理;模块化;密码安全判断(没有用数据库和文件)
  16. 背景与字体的搭配经验
  17. 彻底解决Android系统A-GPS搜不到卫星的问题!亲测!
  18. 【Nginx】冰河又一本超硬核Nginx PDF教程免费开源!!
  19. SuperData:2016年VR行业总产值为27亿美元,表现不如预期
  20. ibm服务器报错代码大全_IBM_x系列服务器报错代码

热门文章

  1. 多电压等级计算机潮流计算,电力系统稳态分析教学心得
  2. 淘宝与拍拍的世纪之战!(庄帅)
  3. 【树莓派 + 深度学习 + Python】从零开始做一个你画AI猜的小游戏
  4. 小米10手机电路图 主板元件位号图
  5. linux使用ftp传递文件,在linux下使用FTP命令传输文件
  6. 【UAV】光流传感器原理分析及讲解
  7. 最优化算法之粒子群算法(PSO)
  8. 新概念英语第三册 阅读
  9. HTML5框架 iframe用法 实现嵌套 好玩用法
  10. 南京大学俞扬博士:强化学习前沿(下)