本节将演示在3D空间中绘制图形的几个简单实例:
(1)在3D空间内绘制圆锥体:
#include <GL/glut.h>
#include <math.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
#define PI 3.1416
GLfloat xRot = ;
GLfloat yRot = ;
GLfloat zRot = ; void Init(void)
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glColor3f(1.0f, 1.0f, 0.0f);
// 把着色模式设置为单调着色
glShadeModel(GL_FLAT); //glShadeModel(GL_SMOOTH);
// 把顺时针环绕的多边形设为正面,这与默认是相反的,因为我们使用的是三角形扇
glFrontFace(GL_CW);
glOrtho(-1.0f,1.0f,-1.0f,1.0f,-1.0f,1.0f);
}
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 (key> 356.0f)
xRot = 0.0f; if (key< -1.0f)
xRot = 355.0f; if (key> 356.0f)
yRot = 0.0f; if (key< -1.0f)
yRot = 355.0f; if (key==GLUT_KEY_F1) //绕着z轴旋转
zRot+= 5.0f; // 使用新的坐标重新绘制场景
glutPostRedisplay();
}
void RenderScene()
{
// 存储坐标和角度
GLfloat x, y, z, angle,x1,y1;
// 用于三角形颜色的交替设置
int iPivot = ;
// 用默认颜色设置背景色,并清除深度缓冲区(必须的,因为3D空间有视景深度)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
//打开剔除功能(背面剔除,它用于消除一个表面的背面)
//glEnable(GL_CULL_FACE);
// 打开深度测试,如果不打开深度测试,3D锥体的显示就会与现实情况不符合
glEnable(GL_DEPTH_TEST); // 保存矩阵状态并旋转
glPushMatrix();
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
glRotatef(zRot, 0.0f, 0.0f, 1.0f); glBegin(GL_TRIANGLE_FAN);
// 三角形扇的共享顶点,z轴中心点上方
glVertex3f(0.0f,0.0f,0.75);
for (angle = 0.0f; angle < (2.0f * PI + PI / 8.0f); angle += (PI / 8.0f)) {
// 计算下一个顶点的位置
x = 0.50f * sin(angle);
y = 0.50f * cos(angle);
if ((iPivot % ) == ) {
glColor3f(1.0f, 1.0f, 0.0f);
}
else{
glColor3f(0.0f, 1.0f, 1.0f);
}
// 增加基准值,下次改变颜色
++iPivot;
// 指定三角形扇的下一个顶点
glVertex2f(x, y);
}
glEnd(); // 绘制一个新的三角形扇,作为覆盖圆锥的底
glBegin(GL_TRIANGLE_FAN);
// 三角形扇的共享顶点,中心位于原点
glVertex3f(0.0f, 0.0f,0.0f);
for (angle = 0.0f; angle < (2.0f * PI + PI / 8.0f); angle += (PI / 8.0f)) {
// 计算下一个顶点的位置
x = 0.50f * sin(angle);
y = 0.50f * cos(angle);
if ((iPivot % ) == ) {
glColor3f(0.5f, 0.0f, 0.5f);
}
else{
glColor3f(1.0f, 0.0f, 1.0f);
}
// 增加基准值,下次改变颜色
++iPivot;
// 指定三角形扇的下一个顶点
glVertex2f(x, y);
}
glEnd();
glPopMatrix();
glutSwapBuffers();
}
int main(int argv,char *argc[]){ glutInit(&argv,argc);
glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE);
glutInitWindowSize(,);
glutInitWindowPosition(,);
glutCreateWindow("3D空间绘制圆锥面");
glutDisplayFunc(RenderScene);
glutSpecialFunc(SpecialKeys);
Init();
glutMainLoop();
}
按F1键绕z轴旋转,按方向键绕x轴或y轴旋转:
(2)在3D空间中模拟地球环绕太阳旋转:
#include <GL/glut.h>
#include <stdlib.h>
#include <math.h>
#define PI 3.1416
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
static int year = ; void init(void)
{
glClearColor(0.2, 0.2, 0.2, 1.0);
glShadeModel(GL_FLAT);
}
void paintLoopPlant()
{
GLfloat x, z; int i;
glColor3f(0.0f,1.0f,0.5f);
glLineWidth(2.0f);
glBegin(GL_LINE_LOOP);
for (i = ; i < ;i++)
{
x = 0.35*sin(i * * PI / -PI);
z = 0.35*cos(i * * PI / -PI);
glVertex3f(x,,z);
}
glEnd(); glLineWidth(5.0f);
glColor3f(0.5,,);
glBegin(GL_LINE_LOOP);
for (i = ; i < ; i++)
{
x = 3.5*sin(i * * PI / )-3.5;
z = 3.5*cos(i * * PI / );
glVertex3f(x, , z);
}
glEnd();
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glColor3f(, 0.0, 0.0);//设置太阳的颜色
glPushMatrix();
glPointSize(1.0f);
//在坐标系*画太阳
glutWireSphere(0.8, , );
//给太阳进平移
glRotatef((GLfloat)year, 0.0, 1.0, 0.0);
glTranslatef(3.5, 0.0, 0.0);
//画地球
glColor3f(, 0.2, 1.0);
glutWireSphere(0.15, , );
paintLoopPlant(); glPopMatrix();
glutSwapBuffers();
}
void reshape(int w, int h)
{
glViewport(, , (GLsizei)w, (GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0, (GLfloat)w / (GLfloat)h, 1.0, 20.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0, 6.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
} void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 'y':
year = (year + ) % ;
break;
case 'Y':
year = (year - ) % ;
break;
case :
exit();
break;
default:
break;
}
glutPostRedisplay();
} int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowSize(, );
glutInitWindowPosition(, );
glutCreateWindow("RotateAroundSun");
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutMainLoop(); return ;
}
(3)在3D空间用OpenGL自带的库函数绘制立方体:
#include <GL/glut.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"") GLfloat yRot = ; //用来画一个坐标轴的函数
void axis(double length)
{
glColor3f(1.0f, 1.0f, 1.0f);
glPushMatrix();
glBegin(GL_LINES);
glVertex3d(0.0, 0.0, 0.0);
glVertex3d(0.0, 0.0, length);
glEnd();
//将当前操作点移到指定位置
glTranslated(0.0, 0.0, length - 0.2);
glColor3f(1.0, 0.0, 0.0);
glutWireCone(0.04, 0.3, , );
glPopMatrix();
}
void paint(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity(); glOrtho(-2.0, 2.0, -2.0, 2.0, -, );
glPointSize();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(1.3, 1.6, 2.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); //画坐标系
axis(); glPushMatrix();
glRotated(90.0, , 1.0, );//绕y轴正方向旋转90度
axis();
glPopMatrix(); glPushMatrix();
glRotated(-90.0, 1.0, 0.0, 0.0);//绕x轴负方向旋转
axis();
glPopMatrix(); glPushMatrix();//旋转除坐标轴之外的物体
glRotated(yRot, 0.0, yRot, 0.0);
glPushMatrix();
glColor3f(0.0f, 0.0f, 1.0f);
glTranslated(0.125, 0.125, 0.125);
glutWireCube(0.25);
glPopMatrix(); glPushMatrix();
glTranslated(1.125,0.125,0.125);
glutWireTeapot(0.25);
glPopMatrix(); glPushMatrix();
glTranslated(0.125, 0.125, 1.125);
glutWireSphere(0.25, , );
glPopMatrix(); glPushMatrix();
glTranslated(1.125,0.125,1.125);
glRotated(-, 1.0, 0.0, 0.0);
glutWireTorus(0.1, 0.25, , );
glPopMatrix(); glPushMatrix();
glTranslated(0.125, 1.125, 0.125);
glScaled(0.5*0.25, 0.5*0.25, 0.5*0.25);
glRotated(-, 0.0, 0.0, 1.0);
glutWireDodecahedron();
glPopMatrix(); glPushMatrix();
glTranslated(0.0, 1.0, 1.0);
GLUquadricObj * quadricObj = gluNewQuadric();
gluQuadricDrawStyle(quadricObj, GLU_LINE);
gluCylinder(quadricObj, 0.2, 0.2, 0.3, , );
glRotated(-, 0.0, 0.0, 0.0);
glPopMatrix(); glPopMatrix();//用来整体绕y轴旋转
glutSwapBuffers();
}
void Init()
{
glClearColor(0.8,0.8,0.8,1.0);
}
void reshape(int w, int h)
{
glViewport(, , (GLsizei)w, (GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0, (GLfloat)w / (GLfloat)h, 1.0, 20.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(1.3, 1.6, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}
void SpecialKeys(int key, int x, int y)
{
if (key == GLUT_KEY_LEFT)
yRot -= 5.0f; if (key == GLUT_KEY_RIGHT)
yRot += 5.0f; if (key> 356.0f)
yRot = 0.0f; if (key< -1.0f)
yRot = 355.0f;
glutPostRedisplay();
}
int main(int argv, char *argc[])
{
glutInit(&argv, argc);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
glutInitWindowSize(, );
glutInitWindowPosition(, );
glutCreateWindow("3D空间绘制各种系统自带立方体");
Init();
glutDisplayFunc(paint);
glutReshapeFunc(reshape);
glutSpecialFunc(SpecialKeys);
glutMainLoop();
}
在这个例子下按左右方向键也是可以让立方体绕y轴旋转的,在此不做演示。
(4)相关知识:
1)OpenGL中的空间变换:(http://www.cnblogs.com/chengmin/archive/2011/09/12/2174004.html)
在使用OpenGL的三维虚拟程序中,当我们指定了模型的顶点之后,在屏幕上显示它们之前,一共会发生3种类型的变换:
视图变换、模型变换、投影变换。
1.视图变换:指定观察者(摄像机)的位置;
2.模型变换:在场景中移动物体;
3.投影变换:改变可视区域的大小;
视口变换:这是一种伪变换,它对窗口上的最终输出进行缩放。
1.视觉坐标:
它表示一种虚拟的固定坐标系统,通常作为一种参考系使用。它是根据观察者(摄像机)的角度而言的,与可能发生的变换无关。我们接下来所讨论的所有变换都是根据它们相对于视觉坐标的效果进行描述的。
用OpenGL在3D空间中进行绘图时,使用的是笛卡尔坐标系统。在不进行任何变换的情况下,这个坐标系统与视觉坐标系相同。
2.视图变换:
这是场景中所应用的第一个变换,它用于确定场景的观察点(拍摄点)。视图变换允许把观察点放在自己所希望的任何位置(观察点的位置任意),并允许在任何方向上观察场景(观察点的朝向任意)。确定视图变换就像在场景中放置照相机并让它指向某个方向。
作为总体原则,在进行任何其他变换之前必须先指定视图变换。因为视图变换的效果相当于根据视觉坐标系统来移动当前所使用的坐标系统。然后,根据最新修改的坐标系统,进行其它所有的后续变换。
3.模型变换
它可以移动物体,对它们进行旋转、平移或者缩放。并且,缩放可以是非一致的(物体的各个方向根据不同的数值进行伸缩)。 场景或物体的最终外观很大程度上取决于模型变换的应用顺序。因为每次变换都是在上次变换执行的基础上进行的。
4.投影变换:
它是在模型视图变换之后应用于物体的顶点之上的。它实际上定义了可视区域,并建立了裁剪平面。其中,投影又有两种不同的类型:正投影(平行投影)和透视投影。正投影通常用于2D绘图,此时你所需要的是像素和绘图单位之间的准确对应。透视投影则用于渲染那些包含了需要应用透视缩短的物体的场景。并且在大多数情况下,3D图形所使用的都是透视投影。
下面是2D投影变换与3D透视投影变换的对比:
5.视口变换
最终,场景的二维投影将被映射到屏幕上的某个窗口。这种到物理窗口坐标的映射是最后一个完成的变换,称为视口变换。
2)Win窗口坐标二维坐标与OpenGl的世界坐标:
1.Win的屏幕坐标的坐标系: 左上为坐标系原点,正右为X轴正方向, 正下方为Y轴正方向。视图类的窗口是这样的,如果是FrameWork的绘图区,则需要包含工具栏和状态栏的区域,二者是不一样的。
2.OpenGL中的屏幕坐标:以左下角为原点,正右方为x轴正方向,正上方为y轴正方向。
墨卡托坐标变换
OPENGL-----3D到2D坐标变换
OPENGL-----2D到3D坐标变换