OpenGL

浏览量:17,948

OpenGL原理介绍

转载请注明原文章链接:http://www.twinklingstar.cn/2015/1532/introduce-to-opengl/ 代码下载地址:https://github.com/twinklingstar20/twinklingstar_cn_demo_introduce_to_opengl 1. OpenGL简介 OpenGL是Open Graphics Library的缩写,是个定义了一个跨编程语言、跨平台的编程接口的标准,显卡通常有OpenGL的实现,不同显卡上的OpenGL实现也不一定相同,OpenGL标准不是平台相关的,所以同一个程序可能在不同的显卡上运行。OpenGL API只处理图形渲染,并不提供动画、定时器、文件IO、图像文件格式处理、GUI等功能,GLUT并不是OpenGL,也不是OpenGL的一部分,它仅是被一些用户用于创建OpenGL窗口。OpenGL不是开源代码,OpenGL指的是开放标准,在网上可以找到,任何人都可以免费下载,也有一个开源的GL实现,名叫 Mesa3D,已经实现了OpenGL 3.0和GLSL 1.30。 OpenGL被当作客户端-服务器系统来实现的,应用程序是客户端,图形硬件厂商提供的OpenGL实现是服务器。如图1所示,客户端程序需要调用OpenGL的接口实现3D渲染,那么OpenGL命令和数据会缓存在RAM中,在一定条件下,会将这些命令和数据通过CPU时钟发送到VRAM,在GPU的控制下,使用VRAM中的数据和命令,完成图形的渲染,并将结果存入帧缓冲区中,帧缓冲区中的帧最终会被发送到显示器上,显示出结果。在现代的图形硬件系统中,还支持不通过CPU时钟直接将数据由RAM发送至VRAM或直接将数据由帧缓冲区发送至RAM(例如OpenGL中的VBO,PBO)。 图1. 计算机图形硬件系统 在一些OpenGL实现中,比如那些与X windows system相关的实现,客户端和服务器在不同的机器上执行,两者通过网络连通起来。在这种情况下,客户端发送OpenGL命令,这些命令被转化成窗口系统相关的协议,再通过共享的网络发送给server。 如图2所示,OpenGL中的顶点、像素等数据需要通过不同阶段的处理,才能产生最后的可视图像,就像是工厂里的生产线,称为图形渲染管线。像素和顶点数据可以选择存储在显示列表中,我们可以把显示列表看成是存储数据的媒介,用于加速渲染速度。顶点数据经过求值器,产生法向量、纹理坐标、点的空间坐标等,通过顶点操作和图元装配,生成相应的像素信息,进行光栅化处理,光栅化是把几何和像素数据转化成片段,每个片段块对应帧缓冲区中的一个像素。其中,顶点操作和图元装配中又可以细分出一条渲染管线,这里称为顶点处理管线。在光栅化完成后,还可以根据命令,对每个像素进行处理,最后写入帧缓冲区内。 图2. OpenGL的图形渲染管线 2. 顶点处理管线 本节介绍如何将三维空间上的图元转化为二维屏幕上渲染出的图元,包括图元的顶点位置、大小等。 顶点的处理管线如图2所示,设我们在三维空间上的坐标\((0,0,0)\)处画一个小球,对小球移动至\((1,0,0)\),再绕着\(y\)坐标轴旋转90度,对小球位移和旋转的处理完成后,这是模型矩阵完成的功能。在空间中放置好模型后,需要架设摄像机,然后才能够观察到模型,架设摄像机,是视图矩阵实现的功能,把这两部分统一起来,就得到模型视图矩阵。接着,需要把小球投影到一个虚拟屏幕上,计算小球的虚拟投影点,这是投影矩阵完成的功能。如果场景中存在多个小球,相对于摄像机的视角,有一部分小球被前面的小球阻挡了,那么被阻挡的小球就不会被渲染出来,裁剪掉被阻挡的模型就是裁剪处理完成的功能。最后,根据屏幕的长宽,把虚拟屏幕映射到真实屏幕上。这就是OpenGL顶点处理的简化流程。接着,详细阐述技术细节。 图3. 顶点处理管线        首先引入齐次坐标的概念,齐次表示是什么?为什么要采用齐次表示呢?在 维空间上,把点和向量扩展一维,就是它们的齐次表示,点\(P\)的齐次表示为\(({p_1},{p_2}, \cdots ,{p_n},1)\),向量\(\vec v\)的齐次表示为\(({v_1},{v_2}, \cdots ,{v_n},0)\)。一个顶点可以表示为\((x,y,z,1)\),一个向量则表示为\((a,b,c,0)\)。点表示空间上的一个位置,向量表示一个方向而没有具体的位置,因此点的位移是有意义的,但是向量的位移是没有意义的。采用齐次表示,三维空间上的图形变换包括旋转、位移、透视、正交等都可以用一个\(4 \times 4\)的矩阵来表示,矩阵的连乘就是各个矩阵代表的变换的叠加。因此,图3中的模型视图变换、投影变换都可以用一个\(4 \times 4\)的矩阵来表示。 2.1.   模型视图变换 我们又可以把模型视图矩阵细分为两个部分,如图4所示,对象坐标先经过模型变换,转化为世界坐标,再经过视图变换,转化为视点坐标。

