OpenGL变换

  • 概述
  • OpenGL变换矩阵
  • 实例:GL_MODELVIEW矩阵
  • 实例:GL_PROJECTION矩阵

概述

OpenGL管线中,在光栅化操作之前,包括顶点位置与法线向量的几何数据经顶点操作与图元装配操作进行变换。

  

模型坐标

它是模型对象的局部坐标系,同时也是任何变换之前模型对象的初始位置与朝向。为了变换模型对象,可以使用glRotatef()、glTranslatef()、glScalef()。

观察坐标

它由模型坐标乘以GL_MODELVIEW矩阵产生。在OpenGL中,可以使用GL_MODELVIEW矩阵将模型对象从模型空间转换到观察空间。GL_MODELVIEW矩阵由模型矩阵与视图矩阵相结合组成(Mview*Mmodel)。模型变换从模型空间变换到世界空间。视图变换从世界空间变换到观察空间。

注意,OpenGL中并没有独立的摄像机(视图)矩阵。因此,为了模仿摄像机变换或视图变换,场景(3D对象与光照)必须按视图变换的逆方向变换。换句话所,OpenGL定义定义摄像机总是位于观察空间坐标的(0,0,0)点且朝向Z轴负方向,且不能够发生变换。关于GL_MODELVIEW矩阵的更详细信息,请参考视图变换矩阵。

为了计算光照,法线向量也将从模型坐标变换到观察坐标。注意,法线向量的变换与顶点变换不同。它将法线向量与GL_MODELVIEW矩阵的逆矩阵的转置矩阵相乘。更详细信息,请参考法线向量变换

裁剪坐标

此时,观察坐标乘以GL_PROJECTION矩阵变换为裁剪坐标。GL_PROJECTION矩阵定义视锥体(平头截面),定义顶点数据如何投影到屏幕(透视或正视)。之所以被称为裁剪坐标,是因为变换后的顶点(x,y,z)通过与±w比较进行裁剪。关于GL_PROJECTION矩阵的更详细信息,请参考投影矩阵。

规范化设备坐标(MDC)

它由裁剪坐标除以w产生。这被称为透视除法。它更接近窗口(屏幕)坐标,不过它并没有经过移动与按屏幕像素缩放。此时,值得范围在3个坐标轴上都规范化为-1到1。

窗口坐标(屏幕坐标)

它由将规范化的设备坐标(NDC)进行视口变换产生。NDC被缩放与平移以适应渲染屏幕。窗口坐标最后经过OpenGL管线的光栅化过程变为片段。glViewport()用于定义最终图像映射的渲染区域矩形。glDepthRange()用于决定窗口坐标的z值。窗口坐标由上述两个函数的指定参数计算而来:

glViewport(x,y,w,h);

glDepthRange(n,f);

视口变换公式需要再NDC与窗口坐标之间进行线性关系映射:

OpenGL变换矩阵

OpenGL使用4×4矩阵。注意,矩阵中的16个元素以列优先顺序存储在1维数组中。如果你希望将它转换为标准习惯的行优先格式,你需要进行转置操作。

OpenGL具有4种不同类别的矩阵:GL_MODELVIEW、GL_PROJECTION、GL_TEXTIRE与GL_COLOR。你可以在代码中通过使用glMatrixMode()切换当前类型。例如,为了选择GL_MODELVIRE矩阵,需要使用glMatrixMode(GL_MODELVIEW)。

模型视图矩阵(GL_MODELVIEW)

GL_MODELVIEW矩阵将视图矩阵与模型矩阵组合成一个矩阵。为了变换视图矩阵,需要以逆变换方式移动整个场景。gluLookAt()主要用于设置视图变换。

最右面3列矩阵元素(m12,m13,m14)为平移变换,glTranslatef()。元素m15为其次坐标。它主要用于投影变换。

3元素集合(m0,m1,m2)、(m4,m5,m6)与(m8,m9,m10)是便于欧几里得与仿射变换,如旋转glRotatef()与缩放glScalef()。注意,这3个集合实际表示3个正交坐标轴:

  • (m0,m1,m2):+X轴,左向量,默认(1,0,0)
  • (m4,m5,m6):+Y轴,上向量,默认(0,1,0)
  • (m8,m9,m10):+Z轴,前向量,默认(0,0,1)

我们可以直接通过角度或观察向量构建GL_MODELVIEW矩阵,而不是变换函数。下面是构造GL_MODELVIEW矩阵的有用代码:

注意,当多个变换作用于一个顶点上时,OpenGL以相反顺序执行矩阵乘法。例如,当顶点首先通过MA进行变换,其次通过MB进行变换,OpenGL先执行MB×MA,然后再乘以该顶点。因此,在代码中,后一次变换先出现,第一次变换后出现。

