http://www.tuicool.com/articles/uiayYrI

OpenGL学习脚印: 坐标变换过程(vertex transformation)

http://blog.csdn.net/wangdingqiaoit/article/details/51594408

写在前面 
前面几节分别介绍了模型变换视变换,以及给出了投影矩阵和视口变换矩阵的推导,本节从全局把握一遍OpenGL坐标转换的过程,从整体上认识坐标变换过程。相关矩阵的数学推导过程请参考前面几节对应的内容。

通过本节可以了解到

  • 坐标变换的各个阶段
  • 利用GLM数学库实现坐标变换

坐标变换的全局图

OpenGL中的坐标处理过程包括模型变换、视变换、投影变换、视口变换等过程,如下图所示: 

在上面的图中,注意,OpenGL只定义了裁剪坐标系、规范化设备坐标系和屏幕坐标系,而局部坐标系(模型坐标系)、世界坐标系和照相机坐标系都是为了方便用户设计而自定义的坐标系,它们的关系如下图所示(来自Chapter 7. World in Motion):

图中左边的过程包括模型变换、视变换,投影变换,这些变换可以由用户根据需要自行指定,这些内容在顶点着色器中完成;而图中右边的两个步骤,包括透视除法、视口变换,这两个步骤是OpenGL自动执行的,在顶点着色器处理后的阶段完成。

各个变换阶段的理解

下面分别对每个阶段的变换做一个总结,以帮助理解。


模型变换——从模型坐标系到世界坐标系

局部坐标系(模型坐标系)是为了方便构造模型而设立的坐标系,建立模型时我们无需关心最终对象显示在屏幕哪个位置。模型的原点定位也可以有所不同,例如下面在模型坐标系定义的模型: 

模型变换的主要目的是通过变换使得用顶点属性定义或者3d建模软件构造的模型,能够按照需要,通过缩小、平移等操作放置到场景中合适的位置。通过模型变换后,物体放置在一个全局的世界坐标系中,世界坐标系是所有物体交互的一个公共坐标系。例如下面的图中在模型坐标系定义的茶壶模型(来自World, View and Projection Transformation Matrices):

茶壶通过模型变换,转换到世界坐标系中(来自World, View and Projection Transformation Matrices):

模型变换包括:旋转、平移、缩放、错切等内容。例如将物体从一个位置p=(x,y,z),移动到另一个位置p′=(x′,y′,z′)的过程,用矩阵表示为:

p′=Tp=⎡⎣⎢⎢⎢⎢100001000010txtytz1⎤⎦⎥⎥⎥⎥⎡⎣⎢⎢⎢xyz1⎤⎦⎥⎥⎥=⎡⎣⎢⎢⎢⎢x+txy+tyz+tz1⎤⎦⎥⎥⎥⎥

应用多个模型变换时,注意变换执行的顺序影响变换的结果,一般按照缩放–》旋转—》平移的顺序执行;另外,注意旋转和缩放变换的不动点问题。这些内容在模型变换一节已经介绍了,这里不再赘述。利用GLM数学库实现模型变换,例如平移变换示例代码为:

glm::mat4 model; // 构造单位矩阵
model = glm::translate(model, glm::vec3(0.0f, 0.0f,-0.5f));
  • 1
  • 2
  • 1
  • 2

视变换——从世界坐标系到相机坐标系

视变换是为了方便观察场景中物体而设立的坐标系,在这个坐标系中相机是个假想的概念,是为了便于计算而引入的。相机坐标系中的坐标,就是从相机的角度来解释世界坐标系中位置。相机和场景的示意图如下所示(来自World, View and Projection Transformation Matrices):

OpenGL中相机始终位于原点,指向-Z轴,而以相反的方式来调整场景中物体,从而达到相同的观察效果。例如要观察-z轴方向的一个立方体的右侧面,可以有两种方式:

  1. 立方体不动,让相机绕着+y轴,旋转+90度,此时相机镜头朝向立方体的右侧面,实现目的。完成这一旋转的矩阵记作Ry(π2)
  2. 相机不动,让立方体绕着+y轴,旋转-90度,此时也能实现同样的目的。注意这时相机没有转动。完成这一旋转的矩阵记作Ry(−π2)

OpenGL中采用方式2的观点来解释视变换。再举一个例子,比如,一个物体中心位于原点,照相机也位于初始位置原点,方向指向-Z轴。为了对物体的+Z面成像,那么必须将照相机从原点移走,如果照相机仍然指向-Z轴,需要将照相机沿着+Z轴方向后退。假若照相机不移动,我们可以通过将物体沿着-Z轴后退d个单位,则变换矩阵为:

T=⎡⎣⎢⎢⎢10000100001000−d1⎤⎦⎥⎥⎥

通过在世界坐标系中指定相机的位置,指向的目标位置,以及viewUp向量来构造一个相机坐标系,通过视变换矩阵将物体坐标由世界坐标系转换到相机坐标系,视变换矩阵的推导已经在视变换一节介绍,感兴趣地可以去参考。利用GLM数学库实现视变换的代码为:

