OpenGL颜色

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

OpenGL颜色
几乎所有OpenGL应用目的都是在屏幕窗口内绘制彩色图形,所以颜色在OpenGL编程中占有很重要的地位。

这里的颜色与绘画中的颜色概念不一样,它属于RGB颜色空间,只在监视器屏幕上显示。

另外,屏幕窗口坐标是以象素为单位,因此组成图形的每个象素都有自己的颜色,而这种颜色值是通过对一系列OpenGL函数命令的处理最终计算出来的。

本章将讲述计算机颜色的概念以及OpenGL的颜色模式、颜色定义和两种模式应用场合等内容,若掌握好颜色的应用,你就能走进缤纷绚丽的色彩世界,从中享受无穷的乐趣。

9.1、计算机颜色
9.1.1 颜色生成原理
计算机颜色不同于绘画或印刷中的颜色,显示于计算机屏幕上每一个点的颜色都是由监视器内部的电子枪激发的三束不同颜色的光(红、绿、蓝)混合而成,因此,计算机颜色通常用R(Red)、G(Green)、B(Blue)三个值来表示,这三个值又称为颜色分量。

颜色生成原理示意图见图9-1所示。

图9-1 计算机颜色生成原理
9.1.2 RGB色立体(RGB Color Cube)
所有监视器屏幕的颜色都属于RGB颜色空间,如果用一个立方体形象地表示RGB颜色组成关系,那么就称这个立方体为RGB色立体,如图9-2所示。

图9-2 RGB色立体
在图中,R、G、B三值的范围都是从0.0到1.0。

如果某颜色分量越大,则表示对应的颜色分量越亮,也就是它在此点所贡献的颜色成分越多;反之,则越暗或越少。

当R、G、B三个值都为0.0时,此点颜色为黑色(Black);当三者都为1.0时,此点颜色为白色(White);当三个颜色分量值相等时,表示三者贡献一样,因此呈现灰色(Grey),在图中表现为从黑色顶点到白色顶点的那条对角线;当R=1.0、G=1.0、B=0.0时,此点颜色为黄色(Yellow);同理,R=1.0、G=0.0、B=1.0时为洋红色,也叫品色(Magenta);R=0.0、G=1.0、B=1.0时为青色(Cyan)。

9.2、颜色模式
OpenGL颜色模式一共有两个:RGB(RGBA)模式和颜色表模式。

在RGB模式下,所有的颜色定义全用R、G、B三个值来表示,有时也加上Alpha值(与透明度有关),即RGBA模式。

在颜色表模式下,每一个象素的颜色是用颜色表中的某个颜色索引值表示,而这个索引值指向了相应的R、G、B值。

这样的一个表成为颜色映射(Color Map)。

9.2.1 RGBA模式(RGBA Mode)
在RGBA模式下,可以用glColor*()来定义当前颜色。

其函数形式为:
void glColor3{b s i f d ub us ui}(TYPE r,TYPE g,TYPE b);
void glColor4{b s i f d ub us ui}(TYPE r,TYPE g,TYPE b,TYPE a);
void glColor3{b s i f d ub us ui}v(TYPE *v);
void glColor4{b s i f d ub us ui}v(TYPE *v);
设置当前R、G、B和A值。

这个函数有3和4两种方式,在前一种方式下,a值缺省为1.0,后一种Alpha值由用户自己设定,范围从0.0到1.0。

同样,它也可用指针传递参数。

另外,函数的第二个后缀的不同使用,其相应的参数值及范围不同,见下表9-1所示。

虽然这些参数值不同,但实际上OpenGL已自动将它们映射在0.0到1.0或-1.0或范围之内。

因此,灵活使用这些后缀,会给你编程带来很大的方便。

后缀数据类型最小值最小值映射最大值最大值映射
b 1字节整型数-128 -1.0 127 1.0
s 2字节整型数-32,768 -1.0 32,767 1.0
i 4字节整型数-2,147,483,648 -1.0 2,147,483,647 1.0
ub 1字节无符号整型数0 0.0 255 1.0
us 2字节无符号整型数0 0.0 65,535 1.0
ui 4字节无符号整型数0 0.0 4,294,967,295 1.0
表9-1 整型颜色值到浮点数的转换
9.2.2 颜色表模式(Color_Index Mode)
在颜色表方式下,可以调用glIndex*()函数从颜色表中选取当前颜色。

其函数形式为:void glIndex{sifd}(TYPE c);
void glIndex{sifd}v(TYPE *c);
设置当前颜色索引值,即调色板号。

若值大于颜色位面数时则取模。