// 注意,模型对象将先平移再旋转
glRotatef(angle, 1, 0, 0);   // 绕X轴旋转模型对象angle角度
glTranslatef(x, y, z);       // 移动模型对象到(x,y,z
drawObject();

投影矩阵(GL_PROJECTION)

GL_PROJECTION矩阵用于定义视锥体。视锥体决定哪些模型对象或部分模型对象将被裁剪掉。同时,它也决定3D场景如何投影到屏幕。(请阅读更详尽的如何构造投影矩阵。)

OpenGL提供两个GL_PROJECTION变换函数。glFrustum()用于产生一个透视投影,glOrtho()用于产生一个正交(平行)投影。这两个函数都需要6个参数以指定6个裁剪面:左裁剪面、右裁剪面、下裁剪面、上裁剪面、近裁剪面与远裁剪面。视锥体的8个顶点如下列图片。


OpenGL透视椎体

远(后)平面的顶点可以简单滴通过简单三角形比率计算出来,如,左远平面为:


OpenGL正交椎体

对于正交投影,该比率为1,因此远平面的左、右、下与上顶点值与近平面上的对应值相同。

你也可以使用更少参数的gluPerspective()与gluOrtho2D()函数。gluPerspective()仅仅需要4个参数:垂直视场(FOV)、宽高纵横比与远近裁剪面的距离。从gluPerspective()到glFrustum()的等价变换描述如下的代码:

// 创建对称截面体
// 从给定4个参数(fovy, aspect, near, far)
// 转换到glFrustum()的六个参数(l, r, b, t, n, f)
void makeFrustum(double fovY, double aspectRatio, double front, double back)
{
    const double DEG2RAD = 3.14159265 / 180;

    double tangent = tan(fovY/2 * DEG2RAD);   // 半fovY的正切值
    double height = front * tangent;          // 近平面半高度
    double width = height * aspectRatio;      // 近平面半宽度

    // 参数: left, right, bottom, top, near, far
    glFrustum(-width, width, -height, height, front, back);
}


对称视锥体示例

然而,如果你需要建立一个非对称视锥体,你必须直接使用glFrustum()。例如,如果你希望渲染宽场景到2个邻接屏幕上,你可以将截面体分成两个对称截面体(左与右)。然后,使用每一个截面体渲染场景。

纹理矩阵(GL_TEXTURE)

纹理坐标(s,t,r,q)在纹理映射之前乘以GL_TEXTURE矩阵。默认情况下,该纹理矩阵为单位矩阵,因此纹理会完全按照你赋值纹理坐标的方式映射到模型对象。通过改变GL_TEXTURE矩阵,你可以滑动、旋转、伸展与收缩纹理。

// 绕X轴旋转纹理
glMatrixMode(GL_TEXTURE);
glRotatef(angle, 1, 0, 0);

颜色矩阵(GL_COLOR)

颜色分量(r,g,b,a)与GL_COLOR矩阵相乘。这可用于颜色空间转换与颜色分量交换。GL_COLOR矩阵并不常用且需要GL_ARB_imaging扩展。

其他矩阵函数

glPushMatrix():将当前矩阵压入当前矩阵栈

glPopMatrix():将当前矩阵弹出当前矩阵栈

glLoadMatrix():设置当前矩阵为单位矩阵

glLoadMatrix{fd}(m):用矩阵m替换当前矩阵

glLoadTransposeMatrix{fd}(m):用行优先矩阵m替换当前矩阵

glMultMatrix{fd}(m):将当前矩阵乘以矩阵m,并更新当前矩阵值

glMultTransposeMatrix{fd}(m):将当前矩阵乘以行优先矩阵m,并更新当前矩阵值

glGetFloatv(GL_MODELVIEW_MATRIX, m):返回GL_MODELVIEW矩阵的16个值到m

实例:模型视图矩阵

这个例子程序展示如何使用glTranslatef()与glRotatef()操作GL_MODELVIEW矩阵。

下载源程序与二进制程序:matrixModelView.zip

注意,所有OpenGL函数调用在ModelGL.h与ModelGL.cpp中实现。

为了制定模型与相机变换,该实例程序使用自定义的4×4矩阵类与默认OpenGL矩阵函数。在ModelGL.cpp中定义3个矩阵对象:matrixModel、matrixView与matrixModelView。每个矩阵保存先前计算的矩阵,并且使用glLoadMatrix()传递这些矩阵元素。实际绘制程序像这样:

...
glPushMatrix();

// 为相机变换设置视图矩阵
glLoadMatrixf(matrixView.getTranspose());

// 在模型变换之前绘制网格
drawGrid();

// 为模型与视图变换设置模型视图矩阵
// 从模型对象空间变换到观察空间
glLoadMatrixf(matrixModelView.getTranspose());