spacer
浏览量:898

OpenGL雾简介

本文主要对OpenGL中雾的计算原理进行介绍,在参考【1】的第6章、 NEHE教程的第16章和第41章都有示例代码。 一. 雾的计算原理 把片断(fragment)颜色和雾颜色以一定比例混合,就能产生雾的效果。比例因子f,主要有三种计算混合因子的方法,如下面的三个等式所示,其中c指的是眼睛坐标与片段中心之间的距离或者是雾坐标,通过等式计算出个的因子f,需要缩放至范围内。 \(f = \frac{{end - c}}{{end - start}}({\rm{GL\_LINEAR)}}\) \(f = {e^{ - (density \cdot c)}}({\rm{GL\_EXP)}}\) \(f = {e^{ - (density \cdot c)}}^2({\rm{GL\_EXP2)}}\) 雾浓度的计算等式 在RGBA模式和颜色索引模式下,都能支持雾的计算,具体可以参考【1】。 二. OpenGL雾相关的API函数 void glFog{f,i}(GLenum  pname,  TYPE  param); void glFog{f,i}v(GLenum  pname,  const TYPE * 

spacer
浏览量:1,189

模板缓冲区

本文主要介绍模板缓冲区的用途,基本用法,主要的操作函数和模板缓冲区在倒影渲染中的应用。 示例代码下载地址:https://github.com/twinklingstar20/twinklingstar_cn_demo_stencil_buffer/ 一. 介绍 所谓的模板缓冲区是做什么用的,举个简单的例子来说明,如图1所示。屏幕上画一个平面,把这个平面写入到模板缓冲区中,再画小球的时候,进行模板测试,小球在平面范围内的内容会显示出来,不在平面范围内的就不显示。假设,把图1中画的平面,看成一扇窗户,而小球看成窗户外面的风景,那么只有在窗户范围内的风景才会显示出来。所谓的模板缓冲区,基本就干这么一件事儿,它在倒影(Reflection)和阴影(Shadow)效果处理上,都起到非常重要的作用。 图1. (1)画一个平面和一个球;(2)启用模板测试,小球不在平面范围内的内容不显示;(3)关闭模板测试,小球在与不在平面范围内,都将内容显示出来 只有存在模板缓冲区的情况下,才会进行模板测试,即将存储在模板缓冲区中的值与参考值进行对比,通过测试的像素才可能在屏幕上显示,而且依赖测试的结果,可能对模板缓冲区中的值进行修改。 OpenGL和DirectX都支持模板缓冲区,每个像素占的位数通常是1、4、8等。渲染过程中,对片断(Fragment)的处理,包括裁剪测试、透明度测试、模板测试、深度测试等等,它们的顺序如图2所示,模板测试发生在深度测试之间。 图2. 每个片断的处理管线 二. OpenGL中与模板缓冲区相关的操作 OpenGL中与模板缓冲区相关的操作,主要有下面几种。 glEnable/glDisable(GL_STENCIL_TEST); glClearStencil(0); glClear(… | GL_STENCIL_BUFFER_BIT); glStencilFunc(function, ref, mask); glStencilOp(stencil_fail, depth_fail, depth_pass); glStencilMask(mask); 默认情况下,模板测试是关闭的,需要调用glEnable()开启模板测试,不用时调用glDisable关系模板测试。 glClearStencil()和glClear()用于清除模板缓冲区。 glStencilFunc设置模板测试比较的条件、参考值和掩码,function可以取的值和意义如下表所示: glStencilOp()用于更新模板缓冲区的操作,模板缓冲区的更新与模板测试的结果和深度测试的结果都有关系,该函数有3个参数,它们的意思分别表示: (1) stencil_fail,规定模板测试失败时进行的操作,默认值是GL_KEEP; (2) depth_fail,规定模板测试通过,但是深度测试失败时的操作,默认值是GL_KEEP; (3) depth_pass,规定了下面有两种情况的操作,默认值是GL_KEEP: i. 模板测试和深度测试都通过 ii.  模板测试通过但是不存在深度缓冲区或者未开启深度测试 glStencilOp()中3个参数可取值和它们对应的意义如下表所示: 三. 倒影

spacer
浏览量:1,203

GLSL与RenderMan、ISL、Cg、HLSL的对比

本文参考【1】第20章,选择性的翻译了其中的部分内容,最后的总结参考【2】中的文章。简单的介绍了着色语言的编年史,以及OpenGL的GLSL着色语言与RenderMan着色语言、ISL、Cg、HLSL四种着色语言的对比。 一. 着色语言编年史 普遍认为Rob Cook和Ken Perlin是第一批开发语言来描述着色计算的人,他们针对的主要是离线渲染系统。Perlin的贡献主要在定义了噪音函数和引入了控制结构,Cook的贡献主要在对着色器的分类上,包括平面着色器、光照着色器、气候着色器等等。人们努力的开发一种能充分描述着色计算的语言,直到Pixar公司推出RenderMan接口规范,达到了一个小巅峰。RenderMan是描述离线渲染系统的着色语言的产业标准,在今天仍旧得到广泛的应用。 第一个交互式着色语言是在北卡罗来纳大学的大规模并行图形系统下得到验证的,该系统名叫PixelFlow,PixelFlow使用到的着色语言是由Marc Olano在1998年描述的。后来,Olano离开北卡,加入SGI公司,定义并且实现了一款运行在OpenGL之上的交互式着色语言,它能使用多通道渲染方法来执行着色器。到了2000年,推出了OpenGL着色器,这是第一款商业上实时的、高级的渲染语言。 OpenGL的GLSL、微软的HLSL、NVIDIA的Cg都是尝试定义商业上可行的、实时的、高级着色语言。3Dlabs的Dave Baldwin在2001年10月推出了描述着色语言的白皮书,就是GLSL。NVIDIA的Cg的规范是在2002年6月推出的,微软的HLSL规范是在2002年11月推出的。 二. RenderMan 在1988年,经过几年的开发,Pixar发布了RenderMan接口规范,这些接口定义了建模程序和渲染程序之间的通行协议,为了能产生照片级真实感质量的图像,原始的目标客户主要动画生产商,后来在电影上也得到应用。 与OpenGL不同的是,它有一整套自己的图形处理管线,不需要太关心交互性和硬件实现。它支持几何图元、层次性建模、相机属性、着色器属性等等,这些与OpenGL着色语言类似。 RenderMan着色语言是RenderMan的组成部分,这种语言也是基于C,能够描述任意的着色器,通过RenderMan接口,把它传输给渲染器。与GLSL类似,可以定义场景中的一些特征,可以计算颜色、位置、透明度等。 GLSL为了能够与当前的商业图形硬件相匹配,抽象出了两种着色器:顶点着色器和片断着色器。而RenderMan抽象出了五类着色器:光照着色器、位移着色器、平面着色器、容量着色器和图像着色器。 两种语言在数据类型的定义上也存在一定的差别。 三. ISL ISL是Interactive Shading Language的缩写,是描述OpenGL着色器的语言,这里说的OpenGL着色器是第1节中由Olano在2000年推出的产品,它是由SGI开发的软件包,现在已经被淘汰了。OpenGL着色器不仅定义了着色语言(该着色语言称为交互式着色语言),而且定义了一组API,用于定义着色器和在渲染过程中使用它们的方法。 ISL支持平面着色器和光照着色器,在这一点上,与RenderMan更加类似。 ISL用于提供过去的和现有的硬件的可移植性而设计,GLSL用于提供现有的和将来的硬件的可编程而设计。即,如果图形硬件不支持可编程性,就不会支持GLSL语言;相反,ISL能在各种硬件上执行各种特效,包括在不支持或者明显支持编程性的硬件上。 ISL相当是一种汇编语言,不需要底层的硬件的改变,而GLSL需要硬件底层定义相应的功能,GLSL能把高级语言转化为硬件可执行的机器码,高级语言的编译器是由硬件产商生产的,更容易实现编译器的优化。 四. HLSL HLSL是High-Level Shader Language的缩写,是由微软定义的,在2002年跟DirectX 9一起发布的。与GLSL相类似,它支持顶点着色器(Vertex Shader)和像素着色器(Pixel Shader)。 HLSL的执行环境与GLSL不同,如图1所示。翻译器在DirectX之外,即HLSL程序从来不会直接发送给DirectX 9或者DirectX 10的API上执行。相反,HLSL编译器编译HLSL源码,生成汇编级原码或者二进制程序。 图1 微软的HLSL的执行环境 这种方法的优点是,HLSL程序能够离线编译生成二进制代码,然后再加入到主程序当中,但是在运行时二进制代码仍然需要转化为机器码。GLSL的编译、链接都放到硬件层来实现,给了硬件生产商大量的空间进行着色器优化和体系创新。 HLSL是为DirectX而设计,所有权归微软所有;GLSL是为OpenGL设计,它是一种开放的跨平台标准,改变很少且对以前的版本保持着高度的兼容性。 五. Cg Cg是C for

spacer
浏览量:4,854

OpenGL着色器介绍

本文主要介绍如何使用OpenGL实现着色器程序,首先会简单介绍着色器在OpenGL渲染管线中是什么个位置,接着是介绍可以通过GLSL语言实现的两类着色器:顶点着色器和片段着色器,最后使用OpenGL实现了个DEMO(在文章最后面,提供了下载),演示如何使用OpenGL接口创建着色器程序。 会例代码下载地址:https://github.com/twinklingstar20/twinklingstar_cn_demo_basic_shader/ 一. 简介 OpenGL是把图像数据和几何数据发送给图形硬件,进行一系列处理,最终显示到屏幕上。我们可以随意的设置管线上某些阶段的状态、参数等,但是OpenGL图形管线的基本操作和顺序是不能改变的,即是“固定渲染管线”。通过OpenGL着色语言和支持它的OpenGL API接口,就允许应用程序开发者使用高级编程语言来实现自己的着色器,这就使得开发者能够使用硬件来实现更丰富的渲染效果,这种就称为“可编程渲染管线”。在应用程序当中,这两种管线不能同时使用,只能使用其中的一种渲染管线。 使用OpenGL的着色器,可以丰富渲染效果,能实现的功能包括: (1)       更加真实的材质-金属、石头、木头等 (2)       更加真实的光照效果-区域光照、柔和阴影等 (3)       自然现象-火、烟、水、云等 (4)       高级渲染效果-全局光照、光线追踪器等 (5)       非照片级材质-绘画效果、笔写效果等 (6)       纹理内存新的一些用途-向量的存储、模糊值、多项式系数等 (7)       过程纹理-动态生成的2D、3D纹理等 (8)       图像处理-卷积、复杂混合、模糊掩盖锐化处理(unsharp masking)等 (9)       动画效果-关键帧插值、粒子系统、程序定义的运动等 (10)   自定义的抗锯齿方法 (11)   一般的计算-排序、数学建模、流体动力学等 上面所述的许多技术原先只能通过软件来实现,如果通过OpenGL提供的GLSL语言和接口,可以将那些功能放在硬件层实现,明显渲染效果会得到提升,而且减轻了CPU的负担。 二. 顶点处理器(Vertex Processor)和片断处理器(Fragment Processor) 在《OpenGL原理介绍》一文中,对OpenGL渲染管线的各个阶段进行了介绍,并不是渲染管线中的各个阶段都能通过着色语言实现的着色器来替换。GLSL着色语言能实现两类处理器的功能:顶点处理器和片断处理器。GLSL语言经过很精细地设计,使硬件能够并行的处理顶点和片段,这就给硬件产商实现更快的图形硬件。图1,显示了OpenGL中可编程渲染管线的逻辑图。 图1. OpenGL中的可编程渲染管线的逻辑图 2.1 顶点处理器 传统的顶点处理器能对顶点值和它相关的数据进行处理,主要操作有: (1)      

spacer
浏览量:4,900

多重纹理和纹理组合器

本文主要介绍OpenGL中两种技术的使用方法:多重纹理技术和纹理组合器技术,最终根据参考【2】中的代码,实现了两个简单的演示DEMO,其中使用到了《八叉树颜色量化、BMP、TGA文件解析》篇章中提供的图像解析类。 下载地址:https://github.com/twinklingstar20/twinklingstar_cn_demo_multitexture_texture_combiner/ 一.多重纹理技术(Multitexture) 1.1 多重纹理技术简介 OpenGL在渲染多边形时允许多张纹理同时被应用,这些纹理在操作管线上一个接一个的被处理。一个纹理单元(texture unit)表示单个纹理的操作,一个纹理单元处理完成后,将它的结果传递给下一个纹理单元,直至所有的纹理单元都被处理完。下图显示了片断(fragment)经历的四个纹理操作过程。 图1.片断经历的四个纹理过程 注意:在反馈模式下,除了第一个纹理单元外多重纹理是处于未定义的状态,即只有第一张纹理单元能用(In feedback mode, multitexturing is undefined beyond the first texture unit)。 1.2 使用多重纹理的几个步骤 (1)   建立每个纹理单元的纹理状态,包括纹理图像数据,过滤器,环境,矩阵,纹理函数生成方式等。可用glActiveTexture()函数以改变当前的纹理状态,然后调用glTexImage*(),glTexParameter*(),glTexEnv*(),glTexGen*(),glBindTexture()方法分配每个纹理单元的纹理信息,纹理状态、纹理坐标和光栅化纹理坐标的查询都应用在当前纹理单上,即glActiveTexture()指定的纹理单元。此外,可以使用glGetIntegerv(GL_MAX_TEXTURE_UNITS,…)显示当前实现中允许的最大纹理单元数,得到的最大纹理单元数至少为2。 注意:glPushAttrib(),glPushClientAttrib(),glPopAttrib(),或者glPopClientAttrib()可以保存或者恢复所有纹理单元的纹理状态,但是纹理矩阵堆栈除外。 (2)   可以用glMultiTexCoord*()函数规定每个顶点上不同纹理单元的纹理坐标。下面的代码片段演示该函数的用法: glBegin(GL_TRIANGLES); glMultiTexCoord2f(GL_TEXTURE0,0.0f,0.0f); glMultiTexCoord2f(GL_TEXTURE1,1.0f,0.0f); glVertex2f(0.0f,0.0f); glMultiTexCoord2f(GL_TEXTURE0,0.5f,1.0f); glMultiTexCoord2f(GL_TEXTURE1,0.5f,0.0f); glVertex2f(50.0f,100.0f); glMultiTexCoord2f(GL_TEXTURE0,1.0f,0.0f); glMultiTexCoord2f(GL_TEXTURE1,1.0f,1.0f); glVertex2f(100.0f,0.0f); glEnd(); 注意:如果使用glTexCoord*()相当于采用了第一个纹理单元的纹理坐标,即与glMultiTexCoord*(GL_TEXTURE0,…)等价。 (3)   恢复使用单个纹理。在使用多重纹理时,如果想要恢复使用单个纹理,需要禁用除纹理单位0之外的所有纹理单位。如下面的代码片段所示:

spacer
浏览量:909

累积缓冲区

经过《锯齿与抗锯齿技术介绍》和《随机采样和分布式光线追踪》两篇文章的铺垫,现在介绍一种图形学中较高级的主题,就是累积缓冲区。读这篇文章前,假设你们对图形学的顶点处理管线,glFrustum()和gluPerspective()之间的关系等都很熟悉。文章首先会简单的介绍下OpenGL中的几种缓冲区和基本的清除缓冲区的操作,接着介绍下OpenGL中操作累积缓冲区的接口使用方法,最后介绍如何使用累积缓冲区完成抗锯齿(anti-alias)、运动模糊(Motion Blur)和深度场(Depth Field)的特效。在文章最后,使用glut库,提供了三种特效实现的程序。 示例代码下载地址:https://github.com/twinklingstar20/twinklingstar_cn_demo_accumulation_buffer/ 一. OpenGL中的缓冲区 1.1   缓冲区分类: OpenGL系统通常有以下几种缓冲区: (1)颜色缓冲区 OpenGL的颜色缓冲区主要有:front-left,front-right,back-left,back-right和一些辅助颜色缓冲区。我们绘制的图像就是存储在颜色缓冲区中,它可以存储颜色索引、RGB颜色数据以及alpha值,如果一些OpenGL实现支持立体渲染(stereoscopic rendering,立体的概念参见【6】,举个与立体3D的应用,例如3D电影或者3D显示器,戴上带有偏振效果的眼镜可以看立体电影),会有存储左右眼图像的左缓冲区和右缓冲区,相反,如果不支持立体渲染,则只有左缓冲区。在支持双缓存的系统中会有前缓冲区和后缓冲区,而单缓冲区系统只有前缓冲区。OpenGL可能还支持一些不用于显示的辅助颜色缓冲区,OpenGL并没有规定那些缓冲区的用途,所以你可以随意的使用那些缓冲区(前提是如果有)。 (2)深度缓冲区 深度缓冲区存储每个像素的深度值,深度值用于存储物体与人眼的距离,深度值越大的像素会被深度值小的像素覆盖,深度缓冲区有时候也称为Z缓冲区。 (3)模板缓冲区 模板缓冲区的一个应用就是限制屏幕上可显示的区域,举个例子,如果你要存储一个形状奇特的挡风玻璃,你只需要模板缓冲区存储这个挡风玻璃的形状,接着绘制整个屏幕,只有挡风玻璃所在区域中的内容会显示出来,这就是模板缓冲区的功能。 (4)累计缓冲区 后面会详细介绍。 1.2   清除缓冲区 在图形程序当中,清除缓冲区的开销非常的大,在一个1280*1024的显示器,就有上百万个像素。OpenGL专门设计了清除缓冲区的命令,充分利用体系结构的特点来解决这个问题。清除缓冲区,分两步走战略: (1)指定要用什么值来填充要清除的缓冲区,例如颜色缓冲区中所有的像素值都用(0,0,0)填充等,指定不同缓冲区值的方法如下所示,单从名字就可以看出指定哪类缓冲区要填充的值了。各个函数具体的参数和使用方法,可以参考【4】中的文档。 void glClearColor(); void glClearIndex(); void glClearDepth(); void glClearStencil( ); void glClearAccum(); (2)选择待清除的缓冲区并开始清除,OpenGL实现这个功能的函数如下所示,具体可以参考【4】中的文档。 void glClear(GLbitfield mask); 下面举个清除颜色缓冲区和深度缓冲区的例子,演示这清除的整个步骤,如下面的代码片段所示: glClearColor(0.0, 0.0,

spacer
浏览量:1,587

像素相关的操作

本篇文章主要包括四个部分,介绍与像素有关的一些操作,第1节,介绍光栅化在图形管线中所处的位置,像素图的概念和像素操作;第2节,介绍颜色混合在OpenGL中的应用,即像素操作在OpenGL当中的应用;第3节,主要介绍光栅位置和位图绘制,简单的介绍了glRasterPos*()和glBitmap()两个函数使用;第4节,主要图像管线,OpenGL当中像素是怎么处理,纹理图像如何在硬件端处理等,还介绍了其中主要的三个函数glDrawPixels()、glReadPixels()、glCopyPixels()用法。 示例代码的下载地址:https://github.com/twinklingstar20/twinklingstar_cn_demo_operation_about_pixels/ 一.      像素操作 光栅化就是把几何数据和像素数据转换为片段的过程,每个片段对应于帧缓冲区中的一个像素,每个片段包括颜色、深度、纹理坐标信息。如图1所示,很多步骤和测试会在“per fragment operations”中进行,之后片段会将像素值写进帧缓冲区(frame buffer)中。 图1 图形管线中的光栅阶段 一张图片在内存中是以像素图(pixmap)的形式保存,即一个矩形的数值数组,像素图随意的保存在内存中,能从一个地方复制到另一个地方,当它被复制到帧缓冲区后,会显示在显示器上。在一个像素图上,每个像素占用固定的位数(bit),固定的位数就是该像素的颜色深度(color depth)。如果颜色深度为1,即产生两种颜色的像素图,就称之为位图(bitmap);如果每个像素用一个字节表示,表示从0到255的灰度级,0表示黑色,255表示白色,称为灰度图像(gray-scale pixmap);通过LUT索引号来存储颜色,每个像素用颜色表(color lookup table,LUT)的一个索引来保存,通常颜色表有256个表项,所以索引用一个字节表示就够了;RGB图像,R、G、B三个颜色分量各占一个字节,这种图像可以称之为真彩图;RGBA图像,除了R、G、B三个颜色分量外,还包括一个alpha分量或者称为alpha通道(alpha channel)。 在一些环境下,我们希望将不同的像素图组合起来,生成一个新的像素图,这样的像素操作是非常有用的。用一个数学公式表示这个过程,如公式所示。其中⊕符号并不明确指定两个像素的操作类型,所以它可以是取平均值操作、加操作、减操作等等。 假设像素图C是通过像素图S和D计算得到,其中D表示目标像素图,S表示源像素图,如果C与D相同,则S和D像素操作后,还要将结果写回D中。这样将D中的像素读取到内存当中,通过与S中的像素操作并修改它的值,最后将结果又写回D中的整个操作称为读-修改-写周期,OpenGL对这种操作提供了一个很高效的方法。 回过来叙述下前面提到的RGBA图像,RGBA图像在图像混合上就起到了重要的作用,前面说的alpha值的取值范围是0到255,0表示完全透明,255表示完全不透明,alpha也通过作为一个0到1范围内的缩放因子,即alpha/255。因此在加入alpha通道后,两个图像的混合计算方法如下所示: D.R = a•S.R + (1-a)D.R; D.G = a•S.G + (1-a)D.G; D.B = a•S.B + (1-a)D.B;   其中a=S.A/255 二.      颜色混合在OpenGL中的应用 下面介绍像素混合在OpenGL中的应用,采用的函数主要有: 默认情况下,颜色混合是被禁用的,可以使用参数GL_BLEND将它激活。源图像指的是要画的图元,图元范围外的图像不进行混合操作,例如,要画一个正方形,只有正方形内的像素进行混合操作,正方形外的图像不进行。目标图像是指已存在帧缓存中的像素组成的图像。

spacer
浏览量:1,355

顶点数组

绘制一个简单的几何图元,OpenGL需要很多函数的调用,例如绘制一个20面的多边形,可能就会要求22个函数的调用,因为需要调用glBegin()和glEnd()两个函数;此外,绘制一个相邻多边形时,可能还存在很多顶点的冗余,例如绘制一个正方体,需要绘制6个面,每个顶点就会被处理3次。但是如果有了顶点数组(vertex array),就能把顶点放在一个数组中,然后调用一次函数就可以完成绘制,即使每个顶点存在一个法向量,也可以把法向量放在另一个数组中,并用另外一个函数进行调用。使用数组可以避免上述提到的函数的多次调用和顶点冗余的问题,从而提高性能。使用顶点数组渲染几何图元主要有以下三个步骤: 1.       激活相应类型的数组 共有8种数组:1)顶点坐标,2)平面法向量;3)RGBA颜色值;4)二次色(secondary color);5)颜色索引;6)雾坐标;7)纹理坐标;8)多边形边标志。 通过函数glEnableClientState()(相对应的是glDisableClientState())来激活相应的数组,理论上可以激活8个数组,但是实际上最多激活6个数组,例如GL_COLOR_ARRAY,GL_INDEX_ARRAY不可以同时激活。这里采用gl*ClientState()而不使用glEnble和glDisable()的原因是glEnable()和glDisable()可以存储在显示列表中,但是顶点的数组却不能,因为这些数据保存在客户端。 2.       把数据放在数组中 数组能通过地址来访问,在OpenGL的客户端-服务器模型中,这个数据被存储在客户端地址空间中。OpenGL提供了8个函数进行存储顶点数组中的数据,如下所示: (1)              glVertexPointer(): 顶点坐标数组 (2)              glNormalPointer():  法向量数组 (3)              glColorPointer():  颜色RGB数组 (4)              glIndexPointer():  索引颜色数组 (5)              glTexCoordPointer():  纹理坐标数组 (6)              glEdgeFlagPointer():  边标志数组 (7)              glSecondaryColorPointer():二级颜色数组。 glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* pointer) size:顶点坐标的维度, 2

