计算机图形学·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、另外,在上述示例中,我们对每个物体进行了面分解,实际上体分解也是可以的:

相关推荐
高山上有一只小老虎7 小时前
求最大连续bit数
java·算法
落羽的落羽7 小时前
【Linux系统】C/C++的调试器gdb/cgdb,从入门到精通
linux·服务器·c语言·c++·人工智能·学习·机器学习
磊磊落落7 小时前
以自然语言的方式使用 Playwright MCP 进行浏览器自动化操作
人工智能·ai编程
电商API_180079052477 小时前
从客户需求到 API 落地:淘宝商品详情批量爬取与接口封装实践
大数据·人工智能·爬虫·数据挖掘
Dylan的码园7 小时前
以二叉树问题为基础的递归调试学习(上)
java·学习·算法·leetcode·r-tree
许泽宇的技术分享7 小时前
NOFX AI量化交易系统 - 完整使用手册
人工智能
亚马逊云开发者7 小时前
Agentic AI基础设施实践经验系列(二):专用沙盒环境的必要性与实践方案
人工智能
少许极端7 小时前
算法奇妙屋(九)-栈
java·数据结构·算法·
rengang667 小时前
01-深度学习概述:介绍深度学习的基本概念和发展背景
人工智能·深度学习