9.2.3 两种模式应用场合
在大多情况下,采用RGBA模式比颜色表模式的要多,尤其许多效果处理,如阴影、光照、雾、反走样、混合等,采用RGBA模式效果会更好些;另外,纹理映射只能在RGBA 模式下进行。

下面提供几种运用颜色表模式的情况(仅供参考):
1)若原来应用程序采用的是颜色表模式则转到OpenGL上来时最好仍保持这种模式,便于移植。

2)若所用颜色不在缺省提供的颜色许可范围之内,则采用颜色表模式。

3)在其它许多特殊处理,如颜色动画,采用这种模式会出现奇异的效果。

9.3、颜色应用举例
颜色是一个极具吸引力的应用,在前面几章中已经逐步介绍了RGBA模式的应用方式,这里就不再多述。

下面着重说一下颜色表模式的应用方法,请看例程:
例9-1 颜色表应用例程(cindex.c)
#include "glos.h"
#include <GL/gl.h>
#include <GL/glaux.h>
void myinit(void);
void InitPalette(void);
void CALLBACK myReshape(GLsizei w,GLsizei h);
void CALLBACK display(void);
void myinit(void)
{
glClearColor(0.0,0.0,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT);
glShadeModel(GL_FLAT);
}
void InitPalette(void)
{
GLint j;
static GLfloat rgb[][3]={
{1.0,0.0,0.0},{1.0,0.0,0.5},{1.0,0.0,1.0},{0.0,0.0,1.0},
{0.0,1.0,1.0},{0.0,1.0,0.0},{1.0,1.0,0.0},{1.0,0.5,0.0}};
for(j=0;j<8;j++)
auxSetOneColor(j+1,rgb[j][0],rgb[j][1],rgb[j][2]);
}
void CALLBACK myReshape(GLsizei w,GLsizei h)
{
glViewport(0,0,w,h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(w<=h)
glOrtho(-12.0,12.0,-12.0*(GLfloat)h/(GLfloat)w, 12.0*(GLfloat)h/(GLfloat)w,-30.0,30.0);
else
glOrtho(-12.0*(GLfloat)h/(GLfloat)w,
12.0*(GLfloat)h/(GLfloat)w,-12.0,12.0,-30.0,30.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void CALLBACK display(void)
{
InitPalette();
DrawColorFans();
glFlush();
}
{
GLint n;
GLfloat pp[8][2]={
{7.0,-7.0},{0.0,-10.0},{-7.0,-7.0},{-10.0,0.0},
{-7.0,7.0}, {0.0,10.0},{7.0,7.0},{10.0,0.0}};
/* draw some filled_fan_triangles */
glBegin(GL_TRIANGLE_FAN);
glVertex2f(0.0,0.0);
glVertex2f(10.0,0.0);
for(n=0;n<8;n++)
{
glIndexi(n+1);
glVertex2fv(pp[n]);
}
glEnd();
}
void main(void)
{
auxInitDisplayMode(AUX_SINGLE|AUX_INDEX);
auxInitPosition(0,0,500,500);
auxInitWindow("Color Index");
myinit();
auxReshapeFunc(myReshape);
auxMainLoop(display);
}
这个程序运行结果是在屏幕上显示八个连成扇形的不同颜色的三角形,每个三角形的颜色定义采用颜色表模式。

其中,调用了辅助库函数auxSetOneColor()来装载颜色映射表,即调色板。

因为将某个颜色装载到颜色查找表(color lookup table)中的过程必须依赖窗口系统,而OpenGL函数与窗口系统无关,所以这里就调用辅助库的函数来完成这个过程,然后才调用OpenGL自己的函数glIndex()设置当前的颜色号。

本文来自CSDN博客,转载请标明出处:
/StFairy/archive/2007/07/15/1691800.aspx
posted @ 2010-11-19 16:03 白了少年头Views(363) Comments(0) Edit OpenGL帧缓冲区
在OpenGL窗口中, 左下角的像素为(0, 0). 一般而言, 像素(x, y)占据的矩形区域左下角为(x, y), 右上角为(x+1, y+1).
10.1 缓存及其用途
1) 颜色缓存, 左前,右前,左后,右后和任意数量的辅助颜色缓存;
2) 深度缓存
3) 模板缓存
4) 累积缓存
注意:X窗口系统,RGBA模式至少保证1个颜色缓冲区,模板缓冲区,深度缓冲区,累计缓冲区颜色索引模式至少保证1个眼色缓冲区,深度缓冲区,模板缓冲区.
可以使用glXGetConfig{}函数查询.
用glGetIntegerv()查询每个像素占据的缓存空间的参数
GL_RED_BITS, GL_GREEN_BITS, GL_BLUE_BITS, GL_ALPHA_BITS --- 颜色缓存中R, G, B, A分量的位数
GL_INDEX_BITS --- 颜色缓存中每个颜色索引的位数
GL_DEPTH_BITS --- 深度缓存中每个像素的位数
GL_STENCIL_BITS --- 模板缓存中每个像素的位数
GL_ACCUM_RED_BITS, GL_ACCUM_GREEN_BITS, GL_ACCUM_BLUE_BITS,
GL_ACCUM_ALPHA_BITS --- 累积缓存中R, G, B, A分量的位数.
10.1.1 颜色缓存
1) 颜色缓存存储了颜色索引或RGB颜色数据, 还可能存储了alpha值.
2) 支持立体观察(stereoscopic viewing)的OpenGL实现有左颜色缓存和右颜色缓存. 它们分别用于左立体图像和右立体图像.
3) 如不支持立体观察, 则只使用左颜色缓存.
4) 双颜色缓存有前缓存和后缓存, 单缓存系统只有前缓存.
5) 支持不可显示的辅助颜色缓存
6) 函数glGetBooleanv()查询是否支持立体观察和双缓存: GL_STEREO和
GL_DOUBLE_BUFFER.
函数glGetIntegerv()查询多少个辅助缓存可用: GL_AUX_BUFFERES.
10.1.2 深度缓存
深度缓存--- 存储了每个像素的深度值. 通常是离视点的距离, 因此深度值大的像素会被深度值小的像素覆盖.
10.1.3 模板缓存
用途之一: 绘图范围限制在屏幕的特定区域内.
10.1.4 累积缓存
累积缓存也存储RGBA颜色数据, 将一系列的图像合成一幅图像.
可以对图像进行超量采样, 然后对样本进行平均, 并将结果写入颜色缓存中, 从而实现场景反走样. 不能将数据直接写入累积缓存.
累加操作总是针对一个矩形块的, 通常是将数据移入或移出颜色缓存.
10.1.5 清空缓存
void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
void glClearIndex(GLfloat index);
void glClearDepth(GLclampd depth);
void glClearStencil(GLint s);
void glClearAccum(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
功能: 指定用于清除颜色缓存(RGBA模式或颜色索引模式), 深度缓存, 模板缓存和累积缓存的值.
类型为GLclampf和GLclampd的参数值被截取到[0.0, 1.0]. 深度缓存的默认清除值为1.0, 其他缓存为0.0.
设置清除值后, 便可以调用函数glClear()来清除缓存.
void glClear(GLbitfield mask);
功能: 清除指定的缓存.
mask:GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT,
GL_STENCIL_BUFFER_BIT, GL_ACCUM_BUFFER_BIT的逻辑或(OR).
清除颜色缓存时, 如果启用了像素所有权测试, 裁剪测试和抖动操作, 它们都会在清除操作中执行.
屏蔽操作(glColorMask()和glIndexMask())也会生效.alpha测试, 模版测试, 深度测试并不会影响glClear()函数的操作.
10.1.6 指定要读写的颜色缓存
指定要写入的缓存glDrawBuffer();
指定要读取的缓存glReadBuffer();
使用双缓存, 通常只绘制后缓存, 并在绘制完成后交换缓存. 你可能想将双缓存窗口视为单缓存窗口: 通过调用函数glDrawBuffer()使得可以同时绘制前缓存和后缓存.
void glDrawBuffer(GLenum mode);
功能: 指定要写入或消除的颜色缓存以及禁用之前被启用的颜色缓存. 可以一次性启用多
个缓存.
GL_FRONT: 单缓存的默认值
GL_FRONT_RIGHT:
GL_NONE:
GL_FRONT_LEFT:
GL_FRONT_AND_BACK:
GL_RIGHT:
GL_AUXi: i表示第几个辅助缓存.
GL_LEFT:
GL_BACK_RIGHT:
GL_BACK: 双缓存的默认值
GL_BACK_LEFT:
注意: 启用多个缓存用于写操作时, 只要其中一个缓存存在, 就不会发生错误. 如果指定的缓存都不存在, 就发生错误.
void glReadBuffer(GLenum mode);
功能: 选择接下来的函数调用glReadPixels(), glCopyPixels(), glCopyTexImage*(), glCopyTexSubImage*() 和glCopyConvolutionFilter*()将读取的缓存.
并启用以前被函数glReadBuffer()启用的缓存.
参数mode取值:
GL_FRONT: 单缓存默认
GL_FRONT_RIGHT:
GL_BACK_RIGHT:
GL_FRONT_LEFT:
GL_LEFT:
GL_AUX:
GL_BACK_LEFT:
GL_BACK: 双缓存默认
GL_RIGHT:
注意: 启用缓存用于读取操作时, 指定的缓存必须存在, 否则将发生错误.
10.1.7 屏蔽缓存
void glIndexMask(GLuint mask);
void glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); void glDepthMask(GLboolean flag);
void glStencilMask(GLuint mask);
功能: 设置掩码, 用于控制写入到指定缓存中的数据,
glIndexMask: 只用于颜色索引模式中, 掩码中1对应的数据位被写入颜色索引缓存中. 0对应的位不写入.
glColorMask: 只影响RGBA模式下的写入, red, green, blue, alpha决定是否写入相应的分量, GL_TRUE时写入.
glDepthMask(): 如果参数flag的值为GL_TRUE, 则启用深度缓存用于写入, 否则禁用深度缓存.
glStencilMask(): 参数mask的含义与函数glIndexMask()中相同.
所有GLboolean参数的默认值是GL_TRUE, 两个GLuint参数的默认值都是1.
禁用深度缓存: 如果背景很复杂, 则在背景绘制之后, 禁用深度缓存. 绘制新增的物体, 只要不相互重叠.. 下一帧时, 只需恢复树木图像, 无需恢复深度缓存中的值.
这种方法很有效.
模板缓存的屏蔽操作让你能够使用一个多位模板缓存来存储多个模板(每位一个)
函数glStencilMask()指定的掩码用于控制哪些模板位面可写, 与函数glStencileFunc()的第三个参数指定的掩码无关, 后者指定模板函数将考虑哪些位面.
10.2 片段测试和操作
测试顺序:
1. 裁剪测试
2. alpha测试
3. 模版测试
4. 深度测试
5. 混合
6. 抖动
7. 逻辑操作
10.2.1 裁剪测试
void glScissor(GLint x, GLint y, GLsizei width, GLsizei height);
设置裁剪矩形的位置和大小. 需要启用GL_SCISSOR_TEST.
10.2.2 alpha测试
需要启用GL_ALPHA_TEST
void glAlphaFunc(GLenum func, GLclampf ref);
设置用于alpha测试的参考值和比较函数.
alpha测试可用于透明算法和贴花.
10.2.3 模板测试
void glStencilFunc(GLenum func, GLint ref, GLuint mask);
void glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask);
设置模板测试所使用的比较函数(func),参考值(ref),掩码(mask)
void glStencilOp(GLenum fail, GLenum zfail, GLenum zpass);
void glStencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass);
指定当一个片段通过或未通过模板测试时, 模板缓冲区中的数据如何进行修改.
下图设置模板为中间的方块
模板查询glGetInteger()可使用的参数:
GL_STENCIL_FUNC
GL_STENCIL_REF
GL_STENCIL_VALUE_MASK
GL_STENCIL_FAIL
GL_STENCIL_PASS_DEPTH_FAIL
GL_STENCIL_PASS_DEPTH_PASS
10.2.4 深度测试
void glDepthFunc(GLenum func);
为深度测试设置比较函数.
10.2.5 遮挡测试
遮挡测试允许我们判断一组几何图形在进行深度测试之后是否可见.
步骤:
1. 为每个所需的遮挡查询生成一个查询ID
2. 调用glBeginQuery(), 表示开始一个遮挡查询
3. 渲染需要进行遮挡查询的几何图形
4. 调用glEndQuery(), 表示已经完成了遮挡查询
5. 提取通过遮挡查询的样本数量.
生成查询对象
void glGenQueries(GLsizei n, GLuint* ids);
对遮挡查询对象进行初始化
void glBeginQuery(GLenum target, GLuint id);
void glEndQuery(GLenum target);
target必须为GL_SAMPLES_PASSED.
判断遮挡查询的结果
void glGetQueryObjectiv(GLenum id, GLenum pname, GLint *params); void glGetQueryObjectuiv(GLenum id, GLenum pname, GLuint *params); 清除遮挡查询对象
void glDeleteQueries(GLsizei n, const GLuint *ids);
10.2.6 混合,抖动和逻辑操作
void glLogicOp(GLenum opcode);
选择需要执行的逻辑操作.
10.3 累计缓冲区
void glAccum(GLenum op, GLfloat value);
控制累积缓冲区
op参数:
GL_ACCUM--用glReadBuffer()所读取的当前缓冲区中读取每个像素.把R,G,B,A值乘以value.而后结果累加到累积缓冲区.
GL_LOAD--同上,只是用结果替换累积缓冲区中的值.
GL_RETURN--累积缓冲区中的值乘以value, 结果放在颜色缓冲区中.
GL_ADD和AL_MULT--累积缓冲区中的值与value相加或者相乘,结果写回累积缓冲区. 另GL_MULT的结果截取[-1.0. 1.0].GL_ADD的结果不截取.
10.3.1 场景抗锯齿
首先清除累积缓冲区, 并启用前缓冲区用于读取和写入.
然后循环执行几次(例如n次)代码, 对图像进行微移和绘制.
对数据进行累积的方法:
glAccum(GL_ACCUM, 1.0/n); // 绘制到不会显示的颜色缓冲区中, 避免显示中间图像. 并最终调用
glAccum((GL_RETURN, 1.0); // 绘制到可显示的颜色缓冲区中(或即将交换的后缓冲区). 可提供一用户接口, 来显示每次图像累积之后所获得的改善, 如图像足够满意, 可随时终止累积.
本例主要是逐步在窗口累积各颜色分量.
一共累积八次,且其中每次都用j8数组的数据微移场景, 使用glFrustum函数可以是的我们场景不必对称.
正交投影的偏移只需要用glTranslatef()移动一个像素内的偏移即可.
下图为没有反锯齿没有用累积缓存的图像
下图为使用了累积缓存反锯齿的图像
累积缓存我分步骤显示,看看效果
10.3.2 运动模糊
按照相同的方式设置累积缓冲区, 但不是对图像进行空间上的微移, 而是进行时间上的微移.
glAccum(GL_MULT, decayFactor);
这样随着场景绘制到累积缓冲区中,整个场景越来越模糊. 其中decayFactor是个0.0到1.0之间的数字, 其值越小, 运动速度越快.
然后使用
glAccum(GL_RETURN, 1.0);
转移到眼色缓冲区中.
10.3.3 景深
距离聚焦平面越远,物体就越模糊.
accPerspective函数
第五个和第六个参数表示在x和y方向上微移, 实现场景抗锯齿
第九个参数设定聚焦平面.
模糊程度有第七个和第八个参数决定, 由这两个参数的乘积决定.
10.3.4 柔和阴影
多个光源所产生的柔和阴影-- 可以多次渲染场景,每次只打开一个光源, 然后将渲染结果累积起来.
10.3.5 微移
样本的微移值
posted @ 2010-11-19 14:23 白了少年头Views(972) Comments(0) Edit OpenGL位图和图像
在前面的章节中,已经讲述了几何数据(点、线、多边形)绘制的有关方法,但OpenGL 还有另外两种重要的数据类:一是位图,二是图像。

这两种数据都是以象素矩阵形式存储,即用一个矩形数组来表示某一位图或图像。

二者不同之处是位图包含每个象素的一位信息,而图像数据一般包含每个象素的多位信息(如,红、绿、蓝和Alpha值);还有位图类似于掩码,可用于遮掩别的图像,而图像数据则简单地覆盖先前已经存在的数据或者与之混合。

下面将详述这些内容。

11.1、位图
11.1.1 位图(Bitmap)与字符(Font)
位图是以元素值为0或1的矩阵形式存储的,通常用于对窗口中相应区域的绘图屏蔽。

比如说,当前颜色设置为红色,则在矩阵元素值为1的地方象素用红色来取代,反之,在为0的地方,对应的象素不受影响。

位图普遍用于字符显示,请看下面例子:
例11-1 位图字符例程(font.c)
#include "glos.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);
GLubyte rasters[12] = { 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc, 0xfc, 0xc0, 0xc0, 0xc0, 0xff, 0xff};
void myinit(void)
{
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
glClearColor (0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
}
void CALLBACK display(void)
{
glColor3f (1.0, 0.0, 1.0);
glRasterPos2i (100, 200);
glBitmap (8, 12, 0.0, 0.0, 20.0, 20.0, rasters);
lBitmap (8, 12, 0.0, 0.0, 0.0, 0.0, rasters);
glColor3f (1.0, 1.0, 0.0); glRasterPos2i (150, 200);
glBitmap (8, 12, 0.0, 0.0, 0.0, 0.0, rasters);
glFlush();
}
void CALLBACK myReshape(GLsizei w, GLsizei h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho (0, w, 0, h, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
}
void main(void)
{
auxInitDisplayMode (AUX_SINGLE | AUX_RGBA);
auxInitPosition (0, 0, 500, 500);
auxInitWindow ("Bitmap");
myinit();
auxReshapeFunc (myReshape);
auxMainLoop(display);
}
以上程序运行结果是显示三个相同的字符F。

OpenGL函数库只提供了最底层操作,即用glRasterPos*()和glBitmap()在屏幕上定位和画一个位图,图11-1显示了F的位图和相应的位图数据。

在图中,字符大小为12*8的方阵,每一行数据用8位16进制表示。

注意:位图数据总是按块存储,每块的位数总是8的倍数,但实际位图的宽并不一定使8的倍数。

组成位图的位从位图的左下角开始画:首先画最底下的一行,然后是这行的上一行,依此类推。

这个程序中的几个重要函数的解释将在下面几个小节,其中函数glPixelstorei()描述了位图数据在计算机内存中存储的方式。

11.1.2 当前光栅位置
当前光栅位置函数就是:
void glRasterPos{234}{SIFD}[V](TYPE x,TYPE y,TYPE z,TYPE w);
设置当前所画位图或图像的原点。

其中参数x、y、z、w给出了光栅位置坐标。

在变换到屏幕坐标时(即用模型变换和透视变换),光栅位置坐标与glVertex*()提供的坐标同样对待。

也就是说,变换后要么确定一个有效点,要么认为位于视口以外的点的当前光栅位置无效。

在上一例中,颜色设置的位置与当前光栅位置函数调用的位置有关,glColor*()必须放在glRasterPos*()前,则紧跟其后的位图就都继承当前的颜色,例前两个紫色的F;若要改变当前位图颜色,则需重新调用glColor*()和glRasterPos*(),如第三个黄色字符F的显示。

11.1.3 位图显示
当设置了光栅位置后,就可以调用glBitmap()函数来显示位图数据了。

这个函数形式为:
void glBitmap( GLsizei width,GLsizei height,GLfloat xbo,GLfloat
ybo,GLfloat xbi,GLfloat ybi,const GLubyte *bitmap);
显示由bitmap指定的位图,bitmap是一个指向位图的指针。

位图的原点放在最近定义的当前光栅位置上。

若当前光栅位置是无效的,则不显示此位图或其一部分,而且当前光栅位置仍然无效。

参数width和height一象素为单位说明位图的宽行高。

宽度不一定是8的倍数。

参数xbo和ybo定义位图的原点(正值时,原点向上移动;负值时,原点向下移动)。

参数xbi和ybi之处在位图光栅化后光栅位置的增量。

在上一例中:
glColor3f (1.0, 0.0, 1.0);glRasterPos2i (100, 200);glBitmap (8, 12, 0.0, 0.0, 20.0, 20.0, rasters);glBitmap (8, 12, 0.0, 0.0, 0.0, 0.0, rasters);
第一个字符F与第二个字符F的间距是由glBitmap()的两个增量参数决定的,即第二个字符F在第一个字符F的基础上分别向X正轴和Y负轴移动20个象素单位。

11.2 图像
一般来说,OpenGL图像(image)操作包括象素读写、象素拷贝和图像缩放,下面分别来介绍。

例11-2 图像应用例程(image.c)
#include "glos.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
void myinit(void);
void triangle(void);
void SourceImage(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h); void myinit (void)
{
glClear (GL_COLOR_BUFFER_BIT);
}
void triangle(void)
{
glBegin (GL_TRIANGLES);
glColor3f (0.0, 1.0, 0.0);
glVertex2f (2.0, 3.0);
glColor3f(0.0,0.0,1.0);
glVertex2f (12.0, 3.0);
glColor3f(1.0,0.0,0.0);
glVertex2f (7.0, 12.0);
glEnd ();
}
void SourceImage(void)
{
glPushMatrix();
glLoadIdentity();
glTranslatef(4.0,8.0,0.0);
glScalef(0.5,0.5,0.5);
triangle ();
glPopMatrix();
}
void CALLBACK display(void)
{
int i;
/* 绘制原始图像 */
SourceImage();
/* 拷贝图像 */
for(i=0;i<5;i++)
{
glRasterPos2i(1+i*2,i);
glCopyPixels(160,310,170,160,GL_COLOR);
}
glFlush ();
}
void CALLBACK myReshape(GLsizei w, GLsizei h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(w <= h)
gluOrtho2D (0.0, 15.0, 0.0, 15.0 * (GLfloat) h/(GLfloat) w);
else
gluOrtho2D (0.0, 15.0 * (GLfloat) w/(GLfloat) h, 0.0, 15.0); g lMatrixMode(GL_MODELVIEW);
}
void main(void)
{
auxInitDisplayMode (AUX_SINGLE | AUX_RGBA);
auxInitPosition (0, 0, 500, 500);
auxInitWindow ("Pixel Processing");
myinit();
auxReshapeFunc (myReshape);
auxMainLoop(display);
}
以上程序运行的结果是在屏幕正上方显示一个最初的五彩三角形,然后在下半部显示一串拷贝的三角形。

当然,读者自己可以再加上图像放大缩小等,试试看,会发生怎样的情形?
posted @ 2010-11-19 11:25 白了少年头Views(453) Comments(0) Edit OpenGL像素操作
今天我们先简单介绍Windows中常用的BMP文件格式,然后讲OpenGL的像素操作。

虽然看起来内容可能有点多,但实际只有少量几个知识点,如果读者对诸如“显示BMP图象”等
内容比较感兴趣的话,可能不知不觉就看完了。

像素操作可以很复杂,这里仅涉及了简单的部分,让大家对OpenGL像素操作有初步的印象。

学过多媒体技术的朋友可能知道,计算机保存图象的方法通常有两种:一是“矢量图”,一是“像素图”。

矢量图保存了图象中每一几何物体的位置、形状、大小等信息,在显示图象时,根据这些信息计算得到完整的图象。

“像素图”是将完整的图象纵横分为若干的行、列,这些行列使得图象被分割为很细小的分块,每一分块称为像素,保存每一像素的颜色也就保存了整个图象。

这两种方法各有优缺点。

“矢量图”在图象进行放大、缩小时很方便,不会失真,但如果图象很复杂,那么就需要用非常多的几何体,数据量和运算量都很庞大。

“像素图”无论图象多么复杂,数据量和运算量都不会增加,但在进行放大、缩小等操作时,会产生失真的情况。

前面我们曾介绍了如何使用OpenGL来绘制几何体,我们通过重复的绘制许多几何体,可以绘制出一幅矢量图。

那么,应该如何绘制像素图呢?这就是我们今天要学习的内容了。

1、BMP文件格式简单介绍
BMP文件是一种像素文件,它保存了一幅图象中所有的像素。

这种文件格式可以保存单色位图、16色或256色索引模式像素图、24位真彩色图象,每种模式种单一像素的大小分别为1/8字节,1/2字节,1字节和3字节。

目前最常见的是256色BMP和24位色BMP。

这种文件格式还定义了像素保存的几种方法,包括不压缩、RLE压缩等。

常见的BMP文件大多是不压缩的。

这里为了简单起见,我们仅讨论24位色、不使用压缩的BMP。

(如果你使用Windows自带的画图程序,很容易绘制出一个符合以上要求的BMP)
Windows所使用的BMP文件,在开始处有一个文件头,大小为54字节。

保存了包括文件格式标识、颜色数、图象大小、压缩方式等信息,因为我们仅讨论24位色不压缩的BMP,所以文件头中的信息基本不需要注意,只有“大小”这一项对我们比较有用。

图象的宽度和高度都是一个32位整数,在文件中的地址分别为0x0012和0x0016,于是我们可以使用以下代码来读取图象的大小信息:
GLint width, height; // 使用OpenGL的GLint类型,它是32位的。

// 而C语言本身的int则不一定是32位的。

FILE* pFile;
// 在这里进行“打开文件”的操作
fseek(pFile, 0x0012, SEEK_SET); // 移动到0x0012位置
fread(&width, sizeof(width), 1, pFile); // 读取宽度
fseek(pFile, 0x0016, SEEK_SET); // 移动到0x0016位置
// 由于上一句执行后本就应该在0x0016位置
// 所以这一句可省略
fread(&height, sizeof(height), 1, pFile); // 读取高度
54个字节以后,如果是16色或256色BMP,则还有一个颜色表,但24位色BMP没有这
个,我们这里不考虑。

接下来就是实际的像素数据了。

24位色的BMP文件中,每三个字节表示一个像素的颜色。

注意,OpenGL通常使用RGB来表示颜色,但BMP文件则采用BGR,就是说,顺序被反过来了。

另外需要注意的地方是:像素的数据量并不一定完全等于图象的高度乘以宽度乘以每一像素的字节数,而是可能略大于这个值。

原因是BMP文件采用了一种“对齐”的机制,每一行像素数据的长度若不是4的倍数,则填充一些数据使它是4的倍数。

这样一来,一个17*15
的24位BMP大小就应该是834字节(每行17个像素,有51字节,补充为52字节,乘以15得到像素数据总长度780,再加上文件开始的54字节,得到834字节)。

分配内存时,一定要小心,不能直接使用“图象的高度乘以宽度乘以每一像素的字节数”来计算分配空间的长度,否则有可能导致分配的内存空间长度不足,造成越界访问,带来各种严重后果。

一个很简单的计算数据长度的方法如下:
int LineLength, TotalLength;
LineLength = ImageWidth * BytesPerPixel; // 每行数据长度大致为图象宽度乘以
// 每像素的字节数
while( LineLength % 4 != 0 ) // 修正LineLength使其为4的倍数
++LineLenth;
TotalLength = LineLength * ImageHeight; // 数据总长 = 每行长度 * 图象高度
这并不是效率最高的方法,但由于这个修正本身运算量并不大,使用频率也不高,我们就不需要再考虑更快的方法了。

2、简单的OpenGL像素操作
OpenGL提供了简洁的函数来操作像素:
glReadPixels:读取一些像素。

当前可以简单理解为“把已经绘制好的像素(它可能已经被保存到显卡的显存中)读取到内存”。

glDrawPixels:绘制一些像素。

当前可以简单理解为“把内存中一些数据作为像素数据,进行绘制”。

glCopyPixels:复制一些像素。

当前可以简单理解为“把已经绘制好的像素从一个位置复制到另一个位置”。

虽然从功能上看,好象等价于先读取像素再绘制像素,但实际上它不需要把已经绘制的像素(它可能已经被保存到显卡的显存中)转换为内存数据,然后再由内存数据进行重新的绘制,所以要比先读取后绘制快很多。

这三个函数可以完成简单的像素读取、绘制和复制任务,但实际上也可以完成更复杂的任务。

当前,我们仅讨论一些简单的应用。

由于这几个函数的参数数目比较多,下面我们分别介绍。

3、glReadPixels的用法和举例
3.1 函数的参数说明
该函数总共有七个参数。

前四个参数可以得到一个矩形,该矩形所包括的像素都会被读取出来。

(第一、二个参数表示了矩形的左下角横、纵坐标,坐标以窗口最左下角为零,最右上
角为最大值;第三、四个参数表示了矩形的宽度和高度)
第五个参数表示读取的内容,例如:GL_RGB就会依次读取像素的红、绿、蓝三种数据,GL_RGBA则会依次读取像素的红、绿、蓝、alpha四种数据,GL_RED则只读取像素的红
色数据(类似的还有GL_GREEN,GL_BLUE,以及GL_ALPHA)。

如果采用的不是RGBA 颜色模式,而是采用颜色索引模式,则也可以使用GL_COLOR_INDEX来读取像素的颜色
索引。

目前仅需要知道这些,但实际上还可以读取其它内容,例如深度缓冲区的深度数据等。

第六个参数表示读取的内容保存到内存时所使用的格式,例如:GL_UNSIGNED_BYTE会
把各种数据保存为GLubyte,GL_FLOAT会把各种数据保存为GLfloat等。

第七个参数表示一个指针,像素数据被读取后,将被保存到这个指针所表示的地址。

注意,需要保证该地址有足够的可以使用的空间,以容纳读取的像素数据。

例如一幅大小为
256*256的图象,如果读取其RGB数据,且每一数据被保存为GLubyte,总大小就是:
256*256*3 = 196608字节,即192千字节。

如果是读取RGBA数据,则总大小就是
256*256*4 = 262144字节,即256千字节。

注意:glReadPixels实际上是从缓冲区中读取数据,如果使用了双缓冲区,则默认是从正在显示的缓冲(即前缓冲)中读取,而绘制工作是默认绘制到后缓冲区的。

因此,如果需要读取已经绘制好的像素,往往需要先交换前后缓冲。

再看前面提到的BMP文件中两个需要注意的地方:
3.2 解决OpenGL常用的RGB像素数据与BMP文件的BGR像素数据顺序不一致问题
可以使用一些代码交换每个像素的第一字节和第三字节,使得RGB的数据变成BGR的数据。

当然也可以使用另外的方式解决问题:新版本的OpenGL除了可以使用GL_RGB读取
像素的红、绿、蓝数据外,也可以使用GL_BGR按照相反的顺序依次读取像素的蓝、绿、
红数据,这样就与BMP文件格式相吻合了。

即使你的gl/gl.h头文件中没有定义这个GL_BGR,也没有关系,可以尝试使用GL_BGR_EXT。

虽然有的OpenGL实现(尤其是旧版本的实现)并不能使用GL_BGR_EXT,但我所知道的Windows环境下各种OpenGL实现都对GL_BGR 提供了支持,毕竟Windows中各种表示颜色的数据几乎都是使用BGR的顺序,而非RGB
的顺序。

这可能与IBM-PC的硬件设计有关。

3.3 消除BMP文件中“对齐”带来的影响
实际上OpenGL也支持使用了这种“对齐”方式的像素数据。

只要通过glPixelStore修改“像素保存时对齐的方式”就可以了。

像这样:
int alignment = 4;
glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
第一个参数表示“设置像素的对齐值”,第二个参数表示实际设置为多少。

这里像素可以单字节对齐(实际上就是不使用对齐)、双字节对齐(如果长度为奇数,则再补一个字节)、四字节对齐(如果长度不是四的倍数,则补为四的倍数)、八字节对齐。

分别对应alignment
的值为1, 2, 4, 8。

实际上,默认的值是4,正好与BMP文件的对齐方式相吻合。

相关文档
最新文档