tupx

 

这里从OpenGL的角度来谈绘制花托的方法。

--写在前面的话

首先看一组花托的实例:

图1 花托的一个实例正面图

图2 花托的一个实例侧面图

  既然选择手工绘制,必须进行数学分析了。首先要确定总体的设计算法:按照微积分的数学思想,我们可以选择一个“通用”的截面进行分析,这里很明显就是下图标记为红色的圆,然后让它绕原点旋转360°,就形成了花托(这是老外的说法,说成轮胎也无妨)。

  为了方便描述,我们做些规定(可能与之前的“内圆”“外圆”的说法不同,但后面具体实现绘制花托时,就会看到它的方便了):

  圆心O1和O2的距离,我们称之为大半径,用R表示,对应的圆称之为“大圆”;

  以O2为圆心的圆的半径,我们称之为小半径,用r表示,对应的圆称之为“小圆”。

也就是说,“大圆”是我们正面看到的边界在花托中线的圆,“小圆”是我们侧面看到的一个截面。

  从设计程序角度来说,绘制这个花托需要2层循环:外循环负责旋转“大圆”内的角度值,内循环负责旋转“小圆”内的角度值。“大圆”每旋转一个角度步长值,“小圆”要完成一圈的旋转。更进一步,我们在“大圆”上采集80个点(实际上每个点都是一个“小圆”的圆心),在“小圆”上采集40个点(GLint numMajor=80; GLint numMinor=40;)。

  现在,我们来考虑在“小圆”上采集的这些点的坐标,这里使用俯瞰视图分析比较方便(实际上,下图的上下方向分别代表z轴的负正方向),至于圆截面上x轴的选取,我们选择大圆半径方向的延长线所在的直线为x轴----这样选取的好处在于,这条直线在xy平面上(关键就是这点),“小圆”边界上的点只要投影到这条直线上,再根据“大圆”内的旋转角度,就可以计算出“小圆”边界上的点在世界坐标系中的x和y坐标,而“小圆”边界上的点距离这条直线的距离其实就是世界坐标系中的z坐标值

图4 俯瞰下的一个截面

而如果如下图选取x轴就会非常不方便:

图5 圆截面上不好的x轴选取方法(本地坐标系)

看着图4结合自己的三维空间想象力,我们可以得到“小圆”上的采样点的坐标:

 

说到这里,核心部分就完了。接下来,只要外层循环----“大圆”完成角度的360°旋转,一个最简易版的边框花托就绘制成了(代码见附录1):

图6 正面视图

图7 侧面视图

  为了实现更好的效果,我们可以利用一个小技巧:使用三角带(GL_TRIANGLE_STRIP),相应地,程序实现上需要修改的地方:在外层循环中要一次采样2个点(代码见附录2)。

图8 使用三角带后的正面视图

图9 使用三角带后的侧面视图

附录1:使用GL_LINE_LOOP绘制轮胎
#include "StdAfx.h"

#define FREEGLUT_STATIC
#include <GLTools.h>
#include <math3d.h>
#include <math.h>
#include <GL/glut.h>

static GLfloat yRot=0.0f;
GLint nNumMajor=80,nNumMinor=40;//采样点的数目
GLfloat fStepMajor=2*M3D_PI/nNumMajor;//大圆旋转步长
GLfloat fStepMinor=2*M3D_PI/nNumMinor;
GLfloat fAngleMajor=0.0f;//大圆的旋转角度
GLfloat fAngleMinor=0.0f;
GLfloat x=0.0f,y=0.0f,z=0.0f;//空间一个顶点的坐标
GLfloat R=5.0f,r=2.0f;//大圆 小圆的半径
void SetupRC()
{
	glClearColor(0.0f,0.0f,1.0f,1.0f);//blue background color
	glColor3f(1.0f,1.0f,1.f);//white pen color
	glEnable(GL_DEPTH_TEST);//开启深度测试
	glEnable(GL_SMOOTH);
	//glPolygonMode(GL_FORNT_AND_BACK,GL_LINE);
}

void RenderScene()
{
	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);//清除颜色 深度缓冲区
	glPushMatrix();

	glTranslatef(0.0f,0.0f,-25.0f);//使球体往屏幕里面平移
	glRotatef(yRot,0.0f,1.0f,0.0f);//旋转

	for(fAngleMajor=0.0f; fAngleMajor<=2*M3D_PI; fAngleMajor+=fStepMajor)
	{
		glBegin(GL_LINE_LOOP);
		for(fAngleMinor=0.0f; fAngleMinor<=2*M3D_PI; fAngleMinor+=fStepMinor)
		{
			x=(r*cos(fAngleMinor)+R)*cos(fAngleMajor);
			y=(r*cos(fAngleMinor)+R)*sin(fAngleMajor);
			z=r*sin(fAngleMinor);

			glVertex3f(x,y,z);
		}
		glEnd();
	}

	glPopMatrix();
	glutSwapBuffers();
}

