计算机图形学--三维图形表面绘制

目录

三维立体图形绘制基本思想

球体绘制思想

代码实现:

圆环体绘制思想

代码实现:

正棱柱体绘制思想

正棱台体以及正棱锥体绘制思想

正棱台体绘制代码实现:


三维立体图形绘制基本思想

        对于三维图形的立体效果绘制过程中,我们采取利用四边形绘制图形表面的细分分块的方式进行表面的模拟。
        在对三维图形的展示过程中,往往采用线框图或实心图的模式进行,但是这两种绘制方式的本质都是前面说到的表面模拟的方法,没有太大差别,可以认为算法过程相差不大。

        下文,我将对三维基本图形球形、圆环体、棱柱体、棱台体、棱锥体的绘制思路以及部分代码进行介绍。(我们以模型坐标系下的坐标以及绘制进行介绍,涉及的代码部分绘制为基于OpenGL的线框图绘制,要绘制表面填充图仅需简单改动即可)

球体绘制思想

       对于球体的绘制,我们可以仿地球经纬表示的方式进行表面模拟。对于经纬线,我们的基本假设有:1.经线数量位偶数;2经线均匀分布,纬线均匀分布,两者数量没有关联。在表面模拟的过程中,我们以对经线的遍历作为外层循环,在这个循环体内部对纬线进行遍历,对当前经线、当前纬线和前一经线、前一纬线组成的四边形进行绘制(如下图四边形HESQ)。完成这个过程后就可以实现球体表面的绘制。

       在处理过程中,值得注意的问题主要有:
        1.对经线的遍历过程在上图中可以表示为对∠NAW的增量变化过程,对纬线的遍历过程可以看作是对∠CAH的增量变化过程。
        2.内层循环应当遍历2*纬线数量次,纬线对应角度phi应当增量应当为180/纬线数量,这与经线的不同,因为经线本质是在360°范围内进行等分的结果,但纬线是对180范围进行等分,所以在遍历360°时自然要遍历两次。

代码实现:

void wireSphere(double rsdius, int mSlices, int mStacks)
{
	double sliceDelta = 1.0 * PI / mSlices;
	double stackDelta = 2.0 * PI / mStacks;
	double theta = 0;
	double phi;
	glColor3f(1.00f, 0.0f, 0.0f);
	for (int i = 0; i < mStacks; i++)
	{
		double theta1 = theta + stackDelta;
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		glBegin(GL_QUAD_STRIP);
		phi = 0;
		for (int j = 0; j <= 2 * mSlices; j++)
		{
			phi = phi + sliceDelta;
			glNormal3f(cos(theta) * cos(phi), sin(theta) * cos(phi), sin(phi));
			glVertex3f(cos(theta) * radius * cos(phi), sin(theta) * radius * cos(phi), radius * sin(phi));
			glNormal3f(cos(theta1) * cos(phi), sin(theta1) * cos(phi), sin(phi));
			glVertex3f(cos(theta1) * radius * cos(phi), sin(theta1) * radius * cos(phi), radius * sin(phi));
		}
		glEnd();
		theta = theta1;
	}
}

圆环体绘制思想

       对于圆环体,设计外层遍历圆环沿z轴方向解得的每一个圆,内层循环遍历垂直z轴方向的划分,记为l,对于每一条划分线,对当前l与当前圆交点、当前l与上一圆交点和上一l与当前圆交点、上一l与上一圆交点组成的四边形进行绘制(如下图BCDE)。完成上述过程即完成了对圆环表面的模拟。

       对于交点的求解,我们可以根据圆环中心以及沿z方向划分的角度首先求得当前圆/上一圆的圆心位置,然后根据当前l/上以l相对圆心的所在平面坐标系的角度进行简单计算得到。上述两个角度均可通过增量计算获得。

代码实现:

void wireTorus(double innerRadius, double outerRadius, int nsides, int nrings)
{
	double sideDelta = 2.0 * PI / nsides;
	double ringDelta = 2.0 * PI / nrings;
	double theta = 0;
	double phi;
	double dist;
	glColor3f(1.00f, 0.0f, 0.0f);
	for (int i = 0; i < nrings; i++)
	{
		double theta1 = theta + ringDelta;
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		glBegin(GL_QUAD_STRIP);
		phi = 0;
		for (int j = 0; j <= nsides; j++)
		{
			phi = phi + sideDelta;
			dist = outerRadius + (innerRadius * cos(phi));
			glNormal3f(cos(theta) * cos(phi), sin(theta) * cos(phi), sin(phi));
			glVertex3f(cos(theta) * dist, sin(theta) * dist, innerRadius * sin(phi));
			glNormal3f(cos(theta1) * cos(phi), sin(theta1) * cos(phi), sin(phi));
			glVertex3f(cos(theta1) * dist, sin(theta1) * dist, innerRadius * sin(phi));
		}
		glEnd();
		theta = theta1;
	}
}

正棱柱体绘制思想

       首先对于棱柱体而言,我们的绘制思路仍是参考球体绘制思路,对小四边形的获取与绘制。将底面顶点数类比球体经线的概念,将侧面切分数类比球体纬线的概念,进行外层经线遍历,内层纬线遍历,取新旧纬线以及新旧经线形成的四个交点绘制四边形的方式,完成对正棱柱体的绘制。

正棱台体以及正棱锥体绘制思想

       对于正棱台体/正棱锥体的绘制,过程大体与正棱柱体类似,但需要注意的点在于,每一层“纬线”截地的面的外接圆半径发生了变化。对于这种变化是线性的,变化步长可以通过底面半径/棱锥高度(棱台体为补全后高度)求得。

正棱台体绘制代码实现:

void wireFrustum(double radius, double high, double high_all, int mSlices, int mStacks)
{
	double zstep = high / mStacks;
	double rstep = -radius * zstep / high_all;
	double stackDelta = 2 * PI / mSlices;
	double theta = 0;
	double h_now;
	double r_now;
	glColor3f(1.00f, 0.0f, 0.0f);
	//绘制底面
	glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	glBegin(GL_POLYGON);
	for (int i = 0; i < mSlices; i++)
	{
		glNormal3f(0, 0, 1);
		glVertex3f(cos(theta) * radius, sin(theta) * radius, 0);
		theta += stackDelta;
	}
	glEnd();
	//绘制顶面
	glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	glBegin(GL_POLYGON);
	r_now = radius * (high_all - high) / high_all;
	theta = 0;
	for (int i = 0; i < mSlices; i++)
	{
		glNormal3f(0, 0, 1);
		glVertex3f(cos(theta) * r_now, sin(theta) * r_now, high);
		theta += stackDelta;
	}
	glEnd();
	//绘制棱台侧面
	for (int i = 0; i < mSlices; i++)
	{
		double theta1 = theta + stackDelta;
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		glBegin(GL_QUAD_STRIP);
		h_now = 0, r_now=radius;
		for (int j = 0; j <= mStacks; j++)
		{
			glVertex3f(cos(theta) * r_now, sin(theta) * r_now, h_now);
			glVertex3f(cos(theta1) * r_now, sin(theta1) * r_now, h_now);
			h_now += zstep; r_now += rstep;
		}
		glEnd();
		theta = theta1;
	}
}
  • 0
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值