计算机图形学·6 OpenGL编程3 谢尔宾斯基垫与三维编程

本文是记录专业课"计算机图形学"的部分笔记,参考教材为Angel的第八版交互式计算机图形学------基于WebGL 2.0的自顶向下方法。

1、在OpenGL中,二维是三维的一个特例,而三维其实变化不大,使用 glVertex3*( )即可。需要考虑的是多边形的绘制顺序、使用隐藏面消除技术以及简单性、凸性和平面性。

2、让我们先考虑二维的The Sierpinski Gasket(谢尔宾斯基垫):

算法如下图,核心是利用随机过程和迭代函数系统。具体流程是①在三角形内部任意选择一个初始点 p0​;②从三角形的三个顶点中随机选择一个;③找到当前点 p与上一步选中的顶点之间的中点q并绘制;④将当前点 p的位置更新为刚刚计算出的中点 q的位置(即 pn+1​=qn​),为下一次迭代做准备;⑤跳回第二步。

上述算法是A1 gasket 2D Sierpinski Gasket Using Randomly Selected Vertices,而对于A2 Sierpinski Gasket 2D ,先从一个三角形开始,连接三边的中点并去掉中间的三角形,再重复上述过程。对于A2的分形效果,我们考虑填充区域(黑色)和周长(围绕填充三角形的所有线的长度):随着我们继续细分,面积变为零但周长趋于无穷大,这不是一个普通的几何对象(既不是二维也不是三维)、而是一个分形(分形维数)对象(例如英格兰海岸线为1.02维)。

A2 Sierpinski Gasket 2D 示例程序:

复制代码
#include <GL/glut.h>

/* initial triangle */
GLfloat v[3][2]={{-1.0, -0.58}, {1.0, -0.58}, {0.0, 1.15}};
 
/* number of recursive steps递归次数*/
int n; // 全局变量 递归层次

//绘制三角形:
void triangle( GLfloat *a, GLfloat *b, GLfloat *c)
/* display one triangle */
{
 glVertex2fv(a); 
 glVertex2fv(b); 
 glVertex2fv(c);
}

//三角形细分:
void divide_triangle(GLfloat *a, GLfloat *b, GLfloat *c, int m)
{
/* triangle subdivision using vertex numbers */
 point2 v0, v1, v2;
 int j;
 if(m>0) {
 for(j=0; j<2; j++) v0[j]=(a[j]+b[j])/2;
 for(j=0; j<2; j++) v1[j]=(a[j]+c[j])/2;
 for(j=0; j<2; j++) v2[j]=(b[j]+c[j])/2;
 divide_triangle(a, v0, v1, m-1);
 divide_triangle(c, v1, v2, m-1);
 divide_triangle(b, v2, v0, m-1);
 }
 else
 triangle(a,b,c);
/* draw triangle at end of recursion */
}

//显示与初始化函数:
void display()
{
 glClear(GL_COLOR_BUFFER_BIT);
 glBegin(GL_TRIANGLES);
 divide_triangle(v[0], v[1], v[2], n); // 全局参变量 n
 glEnd();
 glFlush();
}

void myinit()
{
 glMatrixMode(GL_PROJECTION);
 glLoadIdentity();
 gluOrtho2D(-2.0, 2.0, -2.0, 2.0);
 glMatrixMode(GL_MODELVIEW);
 glClearColor (1.0, 1.0, 1.0,1.0); //背景白色,屏幕
 glColor3f(0.0,0.0,0.0); //前景黑色,三角形
}

//主函数:
int main(int argc, char **argv)
{
 n=4; //程序指定,还可以用户输入,交互界面 或者 命令行
 
 glutInit(&argc, argv); // 初始化GLUT 
 glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
 glutInitWindowSize(500, 500);
 glutCreateWindow("2D Gasket");
 glutDisplayFunc(display);
 myinit();
 glutMainLoop();
}