glm::mat4 view = glm::lookAt(eyePos,
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(0.0f, 1.0f, 0.0f));
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

投影变换——从世界坐标系到裁剪坐标系

投影方式决定以何种方式成像,投影方式有很多种,OpenGL中主要使用两种方式,即透视投影(perspective projection)和正交投影( orthographic projection)。

1.正交投影是平行投影的一种特殊情形,正交投影的投影线垂直于观察平面。平行投影的投影线相互平行,投影的结果与原物体的大小相等,因此广泛地应用于工程制图等方面。 
2.透视投影的投影线相交于一点,因此投影的结果与原物体的实际大小并不一致,而是会近大远小。因此透视投影更接近于真实世界的投影方式。

两者的示意图如下: 

在OpenGL中成像时的效果如下所示(图片来自Modern OpenGL): 

上面的图中,红色和黄色球在视见体内,因而呈现在投影平面上,而绿色球在视见体外,没有在投影平面上成像。指定视见体通过(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble nearVal, GLdouble farVal)6个参数来指定。注意在相机坐标系下,相机指向-z轴,nearVal和farVal表示的剪裁平面分别为:近裁剪平面z=−nearVal,以及远裁剪平面z=−farVal。

使用

glOrtho(xleft, xright, ybottom, ytop, znear, zfar);

或者类似API指定正交投影,参数意义形象表示为下图所示(来自World, View and Projection Transformation Matrices): 

使用

void glFrustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble nearVal, GLdouble farVal); 
void gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);

或者类似的API指定透视投影的视见体,其参数含义如下图所示(来自World, View and Projection Transformation Matrices):

关于投影矩阵的推导,可以参考前面的投影矩阵和视口变换矩阵一节。利用GLM数学库实现视透视投影变换的代码示例为:

glm::mat4 projection = glm::perspective(glm::radians(45.0f),
(GLfloat)(WINDOW_WIDTH)/ WINDOW_HEIGHT, 1.0f, 100.0f);
  • 1
  • 2
  • 1
  • 2

经过投影变换后,物体坐标变换到了裁剪坐标系,经过OpenGL自动执行的透视除法后,变换到规范化设备坐标系中。透视除法就是将裁剪坐标系中坐标都除以wc成分的过程。


视口变换——从NDC到屏幕坐标

视变换是将规范化设备坐标(NDC)转换为屏幕坐标的过程,如下图所示:

 
视口变化通过函数: 
glViewport(GLint sx , GLint sy , GLsizei ws , GLsizei hs)
glDepthRangef(GLclampf ns , GLclampf fs );

两个函数来指定。其中(sx,sy)表示窗口的左下角,ns和 fs指定远近剪裁平面到屏幕坐标的映射关系。视口变换矩阵的推导可以参考前面的投影矩阵和视口变换矩阵一节。用上面的glViewport和glDepthRangef函数指定参数后,视口变换由OpenGL自动执行,不需要额外代码。

坐标变换的计算过程

上述过程从坐标计算角度来看,如下图所示: 


坐标变换的程序实现

在程序中,我们需要在两个部分做处理。 
第一,编写顶点着色器程序如下:

#version 330

