glRectf(-0.5,-0.5,0.5,0.5);
//画个方块
glFlush();
//强制绘图,不驻留缓存
_sleep(1000);
//windows函数,显示1秒(单位是毫秒)
}
//endofsample
根据注释,应该看出程序功能:
显示红色方块(2D)一秒钟。
主程序结构不外乎:
初始化+绘图+其它功能调用,现在不用过分追究函数细节,知道是干什么的就可以乐,好吗?
我想结束本节前让大家实现第一个3D的例子,很简单而且好看的,但是我们必须被迫不得不多学些东东:
第二讲
1.OPENGL函数、变量命名准则
变量:
前缀类型对应C变量
b8-bitintsignedchar
s16-bitintshort
i32-bitintlong
f32-bitfloatfloat
d64-bitfloatdouble
ub8-bitunsignedintunsignedchar
us16-bitunsignedintunsignedshort
ui32-bitunsignedintunsignedlong
例如1.0f实际就是1.0,一般写成1.0f看上去好辨认一些。
函数:
函数参数类型作为函数后缀
例如:
glVertex2i(2,4)表明是opengl基本函数(gl-)
是绘点的函数(-Vertex-)
是两个整型参数(-2i)
将来对一个函数掐头去尾就知道它是干什么的乐。
2.CALLBACK函数
是一些用来让系统调用的函数,系统要调用它们,例如显示、接受键盘输入...
你就好比擂积木一样把它们列在主程序初始化后(顺序一般无关紧要),
系统会自动执行它们,例如:
voidCALLBACKdisplay(void)中你写好乐想画什么东东,然后主程序中使用auxMainLoop(display);就可以让这个东东一直显示乐,很类似VC中的OnPaint()
3.OPENGL基本库的绘图方法
glBegin(类型);
glColor3f(...);
glVertex(...)
glColor3f(...);
glVertex(...);
...
glEnd();
先由“类型”指定画什么平面:
GL_POINTS单个顶点集
GL_LINES多组线,2个点一条线
GL_GL_POLYGON单个简单填充凸多边形
GL_TRAINGLES多组三角型,3个点一个三角
..用到在说吧(自己可以在VCHELPINDEX中查找)
//////////////////////////////////////////////////////
//sample.cpp
#include"glos.h"
#include
#include
#include"windows.h"
voidmyinit(void);
voidCALLBACKdisplay(void);
voidCALLBACKreshape(GLsizeiw,GLsizeih);
//这里不用管reshape(),只是一个用于窗口改变大小时的处理
//与绘图无关,后面会讲到
//这里的重点是display(),请注意绘图的方法
voidmyinit(void)
{
/*初始化:
*/
auxInitDisplayMode(AUX_SINGLE|AUX_RGBA);
//窗口显示单缓存和RGB(彩色)模式
auxInitPosition(0,0,500,500);
//大小x=500y=500(0,0)是屏幕左上点
auxInitWindow("sample1");
//窗口初始化,参数是标题
glClearColor(0.0,0.0,0.0,0.0);
//将窗口清为黑色
glClear(GL_COLOR_BUFFER_BIT);
//将颜色缓存清为glClearColor命令所设置的颜色
//即背景色
}
voidCALLBACKreshape(GLsizeiw,GLsizeih)
{
glViewport(0,0,w,h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(w<=h)
glOrtho(-20.0,20.0,-20.0*(GLfloat)h/(GLfloat)w,
20.0*(GLfloat)h/(GLfloat)w,-50.0,50.0);
else
glOrtho(-20.0*(GLfloat)h/(GLfloat)w,
20.0*(GLfloat)h/(GLfloat)w,-20.0,20.0,-50.0,50.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
voidCALLBACKdisplay(void)
{
glBegin(GL_TRIANGLE_STRIP);//画连续填充的多个三角
glColor3f(1.0,0.0,0.0);
glVertex3f(15.0,0.0,0.0);
glColor3f(0.0,1.0,0.0);
glVertex3f(-15.0,0.0,0.0);
glColor3f(0.0,0.0,1.0);
glVertex3f(0.0,15.0,15.0);
//第一个三角
glColor3f(0.0,1.0,1.0);
glVertex3f(10.0,15.0,-15.0);
//第二个三角
glColor3f(1.0,1.0,0.0);
glVertex3f(15.0,0.0,0.0);
//第三个三角
glEnd();
glFlush();
}
voidmain(void)
{
myinit();
auxReshapeFunc(reshape);
auxMainLoop(display);
}
//endofsample
所谓连续填充就是1、2、3点组成三角1
2、3、4点组成三角2...
这里一共画乐3个向连接的彩色三角,组成乐一个由三个彩色平面围成的空心三角椎...enjoyit
第三讲
上回书说道有个reshape需要进一步讲解这个函数功能是对用户改变窗口大小的操作进行一些重绘的动作(类似VC中的OnResize)。
其中用到了一些变换的概念我希望大家已经具备初步的计算机图形学的知识,这将有利于这部分的理解。
如果还没有,也没关系,我尽量讲解的通俗一些对于3D绘图,把其中3D坐标写成齐次坐标系后,是4*4的矩阵形式(详细…………………………………………………………
可以参阅相关文献,后面也会讲到。
)任何投影、旋转...操作都可以看成是矩阵相乘的操作。
矩阵操作----这个概念一定要形成!
!
例如在2D中普通的旋转变换,可以写成:
|cosθsinθ0|
[x'y'1]=[xy1]|-sinθcosθ0|
|001|
3D中道理完全一样,一个3D图形的显示包括下面步骤:
1.视点变换(将相机放在合适的地方对准3D景物)
2.模型变换(将3D物体放在合适的地方)
3.投影变换(将相机镜头调整,使3D景物投影在2D胶片上)
4.视口变换(决定胶片大小)
其中1、2并没有本质区别。
里面有几个关键性函数:
一几何变换
1.
voidglTranslated(
GLdoublex,
GLdoubley,
GLdoublez
);
voidglTranslatef(
GLfloatx,
GLfloaty,
GLfloatz
);
目标沿XYZ轴平移xyz
2.
voidglRotated(
GLdoubleangle,
GLdoublex,
GLdoubley,
GLdoublez
);
voidglRotatef(
GLfloatangle,
GLfloatx,
GLfloaty,
GLfloatz
);
目标分别以XYZ轴为轴逆时针旋转xyz
3.
voidglScaled(
GLdoublex,
GLdoubley,
GLdoublez
);
voidglScalef(
GLfloatx,
GLfloaty,
GLfloatz
);
目标在XYZ方向缩放,缩放因子为xyz
二投影变换
1.正射投影(即没有“近大远小”)
voidglOrtho(GLdoubleleft,GLdoubleright,GLdoublebottom,GLdoubletop,
GLdoublenear,GLdoublefar)
创建一个平行视景体(的矩阵),即投射线是平行线,把第一个矩形
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
视景投影到第二个矩形视景上。
并用这个矩阵乘以当前矩阵,以完成变换。
近景第一个矩形的左上角三维空间坐标(left,bottom,-near),右下角的三维空间坐标(right,top,-near);
远景第二个矩形左上角三维空间坐标(left,bottom,-far),右下角的三维空间坐标(right,top,-far);
2.透射投影(“近大远小”)
voidglFrustum(
GLdoubleleft,
GLdoubleright,
GLdoublebottom,
GLdoubletop,
GLdoubleznear,
GLdoublezfar
);
创建一个型如棱台的视景体,其近截取面由leftrightbottomtopznear
确定,远截取面由从视点投影近截取面到Z轴zfar位置决定
三视口变换
voidglViewport(GLintx,GLinty,GLsizewidth,GLsizeheight);
这个函数定义一个视口,x和y是视口在屏幕窗口坐标系中左上坐标
缺省是(0,0)。
widthheight是宽和高。
注意使用中视口长宽比例的调整会导致图象变形。
因此reshape()中
要检测窗口尺寸,修正视口大小,保证图象不变形。
附:
voidglMatrixMode(GLenummode);
把当前矩阵转换为指定的矩阵类型
这三个函数的利用见例子中的中文说明:
////////////////////////////////////////////////////////////
//sample.cpp
#include"glos.h"
#include
#include
#include"windows.h"
voidmyinit(void);
voidCALLBACKdisplay(void);
voidCALLBACKreshape(GLsizeiw,GLsizeih);
/////////////////////////////////////////////////////////////
voidmyinit(void)
{
/*初始化:
*/
auxInitDisplayMode(AUX_SINGLE|AUX_RGBA);
//窗口显示单缓存和RGB(彩色)模式
auxInitPosition(0,0,500,500);
//大小x=500y=500(0,0)是屏幕左上点
auxInitWindow("sample1");
//窗口初始化,参数是标题
glClearColor(0.0,0.0,0.0,0.0);
//将窗口清为黑色
glClear(GL_COLOR_BUFFER_BIT);
//将颜色缓存清为glClearColor命令所设置的颜色
//即背景色
}
//////////////////////////////////////////////////
voidCALLBACKreshape(GLsizeiw,GLsizeih)
{
//设定视口XY方向不要大于500单位
if(w<=500&&h<=500)
glViewport(0,0,w,h);
if(w>500&&h<=500)
glViewport(0,0,500,h);
if(w<=500&&h>500)
glViewport(0,0,w,500);
if(w>500&&h>500)
glViewport(0,0,500,500);
//进入世界坐标系,准备变换
//必要的步骤,初始化变换矩阵步骤:
1确定类型2清成单位阵
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//定义一个合适的视景体
if(w<=h)
glOrtho(-20.0,20.0,-20.0*(GLfloat)h/(GLfloat)w,
20.0*(GLfloat)h/(GLfloat)w,-50.0,50.0);
else
glOrtho(-20.0*(GLfloat)h/(GLfloat)w,
20.0*(GLfloat)h/(GLfloat)w,-20.0,20.0,-50.0,50.0);
//把变换结果返回视点坐标系
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
//////////////////////////////////////
//画三棱锥,功能同上一讲
voiddraw(void)
{
glBegin(GL_TRIANGLE_STRIP);
glColor3f(1.0,0.0,0.0);
glVertex3f(15.0,0.0,0.0);
glColor3f(0.0,1.0,0.0);
glVertex3f(-15.0,0.0,0.0);
glColor3f(0.0,0.0,1.0);
glVertex3f(0.0,15.0,15.0);
//
glColor3f(0.0,1.0,1.0);
glVertex3f(10.0,15.0,-15.0);
//
glColor3f(1.0,1.0,0.0);
glVertex3f(15.0,0.0,0.0);
//
glEnd();
}
////////////////////////////////////////
voidCALLBACKdisplay(void)
{
//按上个例子中方法画第一个三棱锥
glClearColor(0.0,0.0,0.0,1.0);
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0.0,0.0,-25.0);
draw();
//开始画第二个三棱锥
//变换开始,清变换矩阵为单位阵
glLoadIdentity();
//先沿XYZ平移-5-10-4,屏幕上看就是向左下移动
//Z方向由于是平行投影,没有“近大远小”所以看不出效果
glTranslatef(-5.0,-10.0,-4.0);
//再沿Z轴转90度
glRotatef(90,0.0,0.0,1.0);
draw();
//绘图工作完成,强制绘图结束
glFlush();
}
voidmain(void)
{
myinit();
auxReshapeFunc(reshape);
auxMainLoop(display);
}
//sampleendshere
/////////////////////
如果大家运行一下就知道上述函数的作用,无非是把物体移动、转动、变形缩放、透视...
我想有了前3讲,OPENGL的基本原理已经如此了它就是提供了一个标准的计算机图形学所使用的数学模型到显示的接口,只要具备图形学知识,掌握OPENGLAPI函数使用,绘图很EASY啦基础部分已经完毕,慢慢再来谈高级一些的绘图功能如:
纹理、光源、动画...
真诚希望大家提提意见和高论
第四讲
前面三篇文章已经把OPENGL的编程基本结构描述完毕。
以后会在这个基础上逐渐深化,不断增添新内容。
这一篇是讲述键盘操作和动画基础(实际还差的远哪)。
只是个简单的能由用户控制的动画,让物体前后移动,左右旋转。
是我们自己的第一个QUAKE!
当然这个版本谁买谁上当,呵呵。
这篇的另一个目的就是加深前面对于CALLBACK函数的认识以及对于变换的直观解释,任何变换你都可以从屏幕上通过自己的操作看出来:
我只把和以前变化的部分标记中文解释
////////////////////////////////////////////////////
//sample.cpp
#include"glos.h"
#include
#include
#include"windows.h"
voidmyinit(void);
voidCALLBACKdisplay(void);
voidCALLBACKreshape(GLsizeiw,GLsizeih);
//注意到乐吗?
这里新加乐4个CALLBACK函数
//类似display()reshape(),也由主函数调用它们
//实现对键盘输入的响应
voidCALLBACKleft(void);//按LEFT
voidCALLBACKright(void);//按RIGHT
voidCALLBACKup(void);//按UP
voidCALLBACKdown(void);//按DOWN
//两个全局变量,z_motion用来控制物体远近
//rotate用来控制物体旋转
staticintz_motion=0,rotate=0;
voidmyinit(void)
{
auxInitDisplayMode(AUX_SINGLE|AUX_RGBA);
auxInitPosition(0,0,500,500);
auxInitWindow("sample1");
glClearColor(0.0,0.0,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT);
}
voidCALLBACKr