三角形是基本的多边形,任何多变形都能由三角形组成。三角形是由三个顶点的连线组成。三个点分别是v0;v1;v2。
从某个顶点开始,有两种连线的方法,顺时针和逆时针,这是两种不同的绕法。
三角形能拼出任何多边形平面或曲面。所以对于平面或曲面,我们只需要保存组成这个面的所有三角形的顶点以及拼法即可。例如某个面有n个三角形组成,我们需要保存3n个顶点。如果我们我比较好的方法,可以减少保存顶点的个数,那么将大大减少内存。以下两种方法能够实现我们的想法。
(1)三角条
如上图,该面由三个三角形组成,假设我们以逆时针为正方形的绕法。
绘制第一个三角形式,我们需要知道三个顶点v0;v1;v2;绘制第二个三角形是,由于应用到第一个三角形的两个顶点v1;v2;所以我们只需要知道(保存)一个顶点(v3)即可;同样的方法绘制第三个三角形。理想情况下,按这种方法,一个面如果由n个三角形拼接成,那么我们只需要保存n+2个顶点即可。
这种方法是以新绘制的三角形利用到前面最后一次绘制的三角形的最后两个顶点。
(2)三角扇形
这种方法跟上面的方法不同的地方是,这里的新绘制的三角形利用到前面已经绘制三角形的顶点是v0(初始顶点)和前面最后一次增加的顶点。如上图所示。
与第一种方法一样,这种方法在理想的情况向,需要保存的顶点个数为n+2。
3、使用三角扇形绘制一个锥体
(1)以锥体的顶点为原点,通过三角扇形的方法绘制锥体的侧面
a)设置以顺时针的方向为正方向:
glFrontFace(GL_CW);
b)设置以三角扇的绘制方法:
glBegin(GL_TRIANGLE_FAN);
c)绘制侧面
glBegin(GL_TRIANGLE_FAN);
glVertex3f(0.0f, 0.0f, 75.0f);
int ipvot = 0;
for(angle = 0.0f; angle < (2.0f * PI); angle += (PI / 8.0f))
{
x = 50.0f * sin(angle);
y = 50.0f * cos(angle);
if(ipvot % 2 == 0) glColor3f(0.0f, 1.0f, 0.0f);
else glColor3f(1.0f, 0.0f, 0.0f);
ipvot++;
glVertex3f(x, y, 0.0f);
}
glEnd();
d)绘制底面
glBegin(GL_TRIANGLE_FAN);
glVertex3f(0.0f, 0.0f, 0.0f);
for(angle = 0; angle < (2.0f * PI); angle += (PI / 8.0f))
{
x = 50.0f * sin(angle);
y = 50.0f * cos(angle);
if(ipvot % 2 == 0) glColor3f(0.0f, 1.0f, 0.0f);
else glColor3f(1.0f, 0.0f, 0.0f);
ipvot++;
glVertex3f(x, y, 0.0f);
}
glEnd();
这个图是以直代曲的方法,上面使用的是16个三角形去模拟侧面,当以100个或者更多个三角形去模拟侧面时,想过会更好,当然执行时间也需要花费更多。
3)通过注册上下左右按钮空时模型旋转
if(key == GLUT_KEY_UP)
xRot -= 5.0f;
else if(key == GLUT_KEY_DOWN)
xRot += 5.0f;
else if(key == GLUT_KEY_LEFT)
yRot -= 5.0f;
else if(key == GLUT_KEY_RIGHT)
yRot += 5.0f;
if(xRot < 0) xRot = 355.0f;
if(xRot > 360.0f) xRot = 0.0f;
if(yRot < 0) yRot = 355.0f;
if(yRot > 360.0f) yRot = 0.0f;
//改变模型角度后需要从新绘画到窗口上去
glutPostRedisplay();
源代码如下:
#include
#include
#include
//常量PI的值
const GLfloat PI = 3.1415f;
GLfloat xRot = 0.0f;
GLfloat yRot = 0.0f;
//再回调函数声明
void rendererScene(void);
void changeWindowSize(GLsizei w, GLsizei h);
void setupRC(void);
void rotateMode(int key, int x, int y);
//主函数
int main(int argc, char* argv[])
{
//设置显示模式
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
//设置窗口大小
glutInitWindowSize(300, 300);
//设置窗口在屏幕上的位置
glutInitWindowPosition(200, 200);
//创建窗口标题
glutCreateWindow("三角形绘3D模型");
//注册显示窗口时回调渲染函数
glutDisplayFunc(rendererScene);
//建立修剪空间的范围
glOrtho(-100.0f, 100.0f, -100.0f, 100.0f, -100.0f, 100.0f);
//注册窗口大小改变时回调函数
glutReshapeFunc(changeWindowSize);
//注册点击上下左右方向按钮时回调rotateMode函数
glutSpecialFunc(rotateMode);
//初始化
setupRC();
//消息循环(处理操作系统等的消息,例如键盘、鼠标事件等)
glutMainLoop();
return 0;
}
/**
渲染函数
*/
void rendererScene(void)
{
GLfloat x, y, angle;
glClear(GL_COLOR_BUFFER_BIT);
//入栈
glPushMatrix();
//x轴旋转函数
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
//y轴旋转函数
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
//设置以三角扇的绘制方法
glBegin(GL_TRIANGLE_FAN);
//顶点坐标
glVertex3f(0.0f, 0.0f, 75.0f);
int ipvot = 0;
//通过for循环来改变点的值完成绘制
for (angle = 0.0f; angle < (2.0f * PI); angle += (PI / 8.0f))
{
x = 50.0f * sin(angle);
y = 50.0f * cos(angle);
//能被2整除的画黄色
if (ipvot % 2 == 0) glColor3f(1.0f, 1.0f, 0.0f); //否则画红色
else glColor3f(1.0f, 0.0f, 0.0f);
ipvot++;
glVertex3f(x, y, 0.0f);
}
glEnd();
//画圆锥的底面
glBegin(GL_TRIANGLE_FAN);
glVertex3f(0.0f, 0.0f, 0.0f);
//通过for循环来改变点的值完成绘制
for (angle = 0; angle < (2.0f * PI); angle += (PI / 8.0f))
{
x = 50.0f * sin(angle);
y = 50.0f * cos(angle);
//能被2整除的画绿色
if (ipvot % 2 == 0) glColor3f(0.0f, 1.0f, 0.0f); //否则画红色
else glColor3f(1.0f, 0.0f, 0.0f);
ipvot++;
glVertex3f(x, y, 0.0f);
}
glEnd();
//出栈
glPopMatrix();
//刷新命令缓冲区
glutSwapBuffers();
}
/**
改变窗口大小时回调函数
*/
void changeWindowSize(GLsizei w, GLsizei h)
{
GLfloat length = 100.0f;
if (h == 0) h = 1;
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w <= h) glOrtho(-length, length, -length, length * h / w, -length, length);
else glOrtho(-length, length * h / w, -length, length, -length, length);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
/**
设置
*/
void setupRC(void)
{
//背景颜色
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
//使用平面明暗处理
glShadeModel(GL_FLAT);
//顺时针为正方向
glFrontFace(GL_CW);
}
/**
旋转
*/
//增加按键相应消息机制,即按下某个键,改变点的坐标,使图形看起来根据你的操作旋转
void rotateMode(int key, int x, int y)
{ //方向键上
if (key == GLUT_KEY_UP)
xRot -= 5.0f;
//方向键下
else if (key == GLUT_KEY_DOWN)
xRot += 5.0f;
//方向键左
else if (key == GLUT_KEY_LEFT)
yRot -= 5.0f;
//方向键右
else if (key == GLUT_KEY_RIGHT)
yRot += 5.0f;
//判断X,Y值是否正常
if (xRot < 0) xRot = 355.0f;
if (xRot > 360.0f) xRot = 0.0f;
if (yRot < 0) yRot = 355.0f;
if (yRot > 360.0f) yRot = 0.0f;
//标记当前窗口需要重新绘制
glutPostRedisplay();
}
经过查找资料和思考,顺利完成这次实验。编程需要多练习,很多知识,你似乎很容易就看明白了,可是在实验的过程中,你会发现很多小细节在实验前你没注意到。有好多函数现在还理解不了,但是在用的过程中,会慢慢理解,不会的地方,可以自己试着改下参数值,这样理解起来就快一些。(注释纯手打,有误的话,请沟通我,谢谢)
另外附一份源码,供有兴趣的同学们研究。
//Triangle.c
#include
#include
#include
//旋转参数为静态变量
static GLfloat xRot = 0.0f;
static GLfloat yRot = 0.0f;
//确定多边形绕法的方向
BOOL bWinding = TRUE;
//初始化窗口
void SetupRC(void)
{
// 设置窗口的背景颜色为白色
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
}
//再回调函数
void ChangeSize(int w, int h)
{
//判断窗口的高度
if (h == 0) h = 1;
//设置视区尺寸
glViewport(0, 0, w, h);
//重置坐标系统,使投影变换复位
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//判断窗口的宽度和高度大小比
if (w <= h)
//建立修剪空间的范围
glOrtho(-100.0f, 100.0f, -100.0f*h / w, 100.0f*h / w, -100.0f, 100.0f);
else
//建立修剪空间的范围
glOrtho(-100.0f*w / h, 100.0f*w / h, -100.0f, 100.0f, -100.0f, 100.0f);
//重置坐标系统,使投影变换复位
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
//显示回调函数,在窗口中绘制图形
void RenderScene(void)
{
glClear(GL_COLOR_BUFFER_BIT);
// 旋转图形
glPushMatrix();
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
//设置点的大小以及线宽
glPointSize(5);
glLineWidth(5);
//设置多边形绕法的方向是顺时针还是逆时针
if (bWinding)
//顺时针
glFrontFace(GL_CW);
else
//逆时针
glFrontFace(GL_CCW);
// 绘制三角形
glBegin(GL_TRIANGLES);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(0, 60, 0);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(-60, -60, 0);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(60, -60, 0);
glEnd();
glPopMatrix();
glutSwapBuffers(); // 刷新命令缓冲区
}
//增加上下左右键的响应函数
void SpecialKeys(int key, int x, int y)
{
if (key == GLUT_KEY_UP) xRot -= 5.0f;
if (key == GLUT_KEY_DOWN) xRot += 5.0f;
if (key == GLUT_KEY_LEFT) yRot -= 5.0f;
if (key == GLUT_KEY_RIGHT) yRot += 5.0f;
if (xRot > 356.0f) xRot = 0.0f;
if (xRot < -1.0f) xRot = 355.0f;
if (yRot > 356.0f) yRot = 0.0f;
if (yRot < -1.0f) yRot = 355.0f;
//标记当前窗口需要重新绘制
glutPostRedisplay();
}
void ProcessMenu(int value)
{
switch (value)
{
case 1:
//修改多边形正面为填充模式
glPolygonMode(GL_FRONT, GL_FILL);
break;
case 2:
//修改多边形正面为线模式
glPolygonMode(GL_FRONT, GL_LINE);
break;
case 3:
//修改多边形为点填充模式
glPolygonMode(GL_FRONT, GL_POINT);
break;
case 4:
//修改多边形反面为填充模式
glPolygonMode(GL_FRONT, GL_FILL);
break;
case 5:
//修改多边形反面为线模式
glPolygonMode(GL_FRONT, GL_LINE);
break;
case 6:
//修改多边形反面为点模式
glPolygonMode(GL_FRONT, GL_POINT);
break;
case 7:
//修改多边形阴影模式为平面明暗模式
glShadeModel(GL_FLAT);
break;
case 8:
//修改多边形阴影模式为光滑明暗模式
glShadeModel(GL_SMOOTH);
break;
case 9:
bWinding = !bWinding;
break;
default:
break;
}
//标记当前窗口需要重新绘制
glutPostRedisplay();
}
int main(int argc, char* argv[])
{
int nModeMenu;
int nMainMenu;
int nColorMenu;
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutCreateWindow("多边形演示");
glutReshapeFunc(ChangeSize);
glutSpecialFunc(SpecialKeys); //设置特殊键响应回调函数
glutDisplayFunc(RenderScene);
//创建一个子菜单并定义菜单回调函数
nModeMenu = glutCreateMenu(ProcessMenu);
//添加菜单项,1表示选择菜单条目时传递的参数值
glutAddMenuEntry("正面多边形填充模式", 1);
glutAddMenuEntry("正面线框模式", 2);
glutAddMenuEntry("正面线点模式", 3);
glutAddMenuEntry("反面多边形填充模式", 4);
glutAddMenuEntry("反面线框模式", 5);
glutAddMenuEntry("反面点模式", 6);
//增加一个子菜单
nColorMenu = glutCreateMenu(ProcessMenu);
glutAddMenuEntry("平面明暗模式", 7);
glutAddMenuEntry("光滑明暗模式", 8);
//创建主菜单
nMainMenu = glutCreateMenu(ProcessMenu);
glutAddSubMenu("多边形模式", nModeMenu);
glutAddSubMenu("颜色模式", nColorMenu);
glutAddMenuEntry("改变绕法", 9);
//将创建的菜单与右键关联,即把菜单设置为右键弹出式菜单
glutAttachMenu(GLUT_RIGHT_BUTTON);
//初始化主窗口
SetupRC();
glutMainLoop();
return 0;
}