通过将glBegin和glEnd放在显示回调中而不是在triangle函数中,并在glBegin中使用GL_TRIANGLES而不是GL_POLYGON,我们只需为整个垫片调用一次glBegin和glEnd,而不是为所有三角形都调用一次。不过也可以用glDrawArrays(GL_TRIANGLES, 0, Nvertices);

3、而对于三维垫,可以使用三维点(GLfloat v[3][3]、glVertex3f、glOrtho)的方式,但不好。因此我们采用四面体(tetrahedron)方法。

复制代码
//三角形代码:
void triangle( GLfloat *a, GLfloat *b, 
GLfloat *c)
{
 glVertex3fv(a);
 glVertex3fv(b);
 glVertex3fv(c);
}

void divide_triangle(GLfloat *a, GLfloat *b, GLfloat *c, int m)
{
 GLfloat v1[3], v2[3], v3[3];
 int j;
 if(m>0)
 {
 for(j=0; j<3; j++) v1[j]=(a[j]+b[j])/2;
 for(j=0; j<3; j++) v2[j]=(a[j]+c[j])/2;
 for(j=0; j<3; j++) v3[j]=(b[j]+c[j])/2;
 divide_triangle(a, v1, v2, m-1);
 divide_triangle(c, v2, v3, m-1);
 divide_triangle(b, v3, v1, m-1);
 }
 else(triangle(a,b,c));
}

//生成四面体:
void tetrahedron(int m)
{
 glColor3f(1.0,0.0,0.0);
 divide_triangle(v[0], v[1], v[2], m);
 glColor3f(0.0,1.0,0.0);
 divide_triangle(v[3], v[2], v[1], m);
 glColor3f(0.0,0.0,1.0);
 divide_triangle(v[0], v[3], v[1], m);
 glColor3f(0.0,0.0,0.0);
 divide_triangle(v[0], v[2], v[3], m);
}

4、不过在三维中,由于三角形是按照程序中的定义顺序绘制的,因此前面的三角形并不总是渲染在它们后面的三角形前面,这涉及了遮挡/消隐问题:

我们只想看到位于其他表面之前的那些表面,OpenGL采用z缓冲算法,使用了一个硬件支持的额外缓冲区,在渲染对象时保存深度信息,以便图像中只显示前面的对象。使用时需要①在main.c中给绘制模式加上z缓冲区 glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);②在init.c中启动深度测试状态 glEnable(GL_DEPTH_TEST);③清屏时候同时清理深度缓存glClear(GL_COLOR_BUFFER_BIT | //当前可写的颜色缓存 GL_DEPTH_BUFFER_BIT)//将所有像素的深度值设置为最大可能距离(一般是远剪裁面)。

5、另外,在上述示例中,我们对每个物体进行了面分解,实际上体分解也是可以的:

相关推荐
千金裘换酒34 分钟前
LeetCode 移动零元素 快慢指针
算法·leetcode·职场和发展
北辰alk41 分钟前
RAG索引流程详解:如何高效解析文档构建知识库
人工智能
九河云44 分钟前
海上风电“AI偏航对风”:把发电量提升2.1%,单台年增30万度
大数据·人工智能·数字化转型
wm10431 小时前
机器学习第二讲 KNN算法
人工智能·算法·机器学习
NAGNIP1 小时前
一文搞懂机器学习线性代数基础知识!
算法
NAGNIP1 小时前
机器学习入门概述一览
算法
沈询-阿里1 小时前
Skills vs MCP:竞合关系还是互补?深入解析Function Calling、MCP和Skills的本质差异
人工智能·ai·agent·ai编程
xiaobai1781 小时前
测试工程师入门AI技术 - 前序:跨越焦虑,从优势出发开启学习之旅
人工智能·学习
盛世宏博北京1 小时前
云边协同・跨系统联动:智慧档案馆建设与功能落地
大数据·人工智能
iuu_star2 小时前
C语言数据结构-顺序查找、折半查找
c语言·数据结构·算法