void ChangeSize(int w,int h)
{
	GLfloat fAspect=(float)w/(float)h;
	if(h==0)
		h=1;
	glViewport(0,0,w,h);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(45.0,fAspect,1.0,100.0);//"glu-"  glOrtho

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	glutPostRedisplay();
}

void TimerFunc(int value)
{
	yRot+=2.0f;
	glutPostRedisplay();
	glutTimerFunc(100,TimerFunc,1);
}

int main(int argc,char** argv)
{
	glutInit(&argc,argv);
	glutInitWindowSize(800,600);
	glutCreateWindow("Drawing a Torus");

	glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH);
	glutDisplayFunc(RenderScene);
	glutReshapeFunc(ChangeSize);
	glutTimerFunc(100,TimerFunc,1);

	SetupRC();

	glutMainLoop();
	return 0;
}
附录2:使用GL_TRIANGLE_STRIP绘制轮胎
#include "StdAfx.h"

#define FREEGLUT_STATIC
#include <GLTools.h>
#include <math3d.h>
#include <math.h>
#include <GL/glut.h>

static GLfloat yRot=60.0f;
GLint nNumMajor=80,nNumMinor=40;//采样点的数目
GLfloat fStepMajor=2*M3D_PI/nNumMajor;//大圆旋转步长
GLfloat fStepMinor=2*M3D_PI/nNumMinor;
GLfloat fAngleMajor1=0.0f,fAngleMajor2=0.0f;//大圆的旋转角度
GLfloat fAngleMinor=0.0f;
GLfloat vx1=0.0f,vy1=0.0f,vz1=0.0f,vx2=0.0f,vy2=0.0f,vz2=0.0f;//空间三角带的2个坐标
GLfloat R=5.0f,r=2.0f;//大圆 小圆的半径
void SetupRC()
{
	glClearColor(0.0f,0.0f,1.0f,1.0f);//blue background color
	glColor3f(1.0f,1.0f,1.f);//white pen color
	glEnable(GL_DEPTH_TEST);//开启深度测试
	glEnable(GL_SMOOTH);
	glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
}

void RenderScene()
{
	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);//清除颜色 深度缓冲区
	glPushMatrix();

	glTranslatef(0.0f,0.0f,-25.0f);
	glRotatef(yRot,0.0f,1.0f,0.0f);//旋转

	for(fAngleMajor1=0.0f; fAngleMajor1<=2*M3D_PI; fAngleMajor1+=fStepMajor)
	{
		fAngleMajor2=fAngleMajor1+fStepMajor;//大圆下一个采样点
		glBegin(GL_TRIANGLE_STRIP);
		for(fAngleMinor=0.0f; fAngleMinor<=2*M3D_PI+fStepMinor; fAngleMinor+=fStepMinor)//上限值2PI+fStepMinor否则会有一个裂痕
		{
			vx1=(r*cos(fAngleMinor)+R)*cos(fAngleMajor1);
			vy1=(r*cos(fAngleMinor)+R)*sin(fAngleMajor1);
			vz1=r*sin(fAngleMinor);
			vx2=(r*cos(fAngleMinor)+R)*cos(fAngleMajor2);
			vy2=(r*cos(fAngleMinor)+R)*sin(fAngleMajor2);
			vz2=r*sin(fAngleMinor);
			glVertex3f(vx1,vy1,vz1);
			glVertex3f(vx2,vy2,vz2);
		}
		glEnd();
	}

	glPopMatrix();
	glutSwapBuffers();
}

void ChangeSize(int w,int h)
{
	GLfloat fAspect=(float)w/(float)h;
	if(h==0)
		h=1;
	glViewport(0,0,w,h);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(45.0,fAspect,1.0,100.0);//"glu-"  glOrtho

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	glutPostRedisplay();
}

void TimerFunc(int value)
{
	//yRot+=2.0f;
	glutPostRedisplay();
	glutTimerFunc(100,TimerFunc,1);
}

int main(int argc,char** argv)
{
	glutInit(&argc,argv);
	glutInitWindowSize(800,600);
	glutCreateWindow("Drawing a Torus");

	glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH);
	glutDisplayFunc(RenderScene);
	glutReshapeFunc(ChangeSize);
	glutTimerFunc(100,TimerFunc,1);

	SetupRC();

	glutMainLoop();
	return 0;
}

  

分类:

技术点:

相关文章: