posts - 189,comments - 115,trackbacks - 0

  翻译openGL的FAQ--可视和转换 

观察和转换
----------------------------------------------------------------------------------
01.相机如何在OpenGL中工作?
02.我怎样才可以移动我的视点, 或者是相机,在我的场景中?
03.我的相机该去哪, 模型视点矩阵还是投射矩阵?
04.我该怎么执行一个缩放操作?
05.给出当前模型视点矩阵,我怎么才可以确定相机的物体空间位置?
06.我怎样才可以使照相机在我的场景中围绕一个点转?
07.如何自动计算一个观察点可以显示全部的场景?(我知道边界球体和向上分量(bounding sphere                            
   and up vector ). )
08.gluLookAt是如何工作的?
09.我怎样才可以得到一个在场景正中间的点?
10.我把我的gluLookAt调用放在我的投影矩阵中,现在雾, 光线, 纹理贴图都不能正确工作了, 究竟     
   发生什么了?
11.我怎样才可以建造一个立体的视觉?
12.我不能使转换正常的工作,哪里才可以知道更多的关于矩阵的东西?
13.OpenGL的矩阵是纵排列,还是横排列的?
14.OpenGL的坐标单位是什么?
15.坐标是如何转换的?坐标空间有什么不同?
16.我怎么样才可以转换我场景中的唯一一个物体或者给每个物体都有单独的变换?
17.如何才可以在3D渲染图上绘制2D图?
18.我如何才可以饶过OpenGL的矩阵转换, 而直接把2D坐标光栅化?
19.使用绝对坐标和相对坐标的优缺点在哪呢?
20.我怎样才可以画一个场景中的不同视图?
21.我怎么样才可以使我的物体围绕一个固定的坐标系统转换, 而不是本地的坐标系统?
22.相对于使用gluPerspective, 使用glFrustum的优缺点?
23.我怎样才可以调用glFrustum来匹配调用gluPerspective?
24.我怎样才可以画一个全屏幕的矩形?
25.对于一个给定的物体空间坐标, 我如何确定相应的屏幕坐标?
26.怎么样才可以找到一个屏幕上点的物体空间坐标?
27.怎样才可以找到一个被模型视点矩阵转换过的顶点坐标? 
28.怎样计算观察者到给定点的物体空间距离?
29.在窗口被重定义大小后, 我怎么样才可以保持我的比例系数?
30.我怎么样才可以使OpenGL使用左手坐标系?
31.怎样转换一个物体是它可以指向或者紧跟着另一个场景中的物体或点
32.我如何才可以使用倾角, 仰角,斜角来转换一个物体?
33.我怎样才可以渲染一个镜面?
34.我怎样才可以做我自己的透视缩放?

==================================================================================

1.OpenGL的照相机是如何工作的?

OpenGL是非常注意的,这里是没有照相机的.更确切的说是, 这个站相机总是位于眼空间坐标处( 0.0, 0.0, 0.0, 0.0 ).为了给出移动相机的效果,你必须反向的移动你的场景,把转换矩阵放入模型视点矩阵来实现.这就是通常被引用的视点转换.

在实际练习中, 这是一个和相机转换相等的等式, 但更有效率,因为更多的模型转换 和相机转换被连接到了一个单独的矩阵中去.因此,但相机且只有相机在模型视点当中时,这些操作是必须被执行的.比如,当安置一个光源在世界坐标系的时候, 当视点转换且只有视点转换被应用于模型视点矩阵的时候, 它是必须被重安置的.
---------------------------------------------------------------------------------
2.我如何才可以移动我的眼睛, 或者相机在我的场景中?

OpenGL并没有使用相机模型来提供一个接口完成这项工作.然后, GLU的库提供了一个gluLookAt函数, 它可以确定一个眼睛的位置, 一个可以看, 有向上向量,在物体空间中的坐标.这个函数可以根据它的参数,计算出它的相机的反向转换然后在乘以系统的当前矩阵.
----------------------------------------------------------------------------------
3.我的照相机该去哪儿, 模型视点矩阵还是投影矩阵?

这GL_PROJECTION矩阵只能够包含投影变换,它转换眼坐标到裁减坐标.

这GL_MODELVIEW矩阵, 正如它名字所暗示的那样,包含了模型和视点转换, 它将会转换物体的空间坐标到眼坐标.请记住把相机转换代码放入GL_MODELVIEW矩阵中去, 不要放入GL_PROJECTION 去.

考虑投影矩阵就好像在描述照相机的特性一样, 例如field of view, focal length, fish eye lens, etc.考虑模型视点矩阵, 就好像你和照相机的位置, 以及你的朝向.

在 gamedev FAQ有更多的描述两个矩阵的信息.

读 Steve Bakers's 文章关于滥用投影.这篇文章是被高度评价和推荐的.它帮助了很多的OpenGL的新程序员们.
----------------------------------------------------------------------------------
4. 我如何执行一个缩放操作?