// 在视图与模型变换之后绘制一个茶壶
drawTeapot();

glPopMatrix();
...

使用模型OpenGL矩阵函数的等价代码为:

...
glPushMatrix();

//初始化模型视图矩阵
glLoadIdentity();

// 首先,变换相机(视图矩阵):从世界空间到观察空间
// 注意所有变量都为负,因为我们将整个场景超相机变换相反方向移动
glRotatef(-cameraAngle[2], 0, 0, 1); // roll
glRotatef(-cameraAngle[1], 0, 1, 0); // heading
glRotatef(-cameraAngle[0], 1, 0, 0); // pitch
glTranslatef(-cameraPosition[0], -cameraPosition[1], -cameraPosition[2]);

// 在模型变换之前在原点绘制网格
drawGrid();

// 变换模型(模型矩阵)
// GL_MODELVIEW矩阵的结果为:ModelView_M = View_M * Model_M
glTranslatef(modelPosition[0], modelPosition[1], modelPosition[2]);
glRotatef(modelAngle[0], 1, 0, 0);
glRotatef(modelAngle[1], 0, 1, 0);
glRotatef(modelAngle[2], 0, 0, 1);

// 使用模型与视图变换绘制一个茶壶
drawTeapot();

glPopMatrix();
...

实例:投影矩阵

这个例子程序展示如何使用glFrustum()与glOrtho()操作投影变换。

下载源程序与二进制程序:matrixProjection.zip

同样,ModelGL.h与ModelGL.cpp是OpenGL函数调用放置的地方。

ModelGL类使用自定义矩阵对象,matrixProjection与2个成员函数:setFrustum()与setOrthoFrustum(),这两个函数与glFrustum()与glOrtho()等价。

///////////////////////////////////////////////////////////////////////////
// 使用与glFrustum()相同的6个参数(left,right,bottom,top,near,far)设置透视截面体// 注意: 此处为行优先标记法。OpenGL需要对它进行转置
///////////////////////////////////////////////////////////////////////////
void ModelGL::setFrustum(float l, float r, float b, float t, float n, float f)
{
    matrixProjection.identity();
    matrixProjection[0]  = 2 * n / (r - l);
    matrixProjection[2]  = (r + l) / (r - l);
    matrixProjection[5]  = 2 * n / (t - b);
    matrixProjection[6]  = (t + b) / (t - b);
    matrixProjection[10] = -(f + n) / (f - n);
    matrixProjection[11] = -(2 * f * n) / (f - n);
    matrixProjection[14] = -1;
    matrixProjection[15] = 0;
}

//////////////////////////////////////////////////////////////////////////////
// 使用与glOrtho()相类似的6个参数(left, right, bottom, top, near, far)设置正交投影
// 注意: 此处为行优先标记法。OpenGL需要对它进行转置
//////////////////////////////////////////////////////////////////////////////
void ModelGL::setOrthoFrustum(float l, float r, float b, float t, float n,
                              float f)
{
    matrixProjection.identity();
    matrixProjection[0]  = 2 / (r - l);
    matrixProjection[3]  = -(r + l) / (r - l);
    matrixProjection[5]  = 2 / (t - b);
    matrixProjection[7]  = -(t + b) / (t - b);
    matrixProjection[10] = -2 / (f - n);
    matrixProjection[11] = -(f + n) / (f - n);
}
...

// pass projection matrx to OpenGL before draw
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(matrixProjection.getTranspose());
...

英文原文:http://www.songho.ca/opengl/gl_normaltransform.html

OpenGL变换,布布扣,bubuko.com

时间: 2024-10-07 05:29:46

OpenGL变换的相关文章

openGL 变换06

变换 使用(多个)矩阵(Matrix) 对象可以更好的变换(Transform)一个物体. 向量 向量最基本的定义就是一个方向. 或者说 向量有一个方向(Direction)和大小(Magnitude,也叫做强度或长度). 向量可以在任意纬度(Dimension)上 ,但是我们通常只使用2至4维. 如果一个向量有两个纬度,它表示一个平面的方向(想象一下2D的图像),当它有3个纬度的时候它可以表达一个3D世界的方向. 下面图片会看到3个向量,每个向量在2D图像中都用一个箭头(x,y)表示.我们在2

SharpGL学习笔记(七) OpenGL的变换总结

笔者接触OpenGL最大的困难是: 经常调试一份代码时, 屏幕漆黑一片, 也不知道结果对不对,不知道如何是好! 这其实就是关于OpenGL"变换"的基础概念没有掌握好, 以至于对"将三维体正确的显示在屏幕上指定位置"这样的操作都无法完成. OpenGL变换包括计算机图形学中最基本的三维变换,即几何变换.投影变换.裁剪变换.视口变换,以及针对OpenGL的特殊变换概念理解和用法,如相机模拟.矩阵堆栈等,这些基础是开始真正走进三维世界无法绕过的基础. 所以笔者在前面花了