spacer
浏览量:807

显示列表

一.OpenGL状态机 OpenGL是一个状态机,可以设置它的各种状态,这些状态会一直有效直到你改变了这些状态为止。例如,当前的状态就是一种状态,你可以设置当前的颜色为白色,红色等等,接下来画的每个对象都是对应的颜色,如画出的drawCube1(),drawCube2()画出的对象都是(1.0f,0.0f,0.0f)颜色,直到你设置为其它颜色(0.0f,1.0f,0.0f)为止,而颜色就是状态机中的一个状态。如下面的代码片段所示: glColor3f(1.0,0.0,0.0f); drawCube1(); drawCube2(); glColor3f(0.0f,1.0f,0.0f); OpenGL还包括其它状态,分别控制着当前的视点变换矩阵,投影变换矩阵,直线和多边形的点画模式,多边形绘图模式,像素打包转换,光照的位置和特点,物体的材质等。许多状态可以通过glEnable()和glDisable()进行控制。每个状态或者模式都有一个默认的值,在任何位置都能查询系统每个状态的值,典型的可以使用glGetBooleanv(),glGetDoublev(,glGetFloatv(),glGetIntegerv(),glGetPointerv()或者glIsEnabled()来查询。一些状态需要使用特别的查询命令,例如glGetLight*(),glGetError(),glGetPolygonStipple()。另外,你也可以将一组状态保存到属性堆栈(Attribute Stack)中,采用glPushAttrib()或者glPushClientAttrib()临时保存它们,用glPopAtrib()或者glPopClientAttrib()恢复这些状态。对于一些临时的状态改变,尽量采用这些状态保存和恢复的命令,而不是使用状态查询命令,这样更加高效。glPushClientAttrib()和glPopClientAttrib()分别用于存储和恢复客户端的状态变量,glPushAttrib()和glPopAttrib()分别用于存储和恢复服务器端的状态变量。 二.显示列表(Display List) 2.1. 显示列表设计哲学 OpenGL采用客户端-服务器模型,显示列表是服务器的一部分,常驻在服务器端,如果重复使用存储在显示列表中的命令,减小了通过网络发送数据的开销,将常用的命令存储在显示列表中可以提高性能,而一些图形硬件能将显示列表存储在硬件专用内存中或者将数据存储在与图形硬件或软件相兼容的优化格式中。 在硬件产商对OpenGL的实现中为了优化性能,显示列表是一个指令的缓存,而不是动态的数据库,换句话说,显示列表不能被修改。如果可以修改显示列表,则由于要搜索显示列表和内存管理等操作,会导致性能降低。在显示列表中指令的优化方法可能由于实现的不同而不同,例如,简单的命令glRotate*(),需要进行复杂的矩阵运算(涉及平方根,三角函数计算),通过显示列表可以产生显著的改善,因为在显示列表中,可能只有最终的旋转矩阵被存储起来,这就能与它的执行与硬件端执行glMultMatrix*()命令一样快。此外,一些复杂硬件产商的OpenGL实现版本中,可能只将相邻的几个变换转化一个简单的矩阵乘法。虽然不能保证OpenGL的实现能够优化一些特别用途的显示列表,但是执行显示列表不比分开执行包含在显示列表中的命令慢。由于跳转到显示列表需要一些开销,如果某些列表特别短,跳转的开销可能抵消掉执行快的优势。在下面几种情况下,更有可能进行优化: (1)       矩阵操作。很大一部分矩阵操作需要计算矩阵逆,在一些OpenGL实现中,显示列表可能存储计算后的矩阵和它的逆。 (2)       光栅化的位图和图像。一些光栅化的数据格式可能不是硬件的理想存储格式,但是通过显示列表的编译,OpenGL可能将数据转化为更适合硬件的存储格式,这在包含一系列位图的字体绘制、字符串绘制上速度有较大的提高。 (3)       光照、材质属性和光照模型。在绘制一个复杂的光照条件的场景时,可能需要改变每个表项的材质,设置材质是很慢的,如果把材质的定义放在显示列表中,每次变换材质时不需要再次进行计算,只需要存储在显示列表中的结果就行。 (4)       多边形的点画模式 2.2. 显示列表的使用 显示列表的使用的片段,如下所示: listName = glGenList(1); glNewList(listName,GL_COMPILE); glColor3f(1.0f,0.0f,0.0f); glBegin(GL_TRIANGLES); glVertex2f(0.0f,0.0f); glVertex2f(1.0f,0.0f); glVertex2f(0.0f,1.0f); glEnd(); glTranslatef(1.5f,0.0f,0.0f); glEndList(); 至此创建显示列表完成,当需要使用到该显示列表的内容时,调用glCallList()即可。glNewList()命令中可以使用两个参数GL_COMPILE和GL_COMPILE_AND_EXECUTE,GL_COMPILE表示创建显示列表时不立刻执行,GL_COMPILE_AND_EXECUTE表示立刻开始执行,并把显示列表创建起来供以后使用。glGenList (),glNewList(),glEndList(),glCallList()四种方法具体的使用方法,参见相应的文档。 并不是所有的OpenGL命令都能用显示列表存储和执行,例如,一些设置客户状态和查询状态值的命令就不能存储在显示列表中。

spacer