一个简单的来缩放的方法就是使用统一的模型视点矩阵.然后, 这通常容易导致假如模型被放的太大的话, 会被zNear和zFar所裁减.

一个更好的方法就是通常对投影的视景体进行宽和高的限制.

例如,你的程序通常会根据用户的输入, 来提供一个缩放的参数,可能是一个浮点数.当设置了一个1.0的值后,将会没有缩放操作执行. 更大的值会有更大的缩放,和更多的关于视景体的限制, 而更小的值将会导致相反的情况.制造这种效果的代码有可能像这样:

static float zoomFactor; /* 全局变量, 假如你需要,可以通过用户输入来改变,初始值为1.0*/

/* 一段函数来设置投影矩阵.  可能的调用会来自一段典型的程序的重定义穿口大小的句柄. 将会              
   给出重画大小的宽和高的整数值. 使用正确的画面比例系数和缩放因子建造一个投影矩阵 */
void setProjectMatrix( int width, int height )
{
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective( 50.0*zoomFactor, (float)width/(float)height, zNear, zFar );
/* zNear 和 zFar 将会由你来填入*/

为了代替gluPerspective, 你的程序有可能使用glFrustum().这里有点小技巧,因为这个左, 右, 底,顶部参数, 还有近平面参数, 也会影响到可视域. 假设你想保持一个固定的zNear面的距离(非常合理的假设),glFrustum的代码可能会看起来如下:

glFrustum( left *zoomFactor, right *zoomFactor,
    bottom *zoomFactor, top *zoomFactor,
    zNear, zFar );
glOrtho()也是类似的.
----------------------------------------------------------------------------------
5.给出了当前的模型视点矩阵, 我如何确定相机的物体空间位置?

这个"相机"或者视点在眼坐标中是在( 0.0, 0.0, 0.0 ).当你把它转换成( 0, 0, 0, 1)并与模型视点矩阵的逆矩阵相乘时, 得出来的向量就是相机的物体空间坐标.

OpenGL并没有方法(使用glGet* 相关的函数)来得到模型视点矩阵的逆矩阵.你需要使用自己的代码来计算它.
----------------------------------------------------------------------------------
6.我怎样才可以使我场景中的照相机围绕一个顶点在转动?

你可以在同样的场景中通过平移/转动 场景/物体来模拟轨道           .例如, 围绕一个在Y轴上的物体, 而且不间断的望向原点, 有可能会这样写:

gluLookAt( camera[0], camera[1], camera[2], /* 相机位置 */
    0, 0, 0, /* 相机朝向原点 */
    0, 1, 0 ); /* 正Y轴方向 */ 
glRotatef( orbitDegree, 0.0f,1.0f, 0.0f ); /* 围绕 Y 轴 */
/* ... 旋转角度, 也可以来自于鼠标的运动 */

glCallList( SCENE ); /* 绘制场景 */
假如你坚持使用物理的移动这个相机的位置, 那么在把它放进你的模型视点转换中之前,你必须得转换当前相机位置的向量.

在任意一种事件中,我都建议你研究下gluLookAt(如果你已经没有是用它了);
----------------------------------------------------------------------------------
7.我如何才可以自动计算一个点,使它可以显示我场景中的全部模型(我知道球体范围和向上向量)?

接下来的是来自Dave Shreiner 设置的一个可视系统.

首先,计算所有在你的场景中的物体的球体范围. 这些将会提供给你两点信息: 球体的中心位置 (让 (cx, cy, cz) 来记录这点)和它的直径.

接下来,选择一个zNear剪裁面距离的值.大部分的建议都是选择一个大于,但接近一的数,  所以,让我们把它设为:

zNear = 1.0;
zFar  = zNear + diam;

按下面这种方式,组织你的矩阵调用(对一个正射投影矩阵)

GLdouble left = c.x - diam;
GLdouble right = c.x + diam;
GLdouble bottom = c.y - diam;
GLdouble top = c.y + diam;

glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glOrtho( left, right, bottom, top, zNear, zFar );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();

这种方法会设置你的物体在窗口的正中心, 伸展边界以符合.(i.e.,这个是假设你窗口的比例系数为1.0).假如你的窗口是非正方形的, 将会按上面的方式计算左, 右, 上, 下, 在调用glOrtho以前放入下面的逻辑代码段中.

GLdouble aspect = (GLdouble) windowWidth / windowHeight;

if ( aspect < 1.0 ) { /* 窗口的高比宽长 */
bottom /= aspect;
top /= aspect;
} else {
left *= aspect;
right *= aspect;
}

上面的的代码将会把你的 场景中的物体放在相对正确的位置. 假如你想试着去操作它( i.e.旋转,etc), 你需要增加一个可视转换.

一个典型的可视转换将会和模型视点矩阵想混和, 而且看起来,应该是这样:

gluLookAt( 0.0, 0.0, 2.0 * diam, c.x, c.y, c.z, 0.0, 1.0, 0.0 );
----------------------------------------------------------------------------------
8.为什么gluLookAt()不能工作?

这个通常是被不正确的转换所导致的.

假设你使用投影矩阵堆栈中的gluPerspective的zNear, zFar来作为第三, 第四个参数,你就必须设置模型视点矩阵堆栈上的gluLookAt(), 并传递相应的参数,使你的物体, 可以落在zNear和zFar之间.

当你正试图去了解可视转换的时候,最好的方法就是写一些代码来做试验.让我们想一下,你正在看一个位于原点的一单位的球体.你可能想按下面的方式来设置你的转换:

glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective( 50.0, 1.0, 3.0, 7.0 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
gluLookAt( 0.0, 0.0, 5.0,
    0.0, 0.0, 0.0, 
    0.0, 1.0, 0.0 );

非常重要的一点就是,知道投影和模型视点矩阵是如何一起工作的.

在这个例子中, 这个投影转换,设立了一个50度的可视角度的观察域, 和一个窗口的为1的比例系数. 这个zNear的剪裁面是视点的前三个单位,这个zFar的剪裁面的是视点前的7个单位.它们分配了一个在Z轴长度为4的视景体,对于一个单位的球体是绝对足够的.

这个模型视点转换设置了眼坐标为(0.0, 0.0, 5.0), 而所观看的点是原点,也就是我们单位圆的正中心.请注意这个眼坐标的位置离观察点的位置有5个单位的距离. 这点是相当重要的,因为眼睛前面的5个单位的距离是在投影转换所定义的视景体的正中间.假如这个gluLookAt()把眼睛设置在( 0.0, 0.0, 1.0 )它只会离原点一个单位的距离.它将会没有足够的长度去包含物体在视景体中, 物体将会被zNear所剪裁.

相同地,假如你把眼坐标设置在( 0.0, 0.0, 10.0 ), 10个单位的离观察点的距离,将会导致这个球体和视点有10个单位的距离, 并且被zFar剪裁面在7单位距离处, 被剪裁.

假如这些东西, 困扰着你,读OpenGL红皮书,或者OpenGL详解(specification)中有关于转换的内容.在你理解了物体空间坐标, 眼坐标空间,和剪裁面坐标空间, 这些上面的知识, 将会变的非常的容易, 清晰. 最好, 在经常写些小的测试程序来实践一下. 假如你在你的主程序中依然有相当的困难来使用正确的变换矩阵,那么写些更简单几何体的测试程序是非常有意义的.
--------------------------------------------------------------------------------
9.我如何去设置一个精确的点在场景的中间显示?

gluLookAt是非常容易做到这个的.简单的设置你的点的X, Y, 和Z值作为你的gluLookAt的第三, 第四, 第5个参数.
---------------------------------------------------------------------------------
10.我把我的gluLookAt调用放入我的投影矩阵中, 现在雾,光线,和纹理映射都不能正常工作了, 这  
   是为什么?

这个通常是因为把转换放在错误的矩阵中所导致的, 看下问题三对这个问题的解释. 然后也有可能, 是因为特殊的光照问题, 所导致的,因为有错误的矩阵被放入模型视点矩阵中,然后定义了光照的位置. 当一个光源点被定位在眼空间坐标中,i.e.和眼睛相关,它将会被重新定位到一个单位矩阵在模型视点矩阵堆栈中的时候.当可视矩阵被放入模型视点堆栈中时, 原先世界坐标系中的光源位置讲会被重定位. 当一个光源的位置是和一个转换的物体相关的时候, 当物体的模型矩阵和在模型视点矩阵堆栈中的可视矩阵想乘时,这个光源也会被重新定位,请记住在任何的光源被渲染前, 它都有可能被重新定位.假如光源在结构中相对于眼睛在移动, 那么每个结构都必须使用合适的矩阵来进行重定位.
----------------------------------------------------------------------------------
11.我如何可以建造一个立体视图?

立体视图就是通过展现观察者观察到的左眼和右眼所看见的画面来形成的.这些图像必须正确的匹配到显示器上,一符合观察者实际所看到的, 更多的习惯是使用多于一副的3D图像. 附加一点就是这种方法会和被使用的显示器技术有相当紧密的联系. 一些图形系统和显示设备, 会在硬件上支持立体视图和支持像左缓存和右缓存, 另外就是在系统上支持双缓存的前缓存和后缓存. 另外的系统就会在显示屏幕显示两个视口来支持立体视图, 并详细定义显卡的模式来发送这些图像到显示屏幕.另外与此相关的就是观察者通常带着一个特殊的眼镜,它会自动对图像进行拣选来以此来符合每个眼睛所看到的.然而即使没有这些图像特性, 一个开发者仍然可以使用颜色过滤在一些基于红或蓝色过滤器的选择图像的地方来实现立体视图,例如画左眼和右眼的图像到到红,蓝帧缓中来实现这点. 或者更加简单的就是有多个系统或是图形显卡(或者一块显卡)来产生两个完全分离的视频信号, 来分别画一个左眼的和右眼的图像.这个视频将会被发送到正确的眼睛中,使用一个display employing polarizing filters 或者一个 head mounted display 或者一些别的用户自己的显示设备, 执行一些原理相同的操作.

从一个OpenGL透视投影中, 立体渲染的设备将会使用合适的操作来渲染到左眼和右眼(作图像掩码, 分离上下文 或是不同视口)并且匹配到与OpenGL投影相关的观察者的左眼和右眼的几何图像到显示器上. 这个最后的OpenGL设备要求是这个在"虚拟"世界中的眼睛位置必须得来自模型视点矩阵中的分离的瞳孔位置, 这种分离会在眼空间坐标产生一个转换 但不会在别的相同的方法中被计算.

Paul Bourke 整理了很多关于立体OpenGL立体视图的资料:

· 3D Stereo Rendering Using OpenGL
· Calculating Stereo Pairs
· Creating Anaglyphs using OpenGL
----------------------------------------------------------------------------------
12.我不能使转换正常的工作. 我可以从哪里得到更多的关于矩阵的知识?

一个系统的关于基础的矩阵数学和线性代数的知识已经超出这份FAQ的范围. 相关的概念已经在美国的高校数学课上解释过了。

假如你知道这些基本概念, 但还是有点糊涂(一个共同的问题就是经验), 读下Steve Baker's 
review of matrix concepts  和他的 article on Euler angles.

执行基本向量, 矩阵, 四元数的操作的delphi代码 可以在这里找到.
----------------------------------------------------------------------------------
13.OpenGL的矩阵是行排列的, 还是纵排列的?

出于编程的原因, OpenGL的矩阵是一个在内存中依次相连的16元素的数组,转换部分占据了16元素的矩阵中的第13, 14, 15, 16位置.

纵队排列相对横队排列纯粹就是一种符号协定. 注意自右的和纵排列矩阵相乘将会产生和自左的和行排列矩阵相乘产生同样的结果. 在 OpenGL详解和OpenGL参考手册都使用了纵排列矩阵. 你可以使用任意一种, 只要你清楚的声明了。

不辛地,使用在spec和蓝皮书中的纵排列格式会在OpenGL社区中导致无止境的困扰.纵排列的格式会导致一个程序员不期望的矩阵在内存中的排列格式.

更多的关于这方面的内容, 请点击:这儿.
----------------------------------------------------------------------------------
14.OpenGL的坐标单位是什么?

最简短的回答就是. 只要你希望, 它可以使用任何你所希望的单位.

根据你几何数据库中的内容, 对于你的程序可能非常方便的处理OpenGL的坐标单位,比如一个毫米,或者一光年, 或者这其中的任何距离(或大或小).

OpenGL也允许你自己定义你的几何体使用不同值的坐标系统.例如, 你可能会发现在对飞机模型控制的时候使用厘米, 而对机身使用米, 而对所飞行的空间使用千米.OpenGL的模型视点矩阵可以缩放不同的坐标系统来形成同样的眼坐标系统.

程序有义务来确定投影和模型视点矩阵的构造所提供的图像和观察者保持一个合适的距离, 和合适的可视域, 以及能够保持zNear和zFar剪裁面在一个合适的范围内.一个在微米程度缩放的显示分子的程序,  例如, 就不能把观察者放在一个10英尺距离, 60度观察域的地方.
----------------------------------------------------------------------------------
15.坐标是如何转换的?坐标空间有什么不同?

物体坐标被模型视点矩阵所转换来产生眼坐标.

眼坐标被投影矩阵所转换来产生裁减坐标.

裁减坐标 X, Y, Z将会被裁减坐标W所除来产生设备规范化坐标.

设备规范化坐标会被视口参数所缩放和转移来产生窗口坐标.

物体坐标也就是当你提交给OpenGL glVertex*()或着是 glVertexPointer()的时候的原始坐标.它将会显示你的物体或者别的你想渲染的几何体的坐标.

许多的程序设计者使用世界坐标系统. 物体通常在同一个坐标系统中被模型化, 缩放, 平移, 旋转都会在你的建造的世界中转换. 世界坐标系统是通过存储在模型视点矩阵中的模型转换而形成的物体坐标转换而来. 然而,OpenGL并没有世界坐标系统的概念. 世界坐标系统纯粹就是一个程序架构.

眼坐标是通过被模型视点矩阵转换的物体坐标而来. 这个模型视点矩阵中包含了模型转换和视点转换, 它定义了观察者位于原点,而且朝向Z的负半轴.

裁减坐标是由眼坐标通过投影转换而来.裁减坐标空间范围是从 -Wc 到 Wc 在所有的三个轴方向上, Wc 是裁减坐标W的值. OpenGL会裁减所有位于范围之外的东西.

裁减坐标上所执行的透视除法会产生标准设备规范化坐标, 在三个轴的范围是从 -1 到 1.

窗口坐标是来通过视口来缩放,平移设备规范化坐标转换而来. glViewport()和glDepthRange()的参数控制这些转换.使用视口, 你可以映射设备规范化坐标的立方体到你的窗口的任何位置 和深度缓存中.

更多的信息, 请看 OpenGL specification , 图解2.6
----------------------------------------------------------------------------------
16. 我如何转换我场景中的唯一物体或者是给我场景中的每一个物体都有单独转换?

OpenGL提供了矩阵堆栈来实现这个功能. 这样的话, 就是使用模型视点矩阵堆栈.

一个典型的OpenGL程序第一次设置这个矩阵模式是使用 glMatrixMode( GL_MODELVIEW )并且加载一个可视转换, 例如一个到gluLookAt().更多的信息可利用的在 gluLookAt.

然后这个代码渲染每个场景中的物体使用他们自己的转换通过包装这个调用glPushMatrix()和glPopMatrix().例如:

glPushMatrix();
glRotatef( 90.0, 1.0, 0.0, 0.0 );
gluCylinder( quad, 1, 1, 2, 36, 12 );
glPopMatrix();

上面的代码渲染一个圆柱体围绕X轴旋转90度. 这个模型视点矩阵将会被重新存储为先前的值在glPopMatrix()调用后. 相似的调用序列可以渲染场景中的并发物体.

17.我如何在我的3D渲染上, 绘制我的2D控制器?

基本的策略是对于要描绘的控制点设置一个2D投影.你既可以在你的3D渲染上做这些, 也可以在覆盖面上来完成这个. 假如你选择在3D渲染上绘制这些, 那么在每帧结束的时候, 都要重画这些控制点(在交换缓存前, 立即重画). 假如你选择画在覆盖面上, 你只需要在跟新的时候重画.

为了设置一个2D投影, 你需要改变这个投影矩阵. 正常地,这是非常容易设置一个世界坐标的单元, 来对应于一个屏幕的像素, 像这样:

glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluOrtho2D( 0, windowWidth, 0, windowHeight );

gluOrtho2D()设置一个-1.0到1.0的Z轴的范围, 所以你可以使用glVertex2*()函数中的一种来确保你的几何图元不会被zNear和zFar剪裁面所剪裁.

正常地, 当绘制2D控制点的时候, 都把模型视点矩阵设置会单位矩阵.所以你会发现非常容易做些别的( 例如, 你可以使用交叉存取的平移矩阵来重复的画控制点).

假如需要精确的像素操作, 你可以把一个小型的平移放入模型视点矩阵, 如下所示:

glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glTranslatef( 0.375, 0.375, 0.0 );

假如你在一个3D 深度缓存的图像上进行绘制, 当你绘制你的2D几何图元你需要关闭深度测试(depth test). 你可以通过调用glDisable( GL_DEPTH_TEST ) 或者是glDepthFunc( GL_ALWASY ).根据你的程序, 你可能在开始2D绘制的时候简单的清除了深度缓存. 最终, 绘制2D几何图元在你最你的Z轴深度上, 也不失为一个解决方法。

在2D投影矩阵按如上所示的建立后,你就可以渲染正确的OpenGL图元到屏幕上, 详细的定义他们的X, Y像素的地址( 使用OpenGL 中心的屏幕坐标, (0, 0)在左下角).
---------------------------------------------------------------------------------
18.我如何越过OpenGL矩阵变换而直接让2D坐标光栅化?

这并不是OpenGL矩阵转换中的一种开关模式. 然而, 假如你使用了glLoadIdentity()调用设置了设置了两个单位矩阵, 典型的OpenGL执行有足够的智力,来知道这是一个单位矩阵转换是不用操作的并因此来行动.

更多的有关纯粹把OpenGL当做一个光栅化的API的信息, 请参考:OpenGL Game Developer's FAQ.
----------------------------------------------------------------------------------
19.使用绝对坐标,或者是相对坐标的好处, 和坏处?

一些OpenGL可能需要在单一的场景中使用更多的坐标来渲染同样的物体.OpenGL有两中方法做这个:

 1.使用"绝对坐标".支持每个物体的多份拷贝, 每个都有他们自己唯一的顶点集合.你不需要去改变   
   模型视点矩阵在需要的位置对物体进行渲染.
 2.使用"相对坐标".会保存物体的唯一一 份拷贝, 通过压入模型视点矩阵堆栈, 设置需要的转换,   
   发送几何数据, 并弹出堆栈来对他们进行多次的渲染. 对每个物体重复这些步骤.

一般来说, 经常的改变状态, 例如模型视点矩阵, 会对你的效率有负面的影响. 假如你没有对每个单独的图元都在模型视点转换使用大量的转换, 那OpenGL是可以快速的处理你的几何数据的.

然而,你有时候可能需要权衡对图像进行多分的拷贝来放弃对内存的节省. 让我们假设你定义了一个高拟真度的门把手,使用了大约200, 300个三角形, 而你对一个房子的建模将会使用50个门把手,它们都使用了相同的门把手.  这个更加好的是使用单独门把手的显示列表, 然后定义多个单独的矩阵转换, 而不是在内存使用大约10-15K的空间来定义绝对坐标.

当有更多的计算主题时, 那么不得不在CPU时间和存储空间之间进行选择. 而你也不得不对此进行更多的尝试.
----------------------------------------------------------------------------------
20.我如何在我的场景中绘制更多的视图?

你可以通过使用glViewport()调用在同样的窗口绘制两个视图. 设置glViewport()对准那个你想作为第一个视图的地区, 设置你的场景观察, 和渲染. 然后设置glViewport()对准那个你想作为第二个视图的地区, 再一次设置你的场景观察, 和渲染.

你可能需要注意有些操作并不会为glViewport()所注意, 例如glSwapBuffers()和glClear(). glSwapBuffers()总是交换当前的整个窗口. 然而, 你可以通过使用剪取矩形来限制你的glClear()专门只对一个矩形窗口.

你的程序有可能只允许在不同的窗口中使用不同的视图. 假如这样, 你需要在两个渲染器中执行一个针对当前的操作. 假如两个窗口共享一个上下文环境, 你需要向上面描述的那样改变场景的视图. 假如你的程序对每个窗口使用分离的上下文, 那么这些操作是没有必要的.
----------------------------------------------------------------------------------
21.我如何使我的物体围绕一个固定的坐标系统, 而不是物体的本地坐标系统?

假如你使一个物体, 围绕Y轴旋转, 你就会发现X轴, 和Y轴在围绕这个物体旋转.一个后续的旋转围绕这些轴进行新的转换轴旋转, 而不使用原始的轴. 在一个固定坐标轴系统中, 而不是物体本地坐标系统中, 执行转换是非常另人满意的.

这个 OpenGL Game Developer's FAQ 包含了使用四元数来存储转换的信息, 在解决这类问题时, 这些资料有可能很有用.

导致这个问题的主要原因是OpenGL矩阵操作是在矩阵堆栈上的自右乘法, 因此导致了出现在物体空间的转换.为了影响屏幕空间转换, 你需要使用自左乘法. OpenGL并没有提供模式的开关来对应矩阵相乘的顺序, 所以你需要手动自左乘法.一个程序可能在每帧后通过取回当前矩阵来对此进行实现. 这个程序乘以新的转换来对应于单位矩阵上的下一帧, 并乘以使用glMultiMatrix()的矩阵上的堆积的当前转换(来自上一帧).

你需要注意的就是每次得到一帧的模型视点矩阵都可能对你的程序执行效率有负面的影响. 然而你可以对这些操作进行测试, 因为这个执行效率是对应于不同的执行而不同的 .
----------------------------------------------------------------------------------
22.使用glFrustum相对于gluPerspective的好处, 于坏处?为什么我们

glFrustum()和gluPerspective()都会产生透视投影矩阵, 它们把眼坐标转换到剪裁坐标而来.这个主要的不同在于glFrustum更多的是产生和允许离轴(off-axis)的矩阵, 而gluperspective可以产生对称的on-axis的矩阵. 确实, 你可以使用glFrustum来顶替gluPerspective. 然而,除了这个层次的调用本身是glu接口本身的一部分外,  使用glFrustum所产生的矩阵对于gluPerspective来说, 本身也没有什么特殊的性能优势.

自从glFrustum相对于gluPerspective更加的常规, 你就可以在万一不能使用gluPerspective的时候, 使用它. 一些例子包括在 projection shadeows, 平铺渲染, 和立体视图中.

平铺渲染 使用在多非轴线矩阵来渲染场景中的不同地方. 这个结果是集合了一个大型的图象数据数组来产生最终的图象 这个是非常必要的当这个最终渲染的期望的维数超过了OpenGL所能执行的最大视口维数.

在立体视图中, 同样的场景的两个渲染图将会被作为有轻微的改变的可视观察位置. 当这个观察轴在眼睛的右边, 每个视图都必须使用轻微的off-axis矩阵到任一边, 来达到正确的可视结果.
---------------------------------------------------------------------------------
23.我如何执行一个到glFrustum的调用来匹配我对gluPerspective()?

这个你的glFrustum()调用的可视的角度(fov)是:

 fov * 0.5 = arctan( (top - bottom) * 0.5 / near )

当 bottom == -top 对于由gluPerspective所产生的对称投影, 然后:

 top = (tan( fov * 0.5)) * near;
 bottom = -top 

注意: 上面公式中的fov的单位必须使用弧度,来配合在C语言的数学库. 假如你想使用角度计算FOV(例如调用gluPerspective), 需要这样进行计算:

  top = tan( fov * 3.1415926 / 360 )

左边和右边的参数,只需要简单的使用 top, bottom, aspect来计算:

 left = aspect * bottom
 right = aspect * topp

OpenGL Reference Manual 显示了使用双函数所产生的矩阵.
----------------------------------------------------------------------------------
24. 我如何画一个全屏幕的四边形?

这个问题通常的意思是, " 我如何画一个可以填充我全部视口的四边形?" 有很多方法可以做到.

最直接的方法就是先设置你所希望的颜色, 设置投影和模型视点矩阵为单位矩阵, 并调用glRectf()来画一个和GL_QUADS相等的图元. 你的矩形或者四面体的Z轴深度应该在-1.0到1.0之间,  -1.0映射到zNear剪裁面, 1.0映射到zFar剪裁面.

这里有个例子, 这儿有个如何画一个全屏幕的四面体在zNear剪裁面上:

glMatrixMode( GL_MODELVIEW  );
glPushMatrix();
glLoadIdentity();
glMatrixMode( GL_PROJECTION );
glPushMatrix();
glLoadIdentity();
glBegin( GL_QUADS );
glVertex3i( -1, -1, -1 );
glVertex3i(  1, -1, -1 );
glVertex3i(  1,  1, -1 );
glVertex3i( -1,  1, -1 );
glEnd();
glPopMatrix();
glMatrixMode( GL_MODELVIEW );
glPopMatrix();

你的程序, 有可能是需要相应的矩形有个最大的Z轴值, 那样的话, 你只需要修改Z轴用1来代替-1.

当绘制一个全屏幕的矩形时, 你可能屏蔽掉一些缓冲区来让只有特殊的缓冲区可被影响. 例如, 你可能屏蔽掉颜色缓冲区并设置深度函数为 GL_ALWAYS, 所以就只有深度缓冲区被绘制.你也可以设置 码区(mask)来允许模板缓存被设置或者更多的别的复合缓存.
----------------------------------------------------------------------------------
25.我如何找到一个给定物体的空间坐标所对应的屏幕坐标?

假如你只打算得到少数的几个点, 你可以使用GLU库中中的gluProject()函数. 假如要大堆的坐标, 使用系统的回掉机制可以更有效率.

为了使用gluProject(),你必须提供模型视点矩阵, 投影矩阵, 视口, 和输入的物体空间坐标.屏幕空间的坐标将会被以 X, Y, Z的形式返回, 其中Z会被归一化(normalize).
----------------------------------------------------------------------------------
26.我如何可以找到屏幕上一个像素所在的物体空间坐标?

这个GLU库提供了gluUnProject()函数来实现这个操作.

你需要读取深度缓存来获得屏幕上的坐标的Z轴值在 X, Y处, 这段代码可以如下.

GLdouble z;
glReadPixels( x, y, 1, 1, GL_DEPTH_COMPONENT, GL_DOUBLE, &z );

注意 X和Y是相对于屏幕左下角的(0, 0)位置.

你需要提供屏幕空间的X, Y和Z值作为 gluUnProject的输入,另外还有当前像素正在被渲染的模型视点矩阵, 投影矩阵, 和视口.
----------------------------------------------------------------------------------
27.我如何可以找到被模型视点矩阵转换过的像素坐标?

这个通常被用于得到顶点的眼坐标空间的值(i.e,物体的空间顶点被模型视点所转换).你可以通过得到当前的模型视点矩阵并执行向量和矩阵的乘法来得到.
----------------------------------------------------------------------------------28.我如何计算观察者到给定点的物体空间距离?

转换这点到眼坐标空间,通过乘以模型视点矩阵. 然后简单的计算它到原点的距离.(假如这个不能正常的工作, 你可能是把视点转换放入了投影矩阵堆栈中了.)

来得到当前的模型视点矩阵:

 GLfloat m[16];
 glGetFloatv( GL_MODELVIEW_MATRIX, m );

对于使用任何的OpenGL调用, 你都可以使用glGet*()函数来得当前的窗口的上下文.
----------------------------------------------------------------------------------
29.在一个窗口被重新定义大小后, 我如何保持我的比例系数?

这个是基于你是如何设置你的投影矩阵. 对于任何一种情况, 你都需要知道你的窗口的维数(宽和高). 如何得到这些是基于你使用的平台. 在GLUT, 例如, 这个维数是作为参数传递给 reshape回调函数.

接下来的是假设你的视口都使用和你的窗口同样的尺寸. 假如你没有, 使用窗口高度和宽度来代替视口的高度和宽度.

假如你使用gluPerspective来设置你的投影矩阵, 第二个参数是控制这个比例系数. 当你的程序捕捉到了一个新的大小, 你需要修改的投影矩阵如下:

 glMatrixMode( GL_PROJECTION );
 glLoadIdentity();
 gluPerspective( fov, (float) width/(float) height, zNear, zFar );

假如你使用glFrustum, 这个比例系数是将会是使用视景体的宽度和高度. 你可是使用下面的重设窗口大小代码来支持1:1的比例系数:

 float cx, halfWidth = windowWidth * 0.5f;
 float aspect = (float)windowWidth/(float)windowHeight;

 glMatrixMode( GL_PROJECTION );
 glLoadIdentity();
 /* cx is the eye space center of the zNear plane in X */
 glFrustum( cx - halfWidth*aspect, cx + halfWidth*aspect, bottom, top, zNear, zFar);

glOrtho()和gluOrho2D() 的调用视和glFrustum()非常相似的.
----------------------------------------------------------------------------------
30. 我可以让OpenGL使用一个左手系空间?

OpenGL 并没有这样一个模式开关来转换左右手的坐标. 然而,你可以非常容易的通过乘以以个负的Z轴缩放到模型视点矩阵来得到左手坐标系统. 例如:

 glMatrixMode( GL_PROJECTION );
 glLoadIdentity();
 glScalef( 1.0, 1.0, -1.0 );
 /* 像平常那样乘以视点转换 */
 /* 像平常那样乘以模型转换 */
----------------------------------------------------------------------------------
31.我如何转换一个物体使它指向或者跟随场景中的一个物体,或点?

你需要构造一个矩阵 , 它可以转换你的物体的本地坐标系统到一个你所希望面对的方向的坐标系统. 看 example code , 是如何建造这类矩阵的.

假如你仅仅指是想渲染一个物体, 使它可以面对观察者, 你可以考虑通过在眼坐标空间中把模型视点矩阵设置成单位矩阵来实现. 
----------------------------------------------------------------------------------
32.我如何转变我的物体使用一个给定的倾角, 仰角, 和斜角?

转换矩阵的左上角的3X3部分, 是由自右转换坐标空间的 X, Y, Z所构成.


----------------------------------------------------------------------------------
33.我该如何渲染处镜面效果?

把你的场景渲染两次, 其中一次, 作为你的镜面的反光. 还有一次,作为来自正常的视图(非反射的). Example Code 示范了这种技术.

对于一个轴对齐的镜面, 例如一个在YZ平面上的镜面. 这个反射场景可以使用简单的缩放和平移. 使用 -1.0来乘以镜面的法向量, 并进行缩放. 并平移两倍的镜面距离.使用适当的转换来渲染这些场景会产生场景在镜面中的反射.使用矩阵堆栈来保存上一次的可视转换的值.

下一步, 使用glClear( GL_DEPTH_BUFFER_BIT )来清空深度缓存.然后渲染镜面.对于一个完美反射的镜面, 只需要简单的渲染深度缓存. 真正的镜面通常是不完美反射的, 他们很容易被一些别的光线所扰乱。为了建造这种效果, 使用混合技术来渲染一个镜面使用0.05的alpha值. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_ALPHA)对于这个目的来说, 是个很好的混合函数.

最终, 渲染这些非反射场景. 当这个完全的反射场景存在于颜色缓存中, 而不是部分的场景在镜面中.你可能需要改变反射场景中的全部像素来覆盖这个区域,这样它们会变的不可见.
--------------------------------------------------------------------------------
34.我如何做我自己的透视缩放?

OpenGL会用模型视点矩阵乘以你的坐标系, 然后通过投影矩阵来得到剪裁坐标系统. 然后会执行透视除法来获得规范化设备坐标系.在透视除法阶段会建立一个透视渲染, 使一个在远处的几何体会比在近处的小.透视除法阶段通过用裁减坐标X, Y, Z的值来除以W来完成的. 例如:

 Xndc = Xcc/Wcc;
 Yndc = Ycc/Wcc;
 Zndc = Zcc/Wcc;

为了执行你自己的透视纠正, 你需要获得裁减坐标W的值.回掉(feedback)缓存提供了同样的坐标系统对于XYZ设备坐标和W剪裁坐标.你可以使用 glGetFloatv(GL_CURRENT_RASTER_POSITION, ... ), 而且W值会再一次在裁减坐标系统中, XYZ在设备坐标系统中。


posted on 2010-08-24 13:54 MEYE 阅读(338) 评论(0)  编辑  收藏 所属分类: Android3D

只有注册用户登录后才能发表评论。


网站导航: