1、 程序运行的软硬件环境算法
本次设计在window10系统下进行,运用C++进行编写,在CodeBlocks环境下使用OpenGL进行设计。编程
所需环境配置分为2部分,第一部分是CodeBlocks的配置,第二部分为OpenGL的相关配置。windows
Codeblocks配置:数组
- 打开搜索引擎,搜索CodeBlocks,点击相关结果进入CodeBlocks官网:codeblocks.org。
- 选择Download选项卡
- 点击Download the binary release
- 选择带mingw编译器的版本codeblocks-16.01mingw-setup.exe
- 下载完成后,直接安装,除了安装路径能够改,其余的都选择默认的便可。
- 打开CodeBlocks便可使用。
OpenGL的配置:函数
- 将h文件拷贝到MinGw\include\GL目录下
- 将dll这个动态连接库文件拷贝到相应文件夹下,此处注意文件夹根据机器操做系统位数不一样,拷贝的文件夹路径也不一样,具体区别为,若是是32位操做系统,则应该将glut32.dll文件拷贝到C:\Windows\System32文件夹下,若是是64位操做系统,则应将该文件拷贝到C:\Windows\SysWOW64文件夹下,若是拷贝错误,将致使程序编译的时候由于找不到该文件而没法经过,以64位机器为例。
- 将a文件放置到MinGw\lib文件夹下。
- 建立openGL工程,打开CodeBlocks,点击file->new->project->GLUT project->go,而后一直下一步,直至进入工程建立完毕,点击工具栏project->build options.在build options中Debug->Linker settings->Add,将咱们第三步放置好的libglut32.a文件添加进来,保存退出。而后自带的openGL实例程序便可正常编译运行,至此CodeBlocks下的openGL开发配置完成。
2、涉及的相关算法的原理工具
本次设计运用C++编写,利用OpenGL进行图像设计,设计的内容为:绘制一个球体,在其表面进行纹理映射,而且球体保持做自由落体运动,同时,能够对球体进行旋转、缩放等操做。oop
纹理映射的原理:ui
纹理映射使用一个图案或者纹理来肯定渲染流水线中片元处理阶段片元的颜色。简单来说,纹理就是矩形的数据数组,例如颜色数据、亮度数据、颜色和alpha数据。纹理数组中的单个值经常称为纹理单元,也叫纹素(texel),这里让它区别于像素,主要是为了强调它的应用方式。 OpenGL支持1D、2D、3D以及立方体纹理,如今主要考虑2D纹理。搜索引擎
纹理映射就是要实现,如何把纹素映射到几何对象的每一个点。一个2D的纹理有一个宽度和高度,经过宽度和高度相乘便可获得有多少个纹素。spa
那么如何来指定顶点的纹素呢?经过坐标来指定,可是这个坐标不该该是具体纹理的中坐标,而应该是抽象的纹理坐标空间中的坐标;不然经过指定具体纹理的坐标,当更换纹理,例如改变纹理的宽度和高度时,这些坐标值可能变得无心义,而不得不更新全部顶点的坐标值,所以须要使用抽象的纹理坐标空间的坐标。纹理坐标通常都规范化到[0,1]范围内。例如一个纹理宽度为320,高度为200,而纹理坐标(0.5,0.1)则表示纹素的位置在: (320*0.5,200*0.1)=(160,20)。一般使用UV坐标系来表示纹理坐标系:
这里注意,OpenGL中V轴从下往上是正方向,U轴从左往右是正方向。在具体使用时,这与应用中纹理Y方向有关。若是纹理从上到下,则须要将纹理的Y方向翻转来知足这个图形所示的纹理坐标。与纹理映射有关的一个特性是,当模型进行变换时,纹理坐标仍然会跟着模型的顶点,他们并不进行变换(固然也有其余方法能够改变纹理坐标),就好像粘着顶点同样。例以下图所示的三角形,若是在其中应用一个小的纹理:
当对三角形进行变换时,纹理坐标保持不变,这样当模型进行旋转、拉伸和放缩时,纹理也会跟着变化,以下图所示:
与纹理有关的另外一个特性是纹理采样。当把纹理坐标映射到纹素数组时,正好获得对应纹素的中心位置的状况不多出现。解决这一问题的一种方法是,从纹素数组中取这样一个纹素,该纹素的位置,最佳逼近经过光栅化模块计算输出的纹理坐标。这样一方法成为点采样(Point sampling),也叫作nearest filtering。例如坐标(152.34,745.14)的纹素,就使用(152,745)来代替。用点采样容易产生走样偏差。
另一种方法是线性滤波方法(linear filtering)。例如,若是计算出一个纹理坐标位于(152.34,745.14),那么这个值对应的最近的4个纹理坐标为: ( (152,745), (153,745), (152,744) , (153,744) )。那么咱们能够利用这4个纹理坐标的对应的颜色值进行线性插值,例如计算这一组纹素的加权平均值,并把该值做为纹理坐标映射到纹素数组时的纹素值。这种方法计算量要比点菜用大,效果通常比点采样好。
OpenGL支持多种滤波类型,能够经过设置来进行选择。
在进行纹理映射时,还须要考虑纹素与屏幕像素之间的对应关系。单个的纹素一般并不与屏幕像素对应,当纹素比单个像素大时,屏幕上多个像素对应于单个像素,称之为放大(manification);当纹素比单个像素小,屏幕上单个像素对应多个纹素,则称之为缩小(minification)。关系以下图所示:
自由落体实现原理:
定义2个常量和1个变量,常量分别是G=9.8为重力加速度、t=0.002为时间间隔;变量为布尔类型的direction,初始化为direction=true,记录球体运动方向,当direction=true时,球体下落,下落到达下极限时,改变direction=false,球体开始上弹。
定义一个结构体,储存球体对象,即:
在此结构中,定义了球体的y坐标属性y和y坐标上的速度属性vy。在球体运动过程当中,运用牛顿运动定律计算球体的位置及速度,经过OpenGL里glTranslated()方法对绘制中心重定义,而后对球体进行从新绘制。
在球体运动过程当中,球体位移公式为:
direction = true à ball.y = ball.y – ( ball.vy*t + 0.5*G*t*t);
direction = false à ball.y = ball.y + ( ball.vy*t – 0.5*G*t*t);
旋转、缩放实现原理:
旋转和缩放将用到OpenGL自带的3个函数实现,即:glRotatef()、gluLookAt()和glScalef()。glRotatef()和gluLookAt()控制左右、先后旋转,glScalef()控制缩放。整个控制旋转、缩放的控制放在keyboard()方法下实现。
3、程序设计思想和设计过程
OpenGL中纹理映射的步骤以下:
一、建立纹理对象,并为他指定一个纹理
二、肯定纹理如何应用到每一个像素上
三、启用纹理贴图功能
四、绘制场景,提供纹理坐标和几何图形坐标
实现纹理映射主要关系到4个概念:纹理对象(the texture object), 纹理单元(the texture unit), 采样器对象(the sampler object )采样器变量(sampler uniform in the shader).他们的关系以下图所示:
纹理对象并不直接绑定到着色器,而是绑定到一个纹理单元,纹理单元的索引将会传递给着色器。要绑定到一个纹理单元,先要将其激活,可使用glActiveTexture函数,例如glActiveTexture(GL_TEXTURE0)将激活单元0。可使用多个纹理单元,每一个纹理单元能够绑定到相同或者不一样的纹理对象。有一点值得注意,只要纹理对象的类型不一样,一个纹理单元能够绑定多个纹理对象。例如你能够分别将两个纹理对象绑定到同一个纹理单元的1D和 2D不一样的目标上。能够经过采样器变量来使用多个纹理,这个uniform变量有’sampler1D’, ‘sampler2D’, ‘sampler3D’, ‘samplerCube’等不一样形式。在片元着色器中,采样函数须要经过采样器变量来访问多个纹理单元。采样器对象与纹理对象不相同。纹理对象中包含了纹理数据,以及配置采样操做的参数,这些参数是采样状态的一部分。然而,你也能够建立一个采样对象,用采样状态参数配置它,并把它绑定到纹理单元中。这样,采样器对象会覆盖纹理对象中定义的采样状态。目前咱们并不使用这一对象。
球体运动控制的流程图:
对流程图进行简单的说明:初始化即对球体的y坐标和y轴上的速度vy进行定义,ball.y=10,ball.vy=0;经过glTranslated()函数实现球体的移动,在移动过程当中由牛的你运动定律肯定球体的坐标以及速度的变化;每次移动都进行判断,是否到达下极限-4,若是尚未到达则继续下落,若是已经到达下极限则改变球体运动方向,使球体上升,每一次上升操做都判断是否到达上极限,若是尚未到达则继续上升,不然改变球体运动方向。
用到的主要OpenGL函数:
函数名 | 参数 | 实现功能 |
makeStripeImage | Void | 制做纹理图形 |
init | Void | 对程序数据进行初始化 |
idle | Void | 空闲时运行的函数 |
display | Void | 控制图形界面的显示,包括图形绘制以及一些属性的控制 |
glutPostRedisplay | Void | 对图形进行重绘 |
glTranslated | x、y、z | 对图形进行移动,x、y、z分别为对应轴上移动的距离 |
glutSolidSphere | GLdouble radius , GLint slices , GLint stacks | 渲染一个球体 |
glVertex3f | x、y、z | 肯定一个顶点 |
reshape | int w , int h | 对显示窗口的控制 |
keyboard | unsigned char key,int x,int y | 实现对图形旋转、缩放的控制 |
遇到的问题:
在功能实现过程当中,遇到一个很是棘手且奇怪的问题,只怪我对openGL掌握得还不是很熟练加上时间也不是很充足,因此至今没有解决,这个问题就是:
当t=0.002时,球体运动的下限不能低于-4,上限不能高于10,不然球体在第二次往返时将不能正常改变方向;当t=0.003时,下限不能低于-3,上限不能高于10,不然结果与上述同样。
4、程序源代码和使用说明
1)程序源代码
#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include
#include
#include
#define stripeImageWidth 32
#define G 9.8
GLubyte stripeImage [4*stripeImageWidth];
static GLuint texName;
bool direction = true;
double t = 0.002;
typedef struct b //定义储存球体的结构
{
GLdouble y;
GLdouble vy;
}Ball;
Ball ball;
void makeStripeImage(void){ //制做纹理条纹
int j;
for(j = 0; j < stripeImageWidth; j++){
stripeImage[4*j] = (GLubyte)((j<=4)?255:0); stripeImage[4*j+1] = (GLubyte)((j>4)?255:0);
stripeImage[4*j+2] = (GLubyte)0;
stripeImage[4*j+3] = (GLubyte)255;
}
}
static GLfloat xequalzero[] = {1.0,0.0,0.0,0.0};
static GLfloat *currentCoeff;
static GLenum currentPlane;
static GLint currentGenMode;
void init(void){
ball.y = 10 ; //初始化球体属性
ball.vy = 0 ;
glClearColor(0.0,0.0,0.0,0.0);
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
makeStripeImage();
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
glGenTextures(1,&texName);
glBindTexture(GL_TEXTURE_1D,texName);
glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_WRAP_S,GL_REPEAT);
glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage1D(GL_TEXTURE_1D,0,GL_RGBA,stripeImageWidth,0,GL_RGBA,GL_UNSIGNED_BYTE,stripeImage);
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
currentCoeff = xequalzero;
currentGenMode = GL_OBJECT_LINEAR;
currentPlane = GL_OBJECT_PLANE;
glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,currentGenMode);
glTexGenfv(GL_S,currentPlane,currentCoeff);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_LIGHTING); //开启光照
glEnable(GL_LIGHT0); //开启光源0
glEnable(GL_AUTO_NORMAL);
glEnable(GL_NORMALIZE);
glFrontFace(GL_CW);
glMaterialf(GL_FRONT,GL_SHININESS,64.0);
}
void idle(void) //空闲函数
{
glutPostRedisplay();
}
void display(void){
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glEnable(GL_TEXTURE_1D);
glBindTexture(GL_TEXTURE_1D,texName);
glPushMatrix(); //将当前矩阵压入矩阵堆栈,本//次操做结束以后将其弹出,使//得本次操做不影响下次操做
glTranslated(0,ball.y,0); //将绘图中心移动到球体的中心
glutSolidSphere(2,50,50); //绘制一个球体
if(direction){
ball.y = ball.y – (ball.vy*t+0.5*G*t*t); //根据牛顿运动定律计算出球
//的位移公式
ball.vy = ball.vy + G*t; //根据牛顿运动定律计算出球体的速度
if(ball.y <=-4){ direction = false; //触发转向条件,改变direction的值,使球//体运动方向改变 } }else{ ball.y = ball.y + (ball.vy*t-0.5*G*t*t); ball.vy = ball.vy – G*t; if(ball.y >= 10){
direction = true;
}
}
glPopMatrix(); //将当前矩阵弹出
glDisable(GL_TEXTURE_1D);
glFlush();
}
void reshape(int w,int h){
glViewport(0,0,(GLsizei)w,(GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(w<=h)
glOrtho(-15,15,-15*(GLfloat)h/(GLfloat)w,15*(GLfloat)h/(GLfloat)w,-15,15);
else
glOrtho(-15*(GLfloat)h/(GLfloat)w,15*(GLfloat)h/(GLfloat)w,-15,15,-15,15);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void keyboard(unsigned char key,int x,int y){ //控制键盘输入,键盘输入a、d、//s、w时分别实现左右先后的旋转,
switch(key){ //输入e、r的时候实现放大和缩小
case ‘a’:
glRotatef(5.0,0.0,0.0,1.0);
glutPostRedisplay();
break;
case ‘d’:
glRotatef(-5.0,0.0,0.0,1.0);
glutPostRedisplay();
break;
case ‘s’:
gluLookAt(0,0.01,0.01,0,0,0,0,1,0);
glutPostRedisplay();
break;
case ‘w’:
gluLookAt(0,-0.01,0.01,0,0,0,0,1,0);
glutPostRedisplay();
break;
case ‘e’:
glScalef(1.1,1.1,1.1);
glutPostRedisplay();
break;
case ‘r’:
glScalef(0.9,0.9,0.9);
glutPostRedisplay();
break;
default:
break;
}
}
int main(int argc,char** argv){
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB|GLUT_DEPTH);
glutInitWindowSize(600,600);
glutInitWindowPosition(450,100);
glutCreateWindow(argv[0]);
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutIdleFunc(idle);
glutMainLoop();
return 0;
}
2)程序说明:
该程序引入了3个OpenGL的包,分别是gl.h、glu.h、glut.h,都是一些OpenGL的基础库,因此并不须要额外进行库函数的配置。程序主体能够分为5个部分,几个主要模块分别为init()、display()、reshape()、keyboard()、main()。
其中,init()主要对程序数据进行初始化,包括球体属性设置,渲染属性设置等。display()用于绘制图形,包括球体和地面,经过不断调用display()函数实现动态效果。reshape()函数则是对视口的设置,当窗口大小变化时,为了防止物体变形,这时要重设投影转换矩阵,设置视口转换矩阵,以及视图转换矩阵。keyboard()函数则是处理键盘输入,对图形的旋转、缩放进行控制:
a:左旋转 d:右旋转
s:前旋转 w:后旋转
e:放大 r:缩小
main()函数是程序的入口,调用其余方法实现程序功能。
5、程序运行结果截图
6、参考目录
- OpenGL编程指南
- 纹理原理部分由参考自百度
原创文章:引用代表出处