[转载]矩阵及变换,以及矩阵在DirectX和OpenGL中的运用问题:左乘/右乘,行优先/列优先

[转载]http://www.xuebuyuan.com/882848.html (一)首先,无论dx还是opengl,所表示的矢量和矩阵都是依据线性代数中的标准定义的:“矩阵A与B的乘积矩阵C的第i行第j列的元素c(ij)等于A的第i行于B的第j列的对应元素乘积的和.”(实用数学手册,科学出版社,第二版)例如c12 = a11*b11+a12*b21+a12*b13... (二)在明确了这一点后,然后我们再看“矩阵的存储方式”,矩阵存储方式有两种,一种是“行主序(row-major order

OpenGL投影矩阵

概述 透视投影 正交投影 概述 计算机显示器是一个2D平面.OpenGL渲染的3D场景必须以2D图像方式投影到计算机屏幕上.GL_PROJECTION矩阵用于该投影变换.首先,它将所有定点数据从观察坐标转换到裁减坐标.接着,这些裁减坐标通过除以w分量的方式转换到归一化设备坐标(NDC). 因此,我们需要记住一点:裁减变换(视锥剔除)与NDC变换都保存在GL_PROJECTION矩阵中.下述章节描述如何从6个限定参数(左.右.下.上.近平面.远平面)构建投影矩阵. 注意,视锥剔除(裁减)在裁减坐标

浅谈OpenGL变换矩阵

OpenGL中使用的矩阵全为列向量为主的矩阵. 参考OpenGL变换网站为  http://www.songho.ca/opengl/gl_transform.html 1.什么是GL_MODELVIEW矩阵? GL_MODELVIEW矩阵是Model矩阵和View矩阵的乘积,即M_modelview = M_view * M_model. model矩阵用于从物体坐标系到世界坐标系的转换,一般是通过旋转.平移或缩放得到. view矩阵用于从世界坐标系到摄像机坐标系的转换. 顶点的矩阵变换如下所

OpenGL入门学习

说起编程作图,大概还有很多人想起TC的#include <graphics.h>吧? 但是各位是否想过,那些画面绚丽的PC游戏是如何编写出来的?就靠TC那可怜的640*480分辨率.16色来做吗?显然是不行的. 本帖的目的是让大家放弃TC的老旧图形接口,让大家接触一些新事物. OpenGL作为当前主流的图形API之一,它在一些场合具有比DirectX更优越的特性. 1.与C语言紧密结合. OpenGL命令最初就是用C语言函数来进行描述的,对于学习过C语言的人来讲,OpenGL是容易理解和学习的

Opengl中矩阵和perspective/ortho的相互转换

Opengl中矩阵和perspective/ortho的相互转换 定义矩阵 Opengl变换需要用四维矩阵.我们来定义这样的矩阵. +BIT祝威+悄悄在此留下版了个权的信息说: 四维向量 首先,我们定义一个四维向量vec4. 1 /// <summary> 2 /// Represents a four dimensional vector. 3 /// </summary> 4 public struct vec4 5 { 6 public float x; 7 public f

OpenGL基础图形编程

一.OpenGL与3D图形世界1.1.OpenGL使人们进入三维图形世界 我们生活在一个充满三维物体的三维世界中,为了使计算机能精确地再现这些物体,我们必须能在三维空间描绘这些物体.我们又生活在一个充满信息的世界中,能否尽快地理解并运用这些信息将直接影响事业的成败,所以我们需要用一种最直接的形式来表示这些信息. 最近几年计算机图形学的发展使得三维表现技术得以形成,这些三维表现技术使我们能够再现三维世界中的物体,能够用三维形体来表示复杂的信息,这种技术就是可视化(Visualization)技术.

SharpGL学习笔记(八) 矩阵堆栈和变换的综合例子: 机器人

我们先引入关于"矩阵堆栈"的官方说法: OpenGL的矩阵堆栈指的就是内存中专门用来存放矩阵数据的某块特殊区域.实际上,在创建.装入.相乘模型变换和投影变换矩阵时,都已用到堆栈操作.一般说来,矩阵堆栈常用于构造具有继承性的模型,即由一些简单目标构成的复杂模型.例如,一辆自行车就是由两个轮子.一个三角架及其它一些零部件构成的.它的继承性表现在当自行车往前走时,首先是前轮旋转,然后整个车身向前平移,接着是后轮旋转,然后整个车身向前平移,如此进行下去,这样自行车就往前走了.矩阵堆栈对复杂模型