OpenGL(六)——创建三维图形
一、知识点
在绘制三维图形前,需要了解下,OpenGL的常见概念,可以打开前两篇文章查看:
如果你看完了,还是没有头绪,不知道绘制的流程。你且记住下面的内容,以后再慢慢消化理解:
1、你要知道从构造模型到最终渲染到屏幕坐标,这其中有6种坐标系。
2、他们的变换过程,是下图这样的:
3、可能有点绕,没关系,至少你记住下面这几个流程:
OpenGL渲染3D物体到屏幕上的过程其实类似我们平时用照相机拍照的过程,这个步骤大致如下:
一、把照相机固定在三脚架并让它对准场景(视图变换)
二、把场景中的物体调整摆放好(模型变换)
三、选择照相机的镜头,并调整放大倍数(投影变换)
四、确定最终照片的大小(视口变换)
其中视图变换必须要在模型变换之前,其它可以在任何时候。
4、变换的概念你可以看下面的图片:
上面的你先记住3中的内容就行,我们就开始我们的3维绘图。
二、绘制3位图形:
1、通过glutWireCube函数绘制:
#include <GLTools.h>
#include <stdio.h>
#include <gl/glew.h>
#include <gl/glut.h>
#include <math.h>
//不显示控制台窗口
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
void draw3D_1(void)
{
glClearColor(1.0, 1.0, 1.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0,0,0);
glLoadIdentity();
//1-视图变换
gluLookAt(6, 0, 2.5, 0, 0, 0, 1, 1, 0);//设置相机位置.
//前三个参数设置观察者的观察位置,中三个参数设置观察点的位置,后三个参数设置观察者的观察方向
glLineWidth(2.0f);
glutWireCube(2.0);
glFlush();
}
//当窗口初次创建或者改变时会被调用.
void shapeWidget(int w, int h)
{
glMatrixMode(GL_PROJECTION);//设置当前矩阵为投影矩阵.
glLoadIdentity();
//投影变换.三维变二维
glFrustum(-1.0, 1.0, -1.0, 1.0, 2.0, 10.0);//透视投影.
//glOrtho(-1.0, 1.0, -1.0, 1.0, 2.0, 20.0);
glMatrixMode(GL_MODELVIEW);//设置当前矩阵为模型视图矩阵.
//视口变换
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
}
int main(int argc, char ** argv)
{
glutInit(&argc, argv); //来初始化GLUT库并同窗口系统对话协商
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); //用来确定所创建窗口的显示模式。本例中的参数GLUT_SINGLE 指定单缓存窗口,\
这也是缺省模式,对应的模式为GLUT_DOUBLE 双缓存窗口。参数GLUT_RGB指定颜色RGBA模式,这也是缺省模式,\
对应的模式为GLUT_INDEX 颜色索引模式窗口。
glutInitWindowPosition(100, 100);
glutInitWindowSize(500, 500);
glutInitDisplayMode(GLUT_RGBA);
glutCreateWindow("3D widget");
glutDisplayFunc(draw3D_1);//册当前窗口的显示回调函数。当一个窗口的图像层需要重新绘制时,GLUT将调用该窗口的的显示回调函数。在此例中的mydisplay就是显示回调函数,\
显示回调函数不带任何参数,它负责整个图像层的绘制。我们的大部分工作将集中在这个函数中。
glutReshapeFunc(shapeWidget);
glutMainLoop();//进入GLUT事件处理循环。glutMainLoop函数在GLUT程序中最多只能调用一次,它一旦被调用就不再返回,\
并且调用注册过的回调函数。所以这个函数必须放在注册回调函数的后面,比如glutReshapeFunc, glutDisplayFunc。
return 0;
}
我们看下运行的效果:
2、通过绘制坐标点绘图:
#include <GLTools.h>
#include <stdio.h>
#include <gl/glew.h>
#include <gl/glut.h>
#include <math.h>
//不显示控制台窗口
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
void draw3D_2(void)
{
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
//1-视图变换
gluLookAt(4, 0, 1.5, 0, 0, 0, 1, 1, 0);//设置相机位置.
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_QUADS);
glNormal3f(-1, 0, 0);//设置点的法向量
glVertex3f(0.5, 0.5, 0.5);
glVertex3f(0.5, -0.5, 0.5);
glVertex3f(0.5, -0.5, -0.5);
glVertex3f(0.5, 0.5, -0.5);
//---2---
glNormal3f(-1, 0, 0);
glVertex3f(-0.5, 0.5, 0.5);
glVertex3f(-0.5, -0.5, 0.5);
glVertex3f(-0.5, -0.5, -0.5);
glVertex3f(-0.5, 0.5, -0.5);
//---3---
glNormal3f(0, 1, 0);
glVertex3f(0.5, 0.5, 0.5);
glVertex3f(-0.5, 0.5, 0.5);
glVertex3f(-0.5, 0.5, -0.5);
glVertex3f(0.5, 0.5, -0.5);
//---4---
glNormal3f(0, -1, 0);
glVertex3f(0.5, -0.5, 0.5);
glVertex3f(-0.5, -0.5, 0.5);
glVertex3f(-0.5, -0.5, -0.5);
glVertex3f(0.5, -0.5, -0.5);
//---5---
glNormal3f(0, 0, 1);
glVertex3f(0.5, 0.5, 0.5);
glVertex3f(-0.5, 0.5, 0.5);
glVertex3f(-0.5, -0.5, 0.5);
glVertex3f(0.5, -0.5, 0.5);
//---6---
glNormal3f(0, 0, -1);
glVertex3f(0.5, 0.5, 0.5);
glVertex3f(-0.5, 0.5, 0.5);
glVertex3f(-0.5, -0.5, 0.5);
glVertex3f(0.5, -0.5, 0.5);
glEnd();
glColor3f(0, 0, 0);
glLineWidth(2.0f);
//绘制正方体的边
glBegin(GL_LINES);
//---1---
glVertex3f(0.5, 0.5, 0.5);
glVertex3f(-0.5, 0.5, 0.5);
glVertex3f(-0.5, 0.5, 0.5);
glVertex3f(-0.5, -0.5, 0.5);
glVertex3f(-0.5, -0.5, 0.5);
glVertex3f(0.5, -0.5, 0.5);
glVertex3f(0.5, -0.5, 0.5);
glVertex3f(0.5, 0.5, 0.5);
//---2---
glVertex3f(0.5, 0.5, -0.5);
glVertex3f(-0.5, 0.5, -0.5);
glVertex3f(-0.5, 0.5, -0.5);
glVertex3f(-0.5, -0.5, -0.5);
glVertex3f(-0.5, -0.5, -0.5);
glVertex3f(0.5, -0.5, -0.5);
glVertex3f(0.5, -0.5, -0.5);
glVertex3f(0.5, 0.5, -0.5);
//---3---
glVertex3f(0.5, 0.5, 0.5);
glVertex3f(0.5, -0.5, 0.5);
glVertex3f(0.5, -0.5, 0.5);
glVertex3f(0.5, -0.5, -0.5);
glVertex3f(0.5, -0.5, -0.5);
glVertex3f(0.5, 0.5, -0.5);
glVertex3f(0.5, 0.5, -0.5);
glVertex3f(0.5, 0.5, 0.5);
//---4---
glVertex3f(-0.5, 0.5, 0.5);
glVertex3f(-0.5, -0.5, 0.5);
glVertex3f(-0.5, -0.5, 0.5);
glVertex3f(-0.5, -0.5, -0.5);
glVertex3f(-0.5, -0.5, -0.5);
glVertex3f(-0.5, 0.5, -0.5);
glVertex3f(-0.5, 0.5, -0.5);
glVertex3f(-0.5, 0.5, 0.5);
glEnd();
glFlush();
}
//当窗口初次创建或者改变时会被调用.
void shapeWidget(int w, int h)
{
glMatrixMode(GL_PROJECTION);//设置当前矩阵为投影矩阵.
glLoadIdentity();
//投影变换.三维变二维
glFrustum(-1.0, 1.0, -1.0, 1.0, 2.0, 10.0);//透视投影.
//glOrtho(-1.0, 1.0, -1.0, 1.0, 2.0, 20.0);
glMatrixMode(GL_MODELVIEW);//设置当前矩阵为模型视图矩阵.
//视口变换
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
}
int main(int argc, char ** argv)
{
glutInit(&argc, argv); //来初始化GLUT库并同窗口系统对话协商
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); //用来确定所创建窗口的显示模式。本例中的参数GLUT_SINGLE 指定单缓存窗口,\
这也是缺省模式,对应的模式为GLUT_DOUBLE 双缓存窗口。参数GLUT_RGB指定颜色RGBA模式,这也是缺省模式,\
对应的模式为GLUT_INDEX 颜色索引模式窗口。
glutInitWindowPosition(100, 100);
glutInitWindowSize(500, 500);
glutInitDisplayMode(GLUT_RGBA);
glutCreateWindow("3D widget");
glutDisplayFunc(draw3D_2);//册当前窗口的显示回调函数。当一个窗口的图像层需要重新绘制时,GLUT将调用该窗口的的显示回调函数。在此例中的mydisplay就是显示回调函数,\
显示回调函数不带任何参数,它负责整个图像层的绘制。我们的大部分工作将集中在这个函数中。
glutReshapeFunc(shapeWidget);
glutMainLoop();//进入GLUT事件处理循环。glutMainLoop函数在GLUT程序中最多只能调用一次,它一旦被调用就不再返回,\
并且调用注册过的回调函数。所以这个函数必须放在注册回调函数的后面,比如glutReshapeFunc, glutDisplayFunc。
return 0;
}
我们看下运行效果:
通过上面的代码你可能知道了,绘制图形中各种变换的顺序。
本文原创作者:冯一川(ifeng12358@163.com),未经作者授权同意,请勿转载。如需获取本程序源代码和资源,欢迎发邮件与我联系。