C# 读取8位位图后,保存的颜色值为一个索引号是什么,如何根据这个0-255之间的索引号是什么获取对应的颜色值RGB呢

PHOTOSHOP 教 案
CorelDRAW 教 案
IndesignCS2
InDesignAdobe
本单元作业安排:
1、书籍或杂志封面&&& 2、报纸排版&&& 3、广告排版
根据作业情况从以下几个方面进行讲评:
1、构图清晰简洁,传达能力强。
2、布图新颖,色块运用得当,页面完美统一。
3、页面分割清晰,规整而又不呆板,灵活运用线条、块面等几何元素。
4、排版方法是否得当。
5、软件技巧的运用。
6、学生学习方法及本单元课的平时表现。(100分求救,有全部代码),24位位图转换成8位位图
[问题点数:100分,结帖人Stefine]
(100分求救,有全部代码),24位位图转换成8位位图
[问题点数:100分,结帖人Stefine]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
相关帖子推荐:
2006年4月 荣获微软MVP称号2007年4月 荣获微软MVP称号
2005年12月 VC/MFC大版内专家分月排行榜第一2005年11月 VC/MFC大版内专家分月排行榜第一2005年10月 VC/MFC大版内专家分月排行榜第一2005年9月 VC/MFC大版内专家分月排行榜第一2005年7月 VC/MFC大版内专家分月排行榜第一
2006年4月 荣获微软MVP称号2007年4月 荣获微软MVP称号
2005年12月 VC/MFC大版内专家分月排行榜第一2005年11月 VC/MFC大版内专家分月排行榜第一2005年10月 VC/MFC大版内专家分月排行榜第一2005年9月 VC/MFC大版内专家分月排行榜第一2005年7月 VC/MFC大版内专家分月排行榜第一
2004年9月 总版技术专家分月排行榜第二
2009年10月 荣获微软MVP称号2005年9月 荣获微软MVP称号2008年10月 荣获微软MVP称号2007年10月 荣获微软MVP称号2006年10月 荣获微软MVP称号
本帖子已过去太久远了,不再提供回复功能。当前访客身份:游客 [
一切都是简单的,你觉得困难只是因为你还未发觉它是简单的!
:引用来自“安度”的评论位图不只是2位的非黑即白...
:位图不只是2位的非黑即白吧,还有8位的位图(调色板...
:请问大神,8位索引图,如何做成纹理?
:请问大神,8位索引图,如何做成纹理?
:有帮助啊!!!原来轮廓是这样实现的。其实都要画...
:引用来自“btyam19”的评论大神您好,在下拜读了...
:引用来自“btyam19”的评论大神您好,在下拜读了...
:大神您好,在下拜读了您的文章"EGL工作笔记" 了解...
:引用来自“loh”的评论我也是按照你这个作下来的...
今日访问:39
昨日访问:292
本周访问:562
本月访问:562
所有访问:45469
列表模式: |
glEnable(GL_LIGHTING);
这个函数调用告诉OpenGL使用材料属性和光照参数去决定每个顶点的颜色值。如果没有开启这个函数,则看到的物体是昏暗的。开启光照后会根据你的材料属性和光照参数计算顶点的颜色值。下图对比未开启光照和开启光照的效果。
(未开启光照)
(开启光照)
设置全局环境光
OpenGL提供了一种全局的环境光。这种光是一种向所有方向均匀发射的光源。它可以照明没有被光源直接照射的物体的背面。
在OpenGL中通过glLightModel这个函数来设置全局环境光。第一个参数是GL_LIGHT_MODEL_AMBIETN。
//明亮的白光GLfloat ambientLight[] = {1.0f, 1.0f, 1.0f, 1.0f};//开启光照glEnable(GL_LIGHTING);//设置全局环境光为白光glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight);
全局环境光照的默认RGBA值是(0.2, 0.2, 0.2, 1.0),这个值是比较昏暗的。
设置材料属性
设置好了全局环境光源后,我们需要为设置材料属性。有两种设置材料属性的方式,第一种是在指定多边形之前调用glMaterial函数。例子:
//灰色的材料属性GLfloat gray[] = {0.75f, 0.75f, 0.75f, 1.0f};glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gray);glBegin(GL_TRIANGLES);
glVertex3f(-15.0f, 0.0f, 30.0f);
glVertex3f(0.0f, 15.0f, 30.0f);
glVertex3f(0.0f, 0.0f, -56.0f);glEnd(); glMaterialfv的第一个参数是指定正面, 反面, 正面和反面。第二个参数是告诉Opengl设置哪个属性。GL_AMBIENT_AND_DIFFUSE指定环境光反射属性和漫反射属性为同一个值。最后一个参数是一个RGBA值的数组。
在绝大多数情况下,环境光和漫反射光的成分是相同的,除非需要特殊的镜面光的效果。在多边形顶点之前需要调用glMaterial函数,并设置一个包含各种颜色成分的数组。这个操作还是相当繁琐的。下面介绍一种更简单的方式
第二种方法颜色追踪较为简单。使用颜色追踪,可以通过glColor来设置材料属性。第一步启用颜色追踪
glEnable(GL_COLOR_MATERIAL);
然后通过glColorMaterial指定需要颜色追踪的材料属性。下面的例子的设置 追踪多边形正面的环境光和漫反射的材料属性。
glColorMaterial(GL_FRONT, GL_AMBIENT_ANDDIFFUSE);
下面的例子使用颜色追踪的方式重写上面的例子:
//开启颜色追踪glEnable(GL_COLOR_MATERIAL);//设置颜色追踪的材料属性以及多边形的面glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);//设置颜色glColor3f(0.75f, 0.75f, 0.75f);glBegin(GL_TRIANGLES);
glVertex3f(-15.0f, 0.0f, 30.0f);
glVertex3f(0.0f, 15.0f, 30.0f);
glVertex3f(0.0f, 0.0f, -56.0f);glEnd();
设置环境的例子:
void SetupRC(){
GLfloat ambientLight[] = {1.0f, 1.0f, 1.0f, 1.0f};
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_LIGHTING);
//设置光照模型
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight);
//设置材料追踪
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glClearColor(0.0f, 0.0f, 05.f,1.0f);} 开启明亮的环境光效果:
把环境光调成原来的一半的效果:
GLfloat ambientLight[] = {0.5f, 0.5f, 0.5f, 1.0f};
完整的例子:#include "gltools.h"
// System and OpenGL Stuff// Rotation amountsstatic GLfloat xRot = 0.0f;static GLfloat yRot = 0.0f;// Called to draw scenevoid RenderScene(void){
// Clear the window with current clearing color
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Save matrix state and do the rotation
glPushMatrix();
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_TRIANGLES);
glVertex3f(-15.0f, 0.0f, 30.0f);
glVertex3f(15.0f, 0.0f, 30.0f);
glVertex3f(0.0f, 0.0f, 60.0f);
glColor3f(0.0f, 0.0f, 0.0f);
glVertex3f(-15.0f, 0.0f, 30.0f);
glVertex3f(0.0f, 0.0f, 60.0f);
glVertex3f(0.0f, 15.0f, 30.0f);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 60.0f);
glVertex3f(15.0f, 0.0f, 30.0f);
glVertex3f(0.0f, 15.0f, 30.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(-15.0f, 0.0f, 30.0f);
glVertex3f(0.0f, 15.0f, 30.0f);
glVertex3f(0.0f, 0.0f, -53.0f);
glColor3f(1.0f, 1.0f, 0.0f);
glVertex3f(0.0f, 15.0f, 30.0f);
glVertex3f(15.0f, 0.0f, 30.0f);
glVertex3f(0.0f, 0.0f, -53.0f);
glColor3f(0.5f, 0.5f, 0.3f);
glVertex3f(15.0f, 0.0f, 30.0f);
glVertex3f(-15.0f, 0.0f, 30.0f);
glVertex3f(0.0f, 0.0f, -53.0f);
glColor3f(0.3f, 1.0f, 0.2f);
glVertex3f(0.0f, 0.0f, -53.0f);
glVertex3f(0.0f, 0.0f, -70.0f);
glVertex3f(0.0f, 15.0f, -70.0f);
glColor3f(0.7f, 0.5f, 0.3f);
glVertex3f(-15.0f, 0.0f, -70.0f);
glVertex3f(0.0f, 0.0f, -53.0f);
glVertex3f(0.0f, 0.0f, -70.0f);
glColor3f(0.2f, 0.2f, 0.8f);
glVertex3f(15.0f, 0.0f, -70.0f);
glVertex3f(0.0f, 0.0f, -70.0f);
glVertex3f(0.0f, 0.0f, -53.0f);
//由于背面被消除,背面再画一次
glColor3f(0.3f, 1.0f, 0.2f);
glVertex3f(0.0f, 0.0f, -70.0f);
glVertex3f(0.0f, 0.0f, -53.0f);
glVertex3f(0.0f, 15.0f, -70.0f);
glColor3f(0.7f, 0.5f, 0.3f);
glVertex3f(0.0f, 0.0f, -53.0f);
glVertex3f(-15.0f, 0.0f, -70.0f);
glVertex3f(0.0f, 0.0f, -70.0f);
glColor3f(0.2f, 0.2f, 0.8f);
glVertex3f(0.0f, 0.0f, -70.0f);
glVertex3f(15.0f, 0.0f, -70.0f);
glVertex3f(0.0f, 0.0f, -53.0f);
glColor3ub(128,128,128);
glVertex3f(0.0f,2.0f,27.0f);
glVertex3f(-60.0f, 2.0f, -8.0f);
glVertex3f(60.0f, 2.0f, -8.0f);
glColor3ub(64,64,64);
glVertex3f(60.0f, 2.0f, -8.0f);
glVertex3f(0.0f, 7.0f, -8.0f);
glVertex3f(0.0f,2.0f,27.0f);
glColor3ub(192,192,192);
glVertex3f(60.0f, 2.0f, -8.0f);
glVertex3f(-60.0f, 2.0f, -8.0f);
glVertex3f(0.0f,7.0f,-8.0f);
glColor3ub(64,64,64);
glVertex3f(0.0f,2.0f,27.0f);
glVertex3f(0.0f, 7.0f, -8.0f);
glVertex3f(-60.0f, 2.0f, -8.0f);
glPopMatrix();
// Display the results
glutSwapBuffers();}// This function does any needed initialization on the rendering// context. void SetupRC(){
GLfloat ambientLight[] = {0.5f, 0.5f, 0.5f, 1.0f};
glEnable(GL_DEPTH_TEST);
// Hidden surface removal
glEnable(GL_CULL_FACE);
// Do not calculate inside of jet
glFrontFace(GL_CCW);
// Counter clock-wise polygons face out
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_LIGHTING);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
// Nice light blue
glClearColor(0.0f, 0.0f, 05.f,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;
// Refresh the Window
glutPostRedisplay();}void ChangeSize(int w, int h){
GLfloat nRange = 80.0f;
// Prevent a divide by zero
if(h == 0)
// Set Viewport to window dimensions
glViewport(0, 0, w, h);
// Reset coordinate system
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Establish clipping volume (left, right, bottom, top, near, far)
if (w &= h)
glOrtho (-nRange, nRange, -nRange*h/w, nRange*h/w, -nRange, nRange);
glOrtho (-nRange*w/h, nRange*w/h, -nRange, nRange, -nRange, nRange);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();}int main(int argc, char* argv[]){
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(800,600);
glutCreateWindow("Jet");
glutReshapeFunc(ChangeSize);
glutSpecialFunc(SpecialKeys);
glutDisplayFunc(RenderScene);
SetupRC();
glutMainLoop();
return 0;}
发布于 2年前,
阅读(395) | 评论(0) |
投票(0) | 收藏(2)
现实中的颜色
如下图:我们为物体构造好了模型,也为每个平面设置了颜色值,但最终画出来的物体却一点都不像真实世界中的物体。在真实世界中物体的表面最终呈现的颜色还取决于你观察它的角度以及光源。
OpenGL提供了近似于真实世界的光照环境。在OpenGl中物体会被三种光照射:环境光,漫放射光,镜面光。通过调整这三种光源的属性,我们可以使得物体看起来更真实。
环境光没有特定的方向。环境光的光线充满着整个场景。场景中的物体都被环境光从各个方向照射着。环境光的特点是:照射在物体上的光来自周围各个方向,又均匀地向各个方向反射。
漫放射光是一组来自特定方向,具有方向性的光。根据入射光线的角度在表面上均匀地向各个方向反射。漫反射的特点是:光源来自一个方向,反射光均匀地射向各个方向。漫反射光采用点光源照射物体。点光源是位于空间某个位置的一个点,向周围所有的方向上辐射等光强的光。在点光源的照射下,物体表面的不同部分亮度不同,亮度的大小依赖于它的朝向以及它与点光源之间的距离。
镜面光与漫反射光一样是具有方向性的。高强度的镜面光会在被照射的物体的表面上形成亮点。对于理想的高光泽度反射面,反射角等于入射角时,光线才会被反射,即只有在等于入射角的反射角方向上,观察者才能看到反射光。对于这种理想的反射面,镜面反射的光强要比环境光和漫反射的光强高出很多倍,这时,如果观察者正好处在P点的镜面反射方向上,就会看到一个比周围亮得多的高光点。
事实上,光源是由各种强度不同的类型的光组成的。场景中的光源是由3种光组成的:环境光,漫反射光,镜面光。每种光的成分也像颜色一样是由RGBA值定义的。它描述了组成这种成分的红光,绿光,蓝光的强度(对于光的颜色而言,alpha值被忽略)。
红色射线的光源成分分布:
红色射线不包含绿光和蓝光成分。这些光源成分的强度取值范围在0.0到1.0之间。上面的表格说明了红色射线包含非常高的镜面光的成分,一点漫放射光成分,和非常少的环境光成分。(注:镜面光的强度要大于其他成分才可能产生亮点).在被红色光照射的地方你可能(取决于观察点是否在光反射的方向上)会看到一个很亮的红点。
现实世界的材料
材料的颜色是根据它所放射的光的波长来定义的。一个蓝色的球体反射了绝大部分的蓝色光子且吸收了其他颜色的光子。在现实世界中白光包含了所有颜色的光,所以我们能够看到材料它本身的颜色。如果蓝色的球体在单纯的红光照射下,我们看到的将是黑色的。
材料的属性
当我们使用光照时,我们是通过材料的反射属性来描述它的颜色的。我们会说一个多边形反射了绝大多数的红光,而不会说这个多边形是红色的。我们为三种光源:环境光,漫放射光,镜面光,指定材料相应的反射属性。一个材料可能会很好的反射镜面光,却吸收了大多数的漫放射光和环境光。
给材料添加光照
物体有它自己反射的颜色,光源也有它自己的颜色。OpenGL是如何决定使用哪种颜色的?
环境光的效果
若光源颜色为( LR , LG , LB ),材质颜色为( MR , MG , MB ),最终颜色为( LR*MR , LG*MG , LB*MB )。(B1+B2)*MB )。因此,材料的颜色成分决定了入射光被反射的百分比。例子:
光源颜色为(0.5, 0.5, 0.5), 材料的颜色为(0.5, 1.0, 0.5)那么最终的颜色是
(0.5*0.5, 0.5*1.0, 0.5*0.5) = (0.25, 0.5, 0.25) 。
ambient = Ka x globalAmbient
漫反射光与镜面光的效果
漫反射光具有方向性,而且照射在物体表面上光的强度与光源和表面的夹角、光源和表面的距离以及相关的衰减因素有关(在光源和物体之间是否有雾)。
反射光的计算通常用Lambert定律:
Ildiff = Kd * Il * Cos(θ)其中Il是点光源强度,θ是入射光方向与顶点法线的夹角,称入射角(0&=A&=90°),Ildiff是漫反射体与方向光交互反射的光强,若 N为顶点单位法向量,L表示从顶点指向光源的单位向量(注意顶点指向光源),则Cos(θ)等价于dot(N,L),故又有:Ildiff = Kd * Il * dot(N,L)
镜面光的计算方法比较复杂。最常用的计算方法是:先通过光线的方向向量和物体的平面法向量来计算出光线的反射方向向量。然后再用光线的反射方向向量和视线的方向向量做点积来计算出光线对眼睛的作用强度。再把这个计算得到的作用强度以光泽度值作乘方运算来增加高光部分的汇聚性,减少扩散。最后再把计算结果乘上光线本身的颜色。
冯氏模型公式如下:
spec = Ks * Il * ( dot(V,R) )^Ns其中Ks 为镜面反射系数,Ns是高光指数,V表示从顶点到视点的观察方向,R代表反射光方向。由于反射光的方向R可以通过入射光方向L(从顶点指向光源)和物体的法向量求出,R + L = 2 * dot(N, L) * N& 即 R = 2 * dot(N,L) * N - L所以最终的计算式为:Ispec = Ks * Il * ( dot(V, (2 * dot(N,L) * N – L ) )^Ns
发布于 2年前,
阅读(320) | 评论(1) |
投票(0) | 收藏(10)
什么是颜色
颜色只是某种波长的光。现实中我们看到几乎都是由许多不同类型的光组合而成的。波长是光波相邻的波峰或相邻的波谷之间的距离。
人肉眼可见的光波的波长位于390nm(紫色光)到720nm(红色光)之间。紫外线和红外线都是人肉眼不可见的光。
事实上白色和黑色并不是一种颜色。黑色是没有颜色,因为可见光都被材料吸收了。而白色则是材料均匀的反射各种波长的光。
光即是波也是粒子(波粒二象性)。人之所以看到颜色,是因为光子刺激视网膜上数以百万计的锥细胞,导致神经能量传递到大脑中,大脑把把这种信息解释为光和颜色,光的亮度越大,撞击锥细胞的光子就越多。物体会反射一些光子并吸收一些光子。人眼看到纯蓝色的物体,是因为该物体反射了蓝色波长的光,吸收了其他颜色其他波长的光。如果人眼接受到的光的波长是均匀分布的,相当于看到了白色。
计算机如何产生颜色
计算机监视器在设计时就用来产生三种颜色的光,每种光的强度在一个可变的范围内。计算机监视器(CRT)的后面,有一个电子枪在屏幕后面发射电子,屏幕上含有磷,当电子撞击屏幕时,能够发射红、绿、蓝光。这三种颜色的磷紧密包装在一起作为屏幕上的一个物理点(像素)。它所发射的光的强度因电子束的强度而异。
在OpenGL中使用颜色
在OpenGL中我们分别指定红、绿、蓝成分的强度来确定一个颜色。
颜色立方体
由于一种颜色是通过三个非负的颜色值指定的,我们可以对所有颜色进行建模,形成一个RGB颜色空间。颜色空间以红、绿、蓝为轴,红、绿、蓝的坐标就像x、y、z的坐标。在坐标(0,0,0)处就代表所红、绿、蓝颜色的强度都为0,即黑色。在坐标(255,255,255)红、绿、蓝颜色的强度达到最大值,为白色。不同的灰色值,就分布在(0,0,0)到(255,255,255)这条立方体的对角线上。
在OpenGL中设置颜色的函数原型如下:
void glColor (red, green, blue, alpha);
其中x代表参数个数,可以是3个值代表rgb颜色,或者使4个值分别代表rgba。alpha成分用来代表透明度。t代表参数的类型,可以是b,d,f,i,s,ub,ui,us对应的类型是byte,double,float,integer,short,unsigned byte, unsigned integer,unsigned short。glColor3f指定各个颜色成分的强度值的范围为0.0到1.0之间。glColor3ub则可以指定各个颜色成分的强度值的范围在0-255之间。
glColor函数设置了一个颜色值,之后的图元都使用这个颜色值着色。如果我们为图元的顶点指定了不同的颜色值,那么这个图元用什么颜色来着色呢?
如果图元是点,那么每个点可以使用其指定的颜色来着色。如果图元是有两个不同颜色的顶点指定的一条线,线的颜色要根据着色的模式来着色,如果是使用GL_FLAT模式,则使用最后一个顶点的颜色来着色。如果使用的是GL_SMOOTH平滑着色的模式,则进行平滑插值。在RGB颜色空间中的任意两个点,都能连成一条直线。平滑着色即是使用这条线上的颜色来对图元内的点进行填着色。无论你的多边形有多复杂,OpenGL都能够正确地为每个点着色。OpenGL已经帮我们实现了这种算法,我们不需要关心。
示例:画一个平滑着色的三角形
static void RenderScene(){
glClear(GL_COLOR_BUFFER_BIT);
glShadeModel(GL_SMOOTH);
// Draw the triangle
glBegin(GL_TRIANGLES);
// Red Apex
glColor3ub((GLubyte)255,(GLubyte)0,(GLubyte)0);
glVertex3f(0.0f,200.0f,0.0f);
// Green on the right bottom corner
glColor3ub((GLubyte)0,(GLubyte)255,(GLubyte)0);
glVertex3f(200.0f,-70.0f,0.0f);
// Blue on the left bottom corner
glColor3ub((GLubyte)0,(GLubyte)0,(GLubyte)255);
glVertex3f(-200.0f, -70.0f, 0.0f);
glutSwapBuffers();}
发布于 2年前,
阅读(702) | 评论(0) |
投票(0) | 收藏(8)
为了更强大的功能和灵活性,我们有时需要直接操作矩阵。在OpenGL中4x4的矩阵用包含16个浮点数值的一维数组来表示,而不是用二维的4x4的数组来表示。OpenGL之所以这么做,因为使用一维数组更高效。当然OpenGL也支持二维数组的表示方式。而且要特别注意的是在矩阵中是使用列主序遍历数组的,即按列逐个遍历数组中的元素。
事实上,这个矩阵里的16个值代表着空间中的一个特定的位置和三个轴的朝向(相对于视点坐标系)。前3列是方向向量分别代表着3个轴的朝向(绝大多数情况下,这3个向量是正交的),第四列用于平移变换、glTranslate函数就是把数值填到这一列中。这个4x4的矩阵相当于一个坐标系统的位置和方向,如果把一个顶点(用列向量的形式)与这个矩阵相乘,得到的结果是一个变换到该坐标系统的新顶点。这意味着空间中的任意一个点和方向,能够用唯一的4x4的矩阵表示。如果你把物体中的所有顶点都乘以这个矩阵,那么你就是把整个物体变换到空间中指定的位置和朝向(我的理解是可以用这个矩阵所代表的坐标系统来表示你的整个物体)。
PS:注意最后一行的元素除了最后一个为1之外,其余为0。
你可以使用下面的两个函数来加载你的列主序的矩阵到投影矩阵,模型视图矩阵或者纹理矩阵栈中。
glLoadMatrixf(GLfloat* m);
glLoadMatrixd(GLdouble* m);
绝大多数的OpenGL的实现是使用单精度的浮点数来计算管道中的数据的。使用双精度的形式会带来一定的性能开销。
下面的代码相当于调用glLoadIdentity函数。
// 加载单位矩阵GLfloat m[] = { 1.0f, 0.0f, 0.0f, 0.0f,&&&&& // X 列0.0f, 1.0f, 0.0f, 0.0f,&&&&& // Y 列0.0f, 0.0f, 1.0f, 0.0f,&&&&& // Z 列0.0f, 0.0f, 0.0f, 1.0f };&&& // 平移列glMatrixMode(GL_MODELVIEW);glLoadMatrixf(m);
相对应的OpenGL还提供了加载行主序的矩阵的两个函数
void glLoadTransposeMatrixf(GLfloat *m);
void glLoadTransposeMatrixd(GLdouble *m);
手工执行变换
一个高级的例子:
1: void RenderScene(void)
M3DMatrix44f
transformationM
//保存旋转矩阵
static GLfloat yRot = 0.0f;
// 旋转的角度
yRot += 0.5f;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 构造一个矩阵
m3dRotationMatrix44(transformationMatrix, m3dDegToRad(yRot), 0.0f, 1.0f, 0.0f);
transformationMatrix[12] = 0.0f;
transformationMatrix[13] = 0.0f;
transformationMatrix[14] = -2.5f;
DrawTorus(transformationMatrix);
glutSwapBuffers();
其中m3dRotationMatrix44是构造一个旋转的矩阵,相当于glRotatef(yRot, 0.0f, 1.0f, 0.0f)函数。
transformationMatrix[12] = 0.0f;transformationMatrix[13] = 0.0f;transformationMatrix[14] = -2.5f;
这三个是执行平移变换.相当于glTranslatef(0.0f, 0.0f, –2.5f); m3dRotationMatrix44函数如下
1: void m3dRotationMatrix44(M3DMatrix44f m, float angle, float x, float y, float z)
float mag, s,
float xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c;
s = float(sin(angle));
c = float(cos(angle));
mag = float(sqrt( x*x + y*y + z*z ));
// Identity matrix
if (mag == 0.0f) {
m3dLoadIdentity44(m);
// Rotation matrix is normalized
#define M(row,col)
m[col*4+row]
one_c = 1.0f -
M(0,0) = (one_c * xx) +
M(0,1) = (one_c * xy) -
M(0,2) = (one_c * zx) +
M(0,3) = 0.0f;
M(1,0) = (one_c * xy) +
M(1,1) = (one_c * yy) +
M(1,2) = (one_c * yz) -
M(1,3) = 0.0f;
M(2,0) = (one_c * zx) -
M(2,1) = (one_c * yz) +
M(2,2) = (one_c * zz) +
M(2,3) = 0.0f;
M(3,0) = 0.0f;
M(3,1) = 0.0f;
M(3,2) = 0.0f;
M(3,3) = 1.0f;
最终的效果如下:
手动执行变换在碰撞检测,平截头体剔除,以及一些特效算法中会用到。
使用相机和角色在OpenGL中移动
在场景中移动的物体成为角色,就像舞台剧上的演员一样。角色有他们自己的变换,不仅仅是相对于世界坐标系(视点坐标系)的变换,也可以相对于其他角色坐标系的变换。每个角色都有自己的参考帧和自己的坐标系(物体坐标系)。在物体坐标系和世界坐标系之间的转换是非常有用的。
一个简单灵活的表示角色的方式是用一个包含一个空间中的位置,一个指向前面的向量以及一个指向上面的向量(第三个向量可以通过计算得到)。使用这些量就可以唯一地标识空间中一个特定的位置和方向。
typedef float M3DMatrix44f[16];
// A 4 X 4 matrix, column major (floats) - OpenGL style
class GLFrame{protected:
M3DVector3f vL
M3DVector3f vUp;
M3DVector3f vFpublic:…};
使用这样的一个参考帧来表示一个物体的位置和方向是非常有用的。我们可以使用这些数据直接创建一个4x4的变换矩阵。其中向上的向量代表y列向量,向前的向量代表z列向量,位置则代表移动列向量。这样只缺少了x向量。因为我们知道这3个轴是互相垂直的,因此可以有由y和z向量的叉乘来计算x列向量。
void GLFrame::GetMatrix(M3DMatrix44f mMatrix, bool bRotationOnly = false){
//计算列向量,叉乘
M3DVector3f vXA
m3dCorssProduct(vXAxis, vUp, vForward);
//把各个向量转换为矩阵的列向量, X列
m3dSetMatrixColumn44(matrix, vXAxis, 0);
matrix[3] = 0.0f;
m3dSetMatrixColumn44(matrix, vUp, 1);
matrix[7] = 0.0f;
m3dSetMatrixColumn44(matrix, vForward, 2);
matrix[11] = 0.0f;
//只包含旋转不移动
if(bRotationOnly = true)
matrix[12] = 0.0f;
matrix[13] = 0.0f;
matrix[14] = 0.0f;
m3dSetMatrixColumn44(matrix, vOrigin, 3);
matrix[15] = 1.0f;}
欧拉角表示法
参考《3D数学基础_图形与游戏开发》
欧拉角的基本思想是讲角位移分解为三个互相垂直轴的三个旋转组成的序列。
以下都是使用左手法则。“heading-pitch-bank” heading为绕y轴的旋转量,绕惯性坐标系y轴的旋转。向右旋转为正。pitch为绕x轴的旋转量。物体坐标系的x轴,不是原惯性坐标系的x轴。向下旋转为正方向。bank为绕z轴的旋转量。物体坐标系的z轴。逆时针为正方向。
PS:当我们说的旋转顺序是"heading-pitch-bank”时,是指从惯性坐标系到物体坐标系。如果从物体坐标系变换到惯性坐标系,旋转的顺序就是相反的
关于欧拉角的其他约定
一组常用的术语是roll-pitch-yaw,其中roll等价于bank, yaw基本上等价于heading。它的顺序和heading-pitch-bank的顺序相反。它定义了向量从物体坐标系到惯性坐标系的变换旋转顺序。(事实上,yaw和heading还是有技术上的差别,yaw是绕物体坐标系y轴的旋转,heading是绕惯性坐标系y轴的旋转。,因为这里的旋转是在物体坐标系y轴和惯性坐标系y轴重合是进行的,所以这个区别并不重要)
任意三个坐标轴都能作为旋转轴。
决定每个旋转的正方向时不一定必须遵守左手或右手法则。
旋转可以以不同的顺序进行。但heading-pitch-bank顺序最为实用。heading度量绕竖直轴的旋转,它之所以有意义主要是因为我们所在的环境经常有某种形式的“地面”,一般料将绕惯性坐标系的x或z轴的旋转没有什么意义。pitch度量水平方向的倾角,bank度量的是绕z轴的旋转量
欧拉角的优点
欧拉角容易使用,它用三个数来代表绕三个轴旋转的角度。角度符合人类的思维习惯。heading-pitch-bank系统就能直接地描述出偏差的角度。当需要显示方位或键盘输入方位时,欧拉角是唯一的选择。
最简洁的表达方式
任意三个数都是合法的。
欧拉角的缺点
给定方位的表达方式不唯一
两个角度间求插值非常困难
将一个角度加上360的倍数,并不会改变方位。
由三个角度不互相独立而导致。如先heading45再pitch90,这与先pitch90再bank45是等价的。
解决方法:讲heading和bank限制在+180到-180之间,pitch限制在+90到-90之间。这种现象,角度为+-90的第二次旋转将使得第一次和第三次旋转的旋转轴相同,称作万向锁。为了消除限制欧拉角的这种别名现象,规定万向锁情况下由heading完成绕竖直轴的全部旋转。
欧拉角总结
照相机管理
OpenGL中并不真正存在照相机变换。相机作为一个隐喻,帮助我们理解如何管理3D环境中的视点。照相机可以想象为一种物体,在空间中具有某个位置和特定方向。
应用照相机变换,我们要使用照相机的角色变换并进行反转。这样当我们把相机向后移时就相当于向前移动整个场景。向左旋转相机则相当于向右旋转整个场景。
glu库中包含了一个函数用于创建相机变换,它使用的数据与上面定义的帧结构数据相同。
void gluLookAt(GLdouble eyex, GLdouble eyey, GLdouble eyez,GLdouble centerx, GLdouble centery, GLdouble centerz,GLdouble upx, GLdouble upy, GLdouble upz);
这个函数接受一个观察点的位置,一个在观察点正前方的一个点,以及向上的方向向量。
渲染一个指定的场景如下图:
gluPerspective和gluLookAt的关系参考。
源代码移步:
发布于 2年前,
阅读(1553) | 评论(0) |
投票(0) | 收藏(6)
变换包括:旋转,平移,缩放扭曲,投影。在指定了顶点之后,在屏幕上显示顶点之前,会进行三种变换:视图变换,模型变换,投影变换。
术语如下表:
视图(Viewing)
指定观察者和照相机的位置
模型(Modeling)
场景中的物体变换
模型视图(ModelView)
描述视图和模型变换的对偶性
投影(Projection)
改变可视区域的大小和形状
视口(Viewport)
一种伪变换,对窗口的最终输出进行缩放
视点坐标是观察者的视角,与发生的变换无关。视点坐标是一个虚拟的固定的坐标系,通常作为参考系使用。许多变换都是相对于视点坐标系而言的。
如下:左图是观察者的观察角度是垂直于显示屏幕,右图对视点坐标做了旋转,这样就能看到z轴的相对位置。
视图变换是场景所应用的第一个变换。它用于确定场景的拍摄点。默认情况下,在透视投影钟观察者是从原点向z轴负方向看过去。这个观察点相对于视点坐标系系统进行移动,以提供一个特定的拍摄点。当观察点位于原点时,场景中所绘制的z值为正的物体就位于观察者的后面。
视图变换允许吧观察点放在任意位置,并在任何方向上观察场景。确定视图变换就像在场景中放置照相机并让它指向某个方向。
模型变换可以操作模型以及模型中的物体。模型变换包括移动物体,旋转物体,缩放物体。下图分别是平移,旋转,缩放
模型变换的最终结果还取决于你的变换顺序。下图展示了先旋转后平移,和先平移后旋转所得到的不同结果。
导致这种最终结果不同的原因是,每一次变换都是基于上一次变换的结果的。上面的变换是物体坐标系的变换。
模型视图的对偶性
事实上,这模型变换和视图变换在最终呈现的效果上市一样的。之所以把这两种变换区分开来,是为了方便程序员。相对于坐标向后移动物体和相对于物体把坐标系往前移的效果是一样的。视图变换像是把模型变换应用于整个场景。即在应用视图变换之后,像是每个物体独立进行了模型变换。模型视图变换在变换管道中用一个矩阵来表示,即模型视图矩阵。
视图变换可以理解为在绘制物体之前,先把模型变换应用于观察者。当在场景中放置更多的物体时,需要重新指定新的变换。所有其他的变换都是以最初的那个变换作为参考系的。
在模型视图变换之后,投影变换才应用于顶点。投影变换定义了可视区域和裁剪面。裁剪面决定了几何图元是否能被观察者看到。投影变换决定了在所有变换做完之后的场景投影到屏幕上的最终图像。投影变换有两种:正交投影和透视投影
正交投影即平行投影,所有多边形按照指定的大小出现在屏幕上。直线和多边形使用平行直线直接映射到2D屏幕上。不管物体有多远,都按照相同的大小进行绘制。通常用于CAD或二维图像的渲染。
透视投影显示的场景则更加真实。远处的物体看上去比相同大小的近处物体小一些。
下图是正投影和透视投影的比较
正投影的方框
透视投影的方框:
在所有变换进行完之后,获得的是将被映射到屏幕上的某个窗口的场景的二维投影。这种映射到物理窗口坐标的变换是最后做的变换——视口变换。通常,在颜色缓冲区和窗口像素之间有一一对应的关系,但并不总是如此。
上图是简单的变换管道。首先你的顶点被转化为1x4的矩阵,头三个值是x,y,z坐标,第四个数值代表着缩放因子,通常为1不需要更改。然后顶点乘以模型视图矩阵,产生经过转换后的视点坐标。然后视点坐标乘以投影矩阵,得到裁剪坐标。OpenGL会剔除所有在裁剪空间外的数据。然后裁剪坐标进行透视除法 除以w坐标得到规格化的设备坐标,原因是w值在模型视图变换和投影变换的过程中可能会被改变。最终坐标通过视口变换映射到二维平面上。
模型视图矩阵
模型视图矩阵是一个4x4的矩阵,代表着你要放置的物体的位置和朝向的变换坐标。图元中的顶点(一个单列的矩阵)乘以模型视图矩阵得到变换过后的相对于视点坐标系系统的坐标。
void glTranslatef(GLfloat x, GLfloat y, GLfloat z);
这个函数有三个参数分别代表着向x,y,z轴平移。调用这个函数之后,OpenGL会构造一个适当的举证乘以当前矩阵栈栈顶的矩阵。例子:
//向Y轴平移十个单元
glTranslatef(0.0f, 10.0f, 0.0f);
//画立方体
gluWireCube(10.0f);
void glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
这个函数告诉OpenGL要沿着指定的坐标轴旋转多少角度。x,y,z为(1,0,0)则代表着沿x轴旋转,你可以沿着任意坐标轴旋转,用x,y,z值指定这个坐标轴。
// 沿着(1,1,1)坐标轴旋转45度
glRotatef(45.0f, 1.0f, 1.0f, 1.0f);glutWireCube(10.0f);
void glScalef(GLfloat x, GLfloat y, GLfloat z);
沿x,y,z轴缩放多少倍。例子:
glScalef(2.0f, 1.0f, 2.0f);
gluWireCube(10.0f);
对角线为1,除了对角线之外的元素为0的矩阵称为单位阵。单位阵,即所有的矩阵乘以单位阵都得到原矩阵。OpenGL中重新加载单位阵到模型视图矩阵(重置模型视图矩阵)可以用下面两个函数调用:
glMatrixMode(GL_MODEVIEW);
glLoadIdentity();
有时我们需要保存当前的变换状态,然后绘制物体,再恢复它。那我们可以使用OpenGL提供的矩阵栈来实现。
glPushMatrix可以保存当前矩阵到矩阵栈中,glPopMatrix从矩阵栈弹出矩阵。
矩阵栈有最大的存储数量。通过glGet(GL_MAX_MODELVIEW_STACK_DEPTH)可以获得模型视图矩阵栈的容量。glGet(GL_MAX_PROJECTION_STACK_DEPT)获得投影矩阵栈的容量。
void SetupRC()
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glEnable(GL_DEPTH_TEST);
void RenderScene()
static GLfloat fEffect = 0.0f;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//先往里平移画原子核
glTranslatef(0.0f, 0.0f, -70.0f);
//红色的原子核
glColor3f(1.0f, 0.0f, 0.0f);
glutSolidSphere(20.0f, 15, 15);
//青色的电子
glColor3f(0.0f, 1.0f, 1.0f);
glPushMatrix();
glRotatef(fEffect, 0.0f, 1.0f, 0.0f);
glTranslatef(80.0f, 0.0f, 0.0f);
glutSolidSphere(10.0f, 15, 15);
glPopMatrix();
//黄色的电子
glColor3f(1.0f, 1.0f, 0.0f);
glPushMatrix();
glRotatef(45.0f, 0.0f, 0.0f, 1.0f);
glRotatef(fEffect, 0.0f, 1.0f, 0.0f);
glTranslatef(80.0f, 0.0f, 0.0f);
glutSolidSphere(10.0f, 15, 15);
glPopMatrix();
//绿色的电子
glColor3f(0.0f, 1.0f, 0.0f);
glPushMatrix();
glRotatef(-45.0f, 0.0f, 0.0f, 1.0f);
glRotatef(fEffect, 0.0f, 1.0f, 0.0f);
glTranslatef(80.0f, 0.0f, 0.0f);
glutSolidSphere(10.0f, 15, 15);
glPopMatrix();
fEffect += 10.0f;
if (fEffect & 360.0)
fEffect = 0.0f;
glutSwapBuffers();
void ChangeSize(GLsizei w, GLsizei h)
GLfloat nRange = 200.0f;
if (h == 0)
//设置视口
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//设置可视区域
GLfloat aspect = (GLfloat)w / (GLfloat)h;
//设置为透视
gluPerspective(85.0, aspect, 80.0, 300.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
void Timer(
int value)
glutPostRedisplay();
glutTimerFunc(100, Timer, 1);
char* argv[])
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowSize(400,400);
glutCreateWindow(
"atom Test");
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
glutTimerFunc(100, Timer, 1);
SetupRC();
glutMainLoop();
更高级的例子:
static GLfloat whiteLight[] = {0.2f, 0.2f, 0.2f, 1.0f};
static GLfloat sourceLight[] = {0.8f, 0.8f, 0.8f, 1.0f};
static GLfloat lightPos[] = {0.0f, 0.0f, 0.0f, 1.0f};
void SetupRC()
//开启深度测试
glEnable(GL_DEPTH_TEST);
glFrontFace(GL_CCW);
//启用裁剪面
glEnable(GL_CULL_FACE);
//开启光照
glEnable(GL_LIGHTING);
//设置光照参数
glLightModelfv(GL_AMBIENT_AND_DIFFUSE, whiteLight);
glLightfv(GL_LIGHT0, GL_AMBIENT, sourceLight);
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
//开启光源LIGHT0
glEnable(GL_LIGHT0);
//设置材料属性
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
void RenderScene()
static GLfloat fEarth = 0.0f;
static GLfloat fMoon = 0.0f;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
//往屏幕里面平移
glTranslatef(0.0f, 0.0f, -300.0f);
//先关闭光照
glDisable(GL_LIGHTING);
glColor3ub(255, 255, 0);
glutSolidSphere(20.0, 15, 15);
//开启光照
glEnable(GL_LIGHTING);
//LIGHT0的发光点
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
glRotatef(fEarth, 0.0f, 1.0f, 0.0f);
glColor3ub(0,0,255);
glTranslatef(105.0f, 0.0f, 0.0f);
glutSolidSphere(15.0, 15, 15);
glColor3ub(200, 200, 200);
glRotatef(fMoon, 0.0f, 1.0f, 0.0f);
glTranslatef(30.0f, 0.0f, 0.0f);
glutSolidSphere(10.0, 15, 15);
//月球的旋转
fMoon += 15.0f;
if (fMoon & 360.0f)
fMoon = 0.0f;
//地球的旋转
fEarth += 10.0f;
if (fEarth & 360.0f)
fEarth = 0.0f;
glPopMatrix();
glutSwapBuffers();
void ChangeSize(GLsizei w, GLsizei h)
if (h == 0)
//设置视口
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//设置可视区域
GLfloat aspect = (GLfloat)w / (GLfloat)h;
gluPerspective(45.0, aspect, 1.0, 400.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
void TimerFunc(
int value)
glutPostRedisplay();
glutTimerFunc(100, TimerFunc, 1);
char* argv[])
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowSize(400,400);
glutCreateWindow(
"solar Test");
glutReshapeFunc(ChangeSize);
glutTimerFunc(100, TimerFunc, 1);
glutDisplayFunc(RenderScene);
SetupRC();
glutMainLoop();
发布于 2年前,
阅读(551) | 评论(0) |
投票(0) | 收藏(2)
使用目标缓冲区
OpenGL并不是直接在屏幕上绘制图元的,而是先渲染到缓冲区中,然后再交换到屏幕上。颜色缓冲区有两个一个是前颜色缓冲区,一个是后颜色缓冲区。OpenGL默认是在后颜色缓冲区中绘制,然后再通过glutSwapBuffers(或者操作系统的缓冲区交换函数)交换前后缓冲区。我们也可以直接在前缓冲区中进行绘制,这样我们可以看到一些绘制的动画效果。
使用前缓冲区的第一个方法是调用
void glDrawBuffer(GLenum mode);
mode参数为GL_FRONT,OpenGL就会在前缓冲区中进行绘制。
第二种方法是不适用双缓冲区机制。
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);这个是使用双缓冲区和RGB颜色缓冲区。
在glut中可以简单地忽略GLUT_DOUBLE参数。glutInitDisplayMode(GLUT_RGB);来实现只使用前缓冲区渲染。进行单缓冲区渲染时,如果希望绘制结果显示到屏幕上,需要调用glFlush();或glFinish();函数。
static void RenderScene(){
static GLdouble dRadius = 0.1;
static GLdouble dAngle = 0.0;
if (dAngle == 0.0)
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0f, 1.0f, 0.0f);
glBegin(GL_POINTS);
glVertex2f(dRadius * cos(dAngle), dRadius * sin(dAngle));
dRadius *= 1.01;
dAngle += 0.1;
if (dAngle & 30.0)
dRadius = 0.1;
dAngle = 0.0;
glFlush();}//定时绘制static void Timer(int value){
glutTimerFunc(50, Timer, 0);
glutPostRedisplay();}
操作深度缓冲区
在glut中请求深度缓冲区,只需在其初始化显示模式的时候,加上GLUT_DEPTH位标记:
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
开启深度测试:
glEnable(GL_DEPTH_TEST);
即使深度测试未被开启。opengl还是会往深度缓冲区中写深度值。有时候你需要暂时关掉往深度缓冲区中写值。可以调用:
void glDepthMask(GLboolean mask);
设置mask这个参数值为GL_FALSE来达到禁用往深度缓冲区里写值。使用GL_TRUE则重新启动往深度缓冲区中写值。
使用剪刀进行裁剪
一种提高渲染性能的方式是只渲染屏幕上产生变化的那一部分。你也许需要限制Opengl只渲染一小块矩形区域。OpenGL允许在窗口中指定一个裁剪矩形。打开裁剪测试
glEnable(GL_SCISSOR_TEST);
关闭裁剪测试glDisable(GL_SCISSOR_TEST);指定裁剪框,一窗口坐标(像素)形式指定。
void glScissor(GLint x, GLint y, GLsizei width, GLsizei height);
x和y指定了裁剪框的左下角坐标,width和height分别指定了宽带和高度。
static void RenderScene(){
glClear(GL_COLOR_BUFFER_BIT);
//设置裁剪框
glScissor(100, 100, 400, 400);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
//启用裁剪功能
glEnable(GL_SCISSOR_TEST);
glClear(GL_COLOR_BUFFER_BIT);
glScissor(200, 200, 200, 200);
glClearColor(1.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
glFlush();}
使用模板缓冲区
如果我们希望使用一个模板突然来渲染一个不规则的形状。在OpenGL中可以使用模板缓冲区。
首先我们需要请求一个模板缓冲区,在GLUT中可以使用GLUT_STENCIL位来初始化显示模式。
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_STENCIL);
在现代的OpenGL实现中,模板操作时被硬件加速过的。我们可以使用glEnable/glDisable来打开或关闭模板测试.
glEnable(GL_STENCIL_TEST);
当打开模板测试的时候,在通过模板测试的地方才会进行绘制。设置模板测试的函数:
void glStencilFunc(GLenum func, GLint ref, GLuint mask);
第一个参数func来设置比较函数。可以是GL_NEVER, GL_ALWAYS, GL_LESS, GL_GREATER, GL_LEQUAL, GL_EUQAL, GL_GEUQAL 和GL_NOTEQUAL。这个参数值告诉Opengl如何去和你传进来的第二个参数ref进行比较。另外你还可以设置掩码值mask。这个掩码值会和ref模板缓冲区中的值进行与操作。即mask先和这两个值进行与操作 后再进行比较。
创建模板图案
开始绘图之前,我们要先清除模板缓冲区。这与glClear函数清除颜色和深度缓冲区的方法是一样的,只不过使用的位掩码是GL_STENCIL_BUFFER_BIT。
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glClearStencil(GLint s);这个函数来设置清除模板缓冲区时使用的值。
在启用了模板测试之后,渲染命令就使用glStencilFunc函数的参数与存储在模板缓冲区中的值进行测试。根据模板测试的结果,来判断值应该被写入或被丢弃。在测试的过程中,模板缓冲区本身也可以被修改,进入模板缓冲区的值取决于glStencilOp函数设置的参数。
void glStencilOp(GLenum fail, Glenum zfail, GLenum zpass);
第一个参数告诉opengl,如果模板测试失败,模板缓冲区的值应该如何被修改。第二个参数和第三个参数则说明当通过模板测试且深度测试失败或当通过模板测试且深度测试通过时,模板缓冲区的值应该如何被修改。这些参数的合法值包括GL_KEEP(保持当前值), GL_ZERO(设置为0), GL_REPLACE(用参考值即glStencilFunc设置的ref值代替), GL_INCR(增加这个值), GL_DECR(减少这个值), GL_INVERT(将这个值进行反转),GL_INCR_WRAP(循环增加), GL_DECR_WRAP(循环减少).
static void RenderScene(){
GLdouble dRadius = 0.1;
//最初的螺旋半径
GLdouble dA
glClearColor(0.0f, 0.0f, 1.0f, 0.0f);
//使用0值来清除模板缓冲区
glClearStencil(0.0f);
glEnable(GL_STENCIL_TEST);
//清除模板缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//先初始化模板,让所有的模板测试都不通过,只是改变模板缓冲区的值
glStencilFunc(GL_NEVER, 0x0, 0x0);
glStencilOp(GL_INCR, GL_INCR, GL_INCR);
//用白线绘制螺旋模型
glColor3f(1.0f, 1.0f ,1.0f);
glBegin(GL_LINE_STRIP);
for (dAngle = 0; dAngle & 400.0; dAngle += 0.1)
glVertex2d(dRadius * cos(dAngle), dRadius * sin(dAngle));
dRadius *= 1.002;
//现在允许进行绘图,但不包含那些模板模型是0x1的地方
glStencilFunc(GL_NOTEQUAL, 0x1, 0x1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
//绘制红色的反弹方块
glColor3f(1.0f, 0.0f, 0.0f);
glRectf(x, y, x + rsize, y - rsize);
glutSwapBuffers();}
发布于 2年前,
阅读(1463) | 评论(0) |
投票(0) | 收藏(29)
OpenGL的GL_QUADS图元,可以指定4个点画出四边形。这些多边形具有顺时针环绕的方向。PS:四边形的四个角必须位于同一个平面
GL_QUADS_STRIP图元可以画四边形带。
通用多边形
GL_POLYGONS可以使用指定的所有点绘制一个多边形。PS:这些点必须在一个平面上
使用点画方式,填充多边形
开启点画模式
glEnagle(GL_POLYGON_STIPPLE);
设置填充的模型
glPolygonStipple(pBitmap);
pBitmap指定了一块数据区域。然后多边形就使用pBitmap(GLubyte*)所指定的模式进行填充。pBitmap指向的缓冲区是32*32的位模式。首先读入的是最高有效位(MSB)这与点画的方式刚好相反。
一个多边形的点画模式,篝火的图像。这个篝火图像的数据用32*32的数组存储
// Bitmap of campfireGLubyte fire[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x01, 0xf0,0x00, 0x00, 0x07, 0xf0, 0x0f, 0x00, 0x1f, 0xe0,0x1f, 0x80, 0x1f, 0xc0, 0x0f, 0xc0, 0x3f, 0x80,0x07, 0xe0, 0x7e, 0x00, 0x03, 0xf0, 0xff, 0x80,0x03, 0xf5, 0xff, 0xe0, 0x07, 0xfd, 0xff, 0xf8,0x1f, 0xfc, 0xff, 0xe8, 0xff, 0xe3, 0xbf, 0x70,0xde, 0x80, 0xb7, 0x00, 0x71, 0x10, 0x4a, 0x80,0x03, 0x10, 0x4e, 0x40, 0x02, 0x88, 0x8c, 0x20,0x05, 0x05, 0x04, 0x40, 0x02, 0x82, 0x14, 0x40,0x02, 0x40, 0x10, 0x80, 0x02, 0x64, 0x1a, 0x80,0x00, 0x92, 0x29, 0x00, 0x00, 0xb0, 0x48, 0x00,0x00, 0xc8, 0x90, 0x00, 0x00, 0x85, 0x10, 0x00,0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00 };
数组的第一行存储的是图像最后一行的数据,数组的最后一行存储的是图像的第一行数据。点画模式只能用于简单的多边形填充,因为其不会随多边形的变换而变换。示例如下
//篝火图像GLubyte fire[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x01, 0xf0,0x00, 0x00, 0x07, 0xf0, 0x0f, 0x00, 0x1f, 0xe0,0x1f, 0x80, 0x1f, 0xc0, 0x0f, 0xc0, 0x3f, 0x80,0x07, 0xe0, 0x7e, 0x00, 0x03, 0xf0, 0xff, 0x80,0x03, 0xf5, 0xff, 0xe0, 0x07, 0xfd, 0xff, 0xf8,0x1f, 0xfc, 0xff, 0xe8, 0xff, 0xe3, 0xbf, 0x70,0xde, 0x80, 0xb7, 0x00, 0x71, 0x10, 0x4a, 0x80,0x03, 0x10, 0x4e, 0x40, 0x02, 0x88, 0x8c, 0x20,0x05, 0x05, 0x04, 0x40, 0x02, 0x82, 0x14, 0x40,0x02, 0x40, 0x10, 0x80, 0x02, 0x64, 0x1a, 0x80,0x00, 0x92, 0x29, 0x00, 0x00, 0xb0, 0x48, 0x00,0x00, 0xc8, 0x90, 0x00, 0x00, 0x85, 0x10, 0x00,0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00 };static void RenderScene(){
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0f, 1.0f, 0.0f);
//开启多边形点画填充
glEnable(GL_POLYGON_STIPPLE);
//设置填充模式
glPolygonStipple(fire);
glBegin(GL_POLYGON);
glVertex2f(0.0f, 0.0f);
glVertex2f(50.0f, 0.0f);
glVertex2f(50.0f, 50.0f);
glVertex2f(0.0f, 50.0f);
glFlush();}
多边形的创建规则:多边形不能被扭曲,边也不能交叉。指定的一个多边形的顶点必须在一个面上。多边形必须是凸多边形。
凹多边形及边界
直接画凹多边形在OpenGL中是非法的,但可以使用多个多边形的拼接来实现绘制凹多边形。当我们使用填充模式的时候,看不出这些由多个多边形拼接起来的凹多边形的内部线条。但是当我们使用多边形画线模式glPolygonMode(GL_FRONT, GL_LINE)的时候会出现这些内部的线。此时我们可以通过OpenGL提供的glEdageFlag()来处理这些边的问题。当这个函数接受true值时,接下来的顶点都将作为多边形边界线的起点。例子:
//五角星的8个点static GLfloat points[16] ={-50.0f, 50.0f,0.0f, 125.0f,50.0f, 50.0f,125.0f, 0.0f,50.0f, -50.0f,0.0f, -125.0f,-50.0f, -50.0f,-125.0f, 0.0f};static GLboolean bFlag =static void RenderScene(){
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0f, 1.0f, 0.0f);
glPolygonMode(GL_FRONT, GL_LINE);
glPolygonMode(GL_BACK, GL_LINE);
glBegin(GL_TRIANGLES);
//五角星的内部两个三角形不是边界
glEdgeFlag(bFlag);
glVertex2f(points[0], points[1]);
glVertex2f(points[4], points[5]);
glVertex2f(points[12], points[13]);
glVertex2f(points[4], points[5]);
glVertex2f(points[8], points[9]);
glVertex2f(points[12], points[13]);
glEdgeFlag(true);
glVertex2f(points[0], points[1]);
glVertex2f(points[2], points[3]);
glEdgeFlag(bFlag);
glVertex2f(points[4], points[5]);
glVertex2f(points[4], points[5]);
glEdgeFlag(true);
glVertex2f(points[8], points[9]);
glVertex2f(points[6], points[7]);
glVertex2f(points[8], points[9]);
glVertex2f(points[10], points[11]);
glEdgeFlag(bFlag);
glVertex2f(points[12], points[13]);
glVertex2f(points[12], points[13]);
glEdgeFlag(true);
glVertex2f(points[0], points[1]);
glVertex2f(points[14], points[15]);
glutSwapBuffers();}
bFlag=true和bFlag=false的区别:
发布于 2年前,
阅读(293) | 评论(0) |
投票(0) | 收藏(3)
学习了画线的知识,我们可以使用GL_LINE_LOOP来画闭合的多边形。但是使用这种方式画出来的只有线框,多边形没有填充颜色。OpenGL支持绘制实心的多边形,并使用当前的颜色进行填充。
简单的三角形,需要指定三个顶点。
1: glBegin(GL_TRIANGLES);
glVertex2f(0.0f, 0.0f);
glVertex2f(25.0f, 25.0f);
glVertex2f(50.0f, 0.0f);
glVertex2f(-50.0f, 0.0f);
glVertex2f(-75.0f, 50.0f);
glVertex2f(-25.0f, 0.0f);
8: glEnd();
这两个三角形会使用当前的颜色进行填充。
多边形的环绕
按照顶点指定的顺序和方向组合称为环绕。上图的两个多边形是顺时针环绕的。我们可以通过改变顶点的顺序,从而改变环绕方向。我们把v4和v5的顺序调换过来,则这个三角形的环绕方向为逆时针了。
OpenGL默认是逆时针环绕方向为正方向,即逆时针环绕的三角形为正面。上图左边的三角形,显示的是正面,而右边的显示的是反面。我们也可以通过函数glFrontFace(GL_CW);来告诉OpenGL顺时针环绕的多边形是正面。环绕方向是多边形一个非常有用的性质,可以用于消除不必要的面。
我们可以绘制多个三角形连起来形成多个面或者多边形。使用GL_TRIANGLE_STRIP图元,可以绘制一串相连的多边形,来节省大量的时间。
由上图可以看到,多边形的边的绘制顺序并不是完全按照我们指定的顶点顺序的。而是按照OpenGL指定的顺序逆时针方向绘制的。
使用三角形带而不是分别指定每个三角形的顶点的优势有两个:
用前面三个顶点指定一个三角形后,只需再指定一个顶点就能画出第二个三角形。要绘制大量的三角形时,这种方法可以节省大量的代码以及数据存储空间
运算性能的提高和带宽的节省。更少的顶点,从内存传输的显卡的时间更快,以及参与变换的顶点更少。
除了三角形带之外,我们还可以通过GL_TRIANGLE_FAN图元,创建一组围绕一个中心点的三角形扇。通过制定5个顶点我们就可以绘制3个三角形来形成三角形扇。用3个顶点指定一个三角形后,后续的顶点Vn与原点V0以及前面的一个顶点Vn-1形成一个三角形。
创建实心物体
1: static void RenderScene()
//清除颜色缓冲区和深度缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
GLfloat x, y, z,
//用于颜色判断
glColor3f(1.0f, 1.0f, 0.0f);
//是否开启深度缓冲
if (bDepth)
glEnable(GL_DEPTH_TEST);
glDisable(GL_DEPTH_TEST);
//是否开启隐藏面消除
if (bCull)
glEnable(GL_CULL_FACE);
glDisable(GL_CULL_FACE);
//开启线框模式
if (bOutLine)
glPolygonMode(GL_BACK, GL_LINE) ;
glPolygonMode(GL_BACK, GL_FILL);
glPushMatrix();
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
glBegin(GL_TRIANGLE_FAN);
glVertex3f(0.0f, 0.0f, 70.0f);
for (angle = 0.0f; angle &= (2 * GL_PI); angle += (GL_PI
x = 50.0f * sin(angle);
y = 50.0f * cos(angle);
glVertex3f(x, y, 0.0f);
glColor3f(1.0f, 0.0f, 0.0f);
glColor3f(0.0f, 1.0f, 1.0f);
//画椎体的底面
glBegin(GL_TRIANGLE_FAN);
glVertex2f(0.0f, 0.0f);
for (angle = 0.0f; angle &= (2 * GL_PI); angle += (GL_PI
x = 50.0f * sin(angle);
y = 50.0f * cos(angle);
glVertex2f(x, y);
glColor3f(1.0f, 0.0f, 0.0f);
glColor3f(0.0f, 1.0f, 1.0f);
glPopMatrix();
glutSwapBuffers();
82: static void SetupRC()
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
//设置环绕方向
glEnable(GL_CW);
//设置填充模式
glShadeModel(GL_FLAT);
//设置要消除的隐藏面,这里设置为反面
glCullFace(GL_BACK);
使用红青相间的方式来绘制一个锥体。
设置多边形的颜色
多边形的填充模式有两种一种是单调着色GL_FLAT,一种平滑着色GL_SMOOTH。
通过glShadeMode(GL_FLAT);和glShadeMode(GL_SMOOTH);来设置。单调着色以三角形的最后一个顶点的颜色来填充三角形。平滑着色则是用三个顶点的颜色值进行内插值运算,来填充三角形的颜色。
平滑着色效果如下:
隐藏面消除
如果没有使用深度缓冲区,来进行深度测试。上面的椎体如何旋转(通过方向键可以旋转),都是把底面显示了出来。因为底面我们是在后面画的,最后一个绘制的物体出现在之前绘制的物体的前面。如果开启了深度测试来解决问题。
深度测试:当一个像素被绘制时,它将被设置一个值(z值),以表示它和观测者的距离。以后当这个位置要绘制新像素时,新像素的z值就会和旧像素的z值进行比较。如果新像素z值更高,则更靠近观察者,则应该被绘制,反之被忽略。
启用深度测试只须调用glEnable(GL_DEPTH_TEST);每次场景被渲染之前,应该清楚深度缓冲区,与清楚颜色缓冲区类似。
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
在程序用使用右键点击,弹出菜单,深度测试来切换深度测试。在绘制3D实体时,深度测试往往是必须的。
虽然启用深度测试,可以消除隐藏面。但很多时候,我们清楚那些面是不需要绘制,应该被剔除的。例如:多边形的背面不应该被显示。如果我们告知OpenGL这个图形面不需要被绘制,就不会把这个图形发送给OpenGL驱动程序和硬件,可以提高性能。
启用剔除面的功能。
//指定顺时针环绕的面为正面
glFrontFace(GL_CW);
//开启剔除面,剔除背面。
glEnable(GL_CULL_FACE);
锥体的底面面向锥体内部的是正面。要改变底面的正面,可以再绘制底面时设置glFrontFace(GL_CCW);
多边形模式
多边形我们可以指定它的模式,要填充还是只画线框或者只画顶点。
通过glPolygonMode(GL_BACK, GL_LINE) ;设置多边形的背面只画线。
通过glPolygonMode(GL_BACK, GL_FILL) ;设置多边形的背面为实体。
通过glPolygonMode(GL_BACK, GL_POINT) ;设置多边形的背面只画点。
发布于 2年前,
阅读(2150) | 评论(0) |
投票(0) | 收藏(2)
3D中画直线
在3D中画直线需要指定两个顶点。如下代码:会在两点之间画一条直线
glBegin(GL_LINES);
&&&& glVertex3f(0.0f, 0.0f, 0.0f);
&&&& glVertex3f(50.0f, 25.0f, 0.0f);
这里用两个顶点指定了一个图元。注意如果为GL_LINES指定奇数个顶点,那么最后一个顶点将被忽略。
static void RenderScene()
& glClear(GL_COLOR_BUFFER_BIT);
& glColor3f(0.0f, 1.0f, 0.0f);
& GLfloat x, y, z,
& z = -50.0f;
& glBegin(GL_LINES);
& for (angle = 0.0f; angle &= 2*GL_PI*3; angle +=0.5f)
&&& x = 50.0f * cos(angle);
&&& y = 50.0f * sin(angle);
&&& //设置两个顶点
&&& glVertex3f(0.0f, 0.0f, 0.0f);
&&& glVertex3f(x, y, z);
&&& z += 0.5f;
& glEnd();
& glutSwapBuffers();
效果如下图:
连续的线以及线环
绘制一条连续的线,使用GL_LINE_STRIP,会绘制一条经过所有这些顶点的连续的线。实例如下:
glBegin(GL_LINE_STRIP);
&&&& glVertex3f(0.0f, 0.0f, 0.0f);
&&&& glVertex3f(50.0f, 50.0f, 0.0f);
&&&& glVertex3f(50.0f, 100.0f, 0.0f);
GL_LINE_LOOP,与GL_LINE_STRIP相似。只是最后一个顶点和第一个顶点之间也会绘制一条直线,形成闭合的线。
用直线绘制近似的曲线
绘制近似曲线可以使用GL_LINE_STRIP来链接所有的点。当点之间的距离越来越近时,就可以形成一条平滑的曲线。示例如下:
//绘制场景
static void RenderSences()
& GLfloat z = -50.0f;&&
& //清空画布
& glClear(GL_COLOR_BUFFER_BIT);
& //设置画图使用的颜色
& glColor3f(1.0f, 0.0f, 0.0f);
& //保存矩阵
& glPushMatrix();
& //旋转, 如果不旋转,那些不同层次的点都重叠在一起,看不出3D效果了。
& glRotatef(200.0f, 1.0f, 0.0f, 0.0f);
& glRotatef(120.5f, 0.0f, 1.0f, 0.0f);
& glBegin(GL_LINE_STRIP);
& for (GLfloat a = 0.0; a & GL_PI * 3.0f; a += 0.1f)
&&& GLfloat x = 50.0f * cos(a);
&&& GLfloat y = 50.0f * sin(a);
&&& //设置点的大小
&&& //设置顶点
&&& glVertex3f(x, y, z);
&&& //调整z值
&&& z += 0.5f;
& glEnd();
& glPopMatrix();
& glFlush();
设置直线的宽度
与设置点的宽度类似,我们也可以设置直线的宽度。但直线的宽度设置也是有限制的。我们可以取得直线宽度的范围,以及其宽度增长的最小间隔。
GLfloat width[2];& //存储直线的宽度范围GL& //存储受支持的直线宽度增量
//获取受支持的直线宽度范围和增量& glGetFloatv(GL_LINE_WIDTH_RANGE, width);glGetFloatv(GL_LINE_WIDTH_GRANULARITY, &step);
static void RenderScene()
& glClear(GL_COLOR_BUFFER_BIT);
& glColor3f(0.0f, 1.0f, 0.0f);
& //获取直线的宽度
& GLfloat width[2];
& glGetFloatv(GL_LINE_WIDTH_RANGE, width);
& glGetFloatv(GL_LINE_WIDTH_GRANULARITY, &step);
& GLfloat y, curW
& curWidht = width[0];
& curWidht +=
& for (y = 80.0f; y & -80.0f; y -= 10.0f)
&&& glLineWidth(curWidht);
&&& curWidht +=
&&& glBegin(GL_LINES);
&&&&& glVertex2f(-50.0f, y);
&&&&& glVertex2f(50.0f, y);
&&& glEnd();
& glutSwapBuffers();
使用点线或虚线模式来绘制直线,称为点画。启用点画模式glEnable(GL_LINE_STIPPLE);
然后可以使用glLineStipple函数创建这种模式。
C Specification
void glLineStipple(GLint& factor,
GLushort& pattern);
其中pattern参数是一个16位的值, 它指定了一个模式,供绘直线所用。它的每个位都表示线段的一部分是画还是不画。在默认情况下,每个位对应一个像素,但factor参数可以作为乘法因子增加模式的宽度。例如把factor设置为5会导致模式中的每个位代表一行中的连续5个像素。模式的第0位(最低有效位)首先被用于指定直线。
注意:模式的顺序是反过来的,这样的原因是OpenGL左移一位获得下一个掩码值的速度更快。
static void RenderScene()
& glClear(GL_COLOR_BUFFER_BIT);
& glColor3f(0.0f, 1.0f, 0.0f);
& //开启stipple模式
& glEnable(GL_LINE_STIPPLE);
& GLint factor = 1;
& GLushort patter = 0x5555;
& for (y = 80.0f; y & -80.0f; y -= 10.0f)
&&& //设置因子和模式
&&& glLineStipple(factor, patter);
&&& glBegin(GL_LINES);
&&&&& glVertex2f(-50.0f, y);
&&&&& glVertex2f(50.0f, y);
&&& glEnd();
&&& factor++;
& glutSwapBuffers();
示例代码地址:
发布于 2年前,
阅读(841) | 评论(0) |
投票(0) | 收藏(1)
像素,计算机显示器中的最小元素。
3D画布,把可视区域看成一个3维的画布,可以用OpenGL命令在上面进行绘图。例如:glOrtho可以设置一个长方体的可视区域。谨记第二章说的:glOrtho是对之前的矩阵进行运算,所以一般都会在此函数之前调用glMatrixMode(GL_PROJECTION); glLoadIdentity();
3D空间中的顶点
我们可以通过glVertex这个函数来设置多个参数多个类型的点。例如:glVertex2f(0.0f, 1.0f); glVertex3f(1.0f, 0.5f, 0.0f);
在图形学中点不仅仅是一个点, OpenGL如何解释一个点,决定了根据这个点画出的几何图元。这里的点一般是图元中的顶点,比如矩形的顶点,曲线的顶点,线段的端点等。我们可以通过glBegin(GLenum mode); glEnd(); 来说明这两个函数之间顶点应当如何被解释。glBegin,glEnd像似{} 在此区域内的点将被解释。例如:
//画点glBegin(GL_POINTS);
glVertex2f(3.0f, 4.0f);
glVertex2f(5.0f, 8.0f);glEnd()//画线,偶数2n个点画成 n条直线 2n+1奇数个的话,最后一个将被忽略glBegin(GL_LINES);
glVertex2f(3.0f, 4.0f);
glVertex2f(5.0f, 8.0f);
glVertex2f(7.0f, 12.0f);//此点将被忽略glEnd();
注意glBegin()和glEnd()之间的缩进,这样是为了更容易寻找顶点源。是不成文的约定。
我们知道如何在OpenGL中指定一个点,并且告诉OpenGL如何对它进行操作。顶点的几何定义不仅仅是空间中的一个点,而是两条直线或曲线的交点。这就是图元的本质。
图元是顶点的集合,它们构成了在屏幕上所绘制的形状。
用简单的三角方法绘制一些点,使它们绕着z轴呈现螺旋状。
static GLfloat GL_PI = 3.14159f;
//绘制场景
void RenderSences()
GLfloat z = -50.0f;
//清空画布
glClear(GL_COLOR_BUFFER_BIT);
//设置画图使用的颜色
glColor3f(1.0f, 0.0f, 0.0f);
//保存矩阵
glPushMatrix();
//旋转, 如果不旋转,那些不同层次的点都重叠在一起,看不出3D效果了。
glRotatef(200.0f, 1.0f, 0.0f, 0.0f);
glRotatef(120.5f, 0.0f, 1.0f, 0.0f);
//获取点的大小的范围,以及增长的步长
GLfloat size[2];
GLfloat sizeS
glGetFloatv(GL_POINT_SIZE_RANGE, size);
glGetFloatv(GL_POINT_SIZE_GRANULARITY, &sizeStep);
GLfloat curStep = size[0];
for (GLfloat a = 0.0; a & GL_PI * 3.0f; a += 0.1f)
GLfloat x = 50.0f * cos(a);
GLfloat y = 50.0f * sin(a);
//设置点的大小
glPointSize(curStep);
curStep += sizeS
//设置顶点
glBegin(GL_POINTS);
glVertex3f(x, y, z);
z += 0.5f;
glPopMatrix();
glFlush();
void ChangeSize(GLsizei w, GLsizei h)
GLfloat nRange = 100.0f;
if (h == 0)
//设置视口
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//设置可视区域
GLfloat aspect = (GLfloat)w / (GLfloat)h;
if (w &= h)
glOrtho(-nRange, nRange, -nRange/aspect, nRange/aspect, -nRange, nRange);
glOrtho(-nRange*aspect, nRange*aspect, -nRange, nRange, -nRange, nRange);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//初始化工作
void SetupRC()
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
//创建窗口
void CreateWindow() {
glutInitWindowSize(200, 200);
glutCreateWindow(
"3d Points");
char **argv)
glutInit(&args, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(200, 200);
glutCreateWindow(
"draw points");
SetupRC();
glutDisplayFunc(RenderSences);
glutReshapeFunc(ChangeSize);
glutMainLoop();
点的大小如果不指定的画,默认是1。点的大小的设置是存在限制的,不同的实现限制不同。可以通过
glGetFloatv(GL_POINT_SIZE_RANGE, size);
获得点的大小的范围,size[0]为最小值,size[1]为最大值。
通过& glGetFloatv(GL_POINT_SIZE_GRANULARITY, &sizeStep); 获得点大小之间的最小间隔。
PS:点和其他的几何图元不同,点不受透视除法的影响,即点离观察点近或远并不影响点最终投影到2d屏幕上的大小。另外点总是正方形的元素。为了获得圆点,必须在抗锯齿模式下绘制点。
另外需要注意的是glPointSize必须在glBegin和glEnd之外调用,并不是所有的函数在glBegin和glEnd之间调用都是合法的。 glPointSize所指定的点的大小并不是以像素为单位的,而是一个圆的近似直径,这个圆包含了这个点所使用的像素。
发布于 2年前,
阅读(986) | 评论(0) |
投票(0) | 收藏(7)

我要回帖

更多关于 ei索引号查询 的文章

 

随机推荐