layout(location = 0) in vec3 position;
layout(location = 1) in vec3 color;
layout(location = 2) in vec2 textCoord; out vec3 VertColor;
out vec2 TextCoord; uniform mat4 projection;
uniform mat4 view;
uniform mat4 model; void main()
{
gl_Position = projection * view * model * vec4(position, 1.0);
VertColor = color;
TextCoord = textCoord;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

第二,在主程序中传递变换矩阵到顶点着色器,并绘制场景中物体,代码如下:

// 投影矩阵
glm::mat4 projection = glm::perspective(glm::radians(45.0f),
(GLfloat)(WINDOW_WIDTH)/ WINDOW_HEIGHT, 1.0f, 100.0f);
// 视变换矩阵
glm::mat4 view = glm::lookAt(eyePos,
glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
// 模型变换矩阵
glm::mat4 model = glm::mat4();
model = glm::translate(model, glm::vec3(-0.25f, 0.0f, 0.0f));
// 使用uniform变量传递MVP矩阵
glUniformMatrix4fv(
glGetUniformLocation(shader.programId, "projection"),
1, GL_FALSE, glm::value_ptr(projection)); // 传递投影矩阵
glUniformMatrix4fv(
glGetUniformLocation(shader.programId, "view"),
1, GL_FALSE, glm::value_ptr(view));// 传递视变换矩阵
glUniformMatrix4fv(
glGetUniformLocation(shader.programId, "model"),
1, GL_FALSE, glm::value_ptr(model)); // 传递模型变换矩阵
// 绘制物体
glDrawArrays(GL_TRIANGLES, 0, 36);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

例如利用圆形坐标系指定相机位置,绘制的立方体如下图所示:

参考资料

1.World, View and Projection Transformation Matrices 
2.GLSL Programming/Vertex Transformations

相关资源

1.OpenGL 101: Matrices - projection, view, model
2.songho OpenGL Transformation 
3.The Perspective and Orthographic Projection Matrix 
4.songho OpenGL Projection Matrix 
5. glOrtho
6. glFrustum 
7. gluPerspective 
8. gluLookAt

OpenGl从零开始之坐标变换的更多相关文章

  1. OpenGl从零开始之坐标变换(上)

    坐标变换是深入理解三维世界的基础,非常重要.学习这部分首先要清楚几个概念:视点变换.模型变换.投影变换.视口变换. 在现实世界中,所有的物体都具有三维特征,但计算机本身只能处理数字,显示二维的图形,因 ...

  2. OpenGl从零开始之坐标变换(下)

    这节主要来理解投影变换和视口变换的使用. 1.正射投影:glOrtho 函数原型: void glOrtho(GLdouble left,GLdouble right,GLdouble bottom, ...

  3. [OpenGL]OpenGL坐标系和坐标变换

    OpenGL通过摄像机的模拟.要实现一个三维计算机图形重大转变,这是几何变换(模型转换-查看转型(两者统称为几何变换)).投影.作物转型.口变换等.同一时候,OpenGL还实现了矩阵堆栈等.理解掌握了 ...

  4. openGl从零开始之添加颜色

    OpenGL 支持两种颜色模式:一种是 RGBA模式,一种是 颜色索引模式.无论哪种颜色模式,计算机都必须为每一个像素保存一些数据,即通过每一个像素的颜色,来改变整体图形的颜色.不同的是, RGBA ...

  5. OpenGL中各种坐标系的理解[转]

    OPENGL坐标系可分为:世界坐标系和当前绘图坐标系. 世界坐标系:在OpenGL中,世界坐标系是以屏幕中心为原点(0, 0, 0),且是始终不变的.你面对 屏幕,你的右边是x正轴,上面是y正轴,屏幕 ...

  6. openGL坐标系

    从我们构造模型的局部坐标系(Local/Object Space)经过一系列的处理最终渲染到屏幕坐标系(Screen Space)下,这个过程有6种坐标系. 一.世界坐标系(World Coordin ...

  7. OpenCASCADE Coordinate Transforms

    OpenCASCADE Coordinate Transforms eryar@163.com Abstract. The purpose of the OpenGL graphics process ...

  8. OpenCASCADE Camera

    OpenCASCADE Camera eryar@163.com Abstract. OpenCASCADE introduce a new class Graphic3d_Camera for th ...

  9. CSharpGL(27)讲讲清楚OpenGL坐标变换

    CSharpGL(27)讲讲清楚OpenGL坐标变换 在理解OpenGL的坐标变换问题的路上,有好几个难点和易错点.且OpenGL秉持着程序难以调试.难点互相纠缠的特色,更让人迷惑.本文依序整理出关于 ...

随机推荐

  1. AIM Tech Round (Div. 2) D. Array GCD dp

    D. Array GCD 题目连接: http://codeforces.com/contest/624/problem/D Description You are given array ai of ...

  2. cdoj 80 Cube 水题

    Cube Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/problem/show/80 Descrip ...

  3. STL源码学习----lower_bound和upper_bound算法[转]

    STL中的每个算法都非常精妙,接下来的几天我想集中学习一下STL中的算法. ForwardIter lower_bound(ForwardIter first, ForwardIter last,co ...

  4. java list三种遍历方法性能比較

    从c/c++语言转向java开发,学习java语言list遍历的三种方法,顺便測试各种遍历方法的性能,測试方法为在ArrayList中插入1千万条记录,然后遍历ArrayList,发现了一个奇怪的现象 ...

  5. ProgressCircular

    https://github.com/eltld/ProgressCircular

  6. Winform模拟post请求和get请求登录网站

    引言 最近有朋友问如何用winform模拟post请求,然后登录网站,稍微想了一下,大致就是对http报文的相关信息的封装,然后请求网站登录地址的样子.发现自己的博客中对这部分只是也没总结,就借着这股 ...

  7. cocos2d-x 2.2 移植wp8遇到的坑

    这两天正在将之前的一款cocos2d游戏,移植到wp平台上,这里记录一下所遇到的问题以及解决方法. 我是用的cocos2d下面的例子程序进行修改的. 遇到的第一个问题是资源路径的问题,当时我把解决方案 ...

  8. Java最重要的21个技术点和知识点之JAVA基础

     (一)Java最重要的21个技术点和知识点之JAVA基础  写这篇文章的目的是想总结一下自己这么多年JAVA培训的一些心得体会,主要是和一些java基础知识点相关的,所以也希望能分享给刚刚入门的Ja ...

  9. GridView格式化

    <asp:TemplateColumn HeaderText="进出境运输方式">    <ItemTemplate> <%# Eval(" ...

  10. SQL中Len与DataLength区别

    SQL中求字符串长度问题 一.LEN(Param) 求字符串的长度 DataLength(param) 求字符串所占的字节长度 二.LEN不返回文本之后的空格长度 而DataLenth则不同 三.针对 ...