OpenGL(6)——坐标系
在掌握基本变换后,学习如何变换coordinate space。
对coordinate space进行变换的目的是将local space中各顶点坐标转换成normalized device coordinate (NDC),然后传递到Fragment Shader中。对于OpenGL,为了能够最终显示在屏幕上,被转换成NDC的顶点坐标的x、y和z坐标范围应当在[-1, 1],并遵循左手坐标系原则,即坐标范围超出[-1, 1]顶点,均是不可见的。coordinate space变换的步骤如下图所示:
记住五个坐标系和三个矩阵!在编写程序时,也是对这三个矩阵进行操作。分别解释它们的作用:
1. model matrix
model matrix将local coordinate变换为world coordinate。想象一个等边三角形,在最开始它处于local space,坐标系原点就是三角形的中心点。当渲染多个等边三角形时,对于每个处于local space的三角形来说,坐标系原点均是自身的中心点。在对三角形进行scale、rotate和translate后,三角形被分散在了各处,并处在world space中,此时需要一个新的坐标系来表示各三角形的顶点坐标,即world coordinate。也很容易看出,在这个例子中,model matrix就是对三角形进行scale、rotate和translate的变换矩阵。
2. world matrix
world matrix将world coordinate变换为view coordinate。假设此时在world space的任一位置有一个摄像机,如果需要模拟摄像机的观察视角,则应当以摄像机的位置作为坐标系原点重新建立坐标系,而world space中其他物体和摄像机的相对位置应当保持不变。这一变换由world matrix完成,新建立的坐标系就是view coordinate。
3. projection matrix
projection matrix将view coordinate变换为clip coordinate,即NDC,所有可见顶点的坐标范围均在[-1, 1],超出这个范围的顶点都会被裁剪掉。其中顶点的z坐标表示depth value,depth value越大,则距离摄像机正面的距离越远,在渲染时由该顶点插值得到的fragment就越容易被遮挡。在程序中,需要手动使能depth test。projection matrix分为perspective projection matrix和orthographic projection matrix。
最后一步viewport transformation由OpenGL自动完成,所有可见顶点的坐标会根据窗口宽高按比例缩放,并传递到Rasterizer中。
以章节后练习第三题为例,解释代码:
渲染10个立方体,使用model matrix只让是3倍数的立方体旋转(包括第1个立方体),而让剩下的立方体保持静止。
1.
输入渲染一个立方体需要的顶点数据,包括顶点坐标,颜色和纹理坐标。一个立方体有6个面,每个面2个三角形,6个顶点数据,因此总共需要36个顶点数据才能完成一个立方体的渲染。
float vertices3[] = {
-0.5f, -0.5f, -0.5f, 0.84f, 0.38f, 0.157f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 0.84f, 0.38f, 0.157f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 0.84f, 0.38f, 0.157f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.84f, 0.38f, 0.157f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.84f, 0.38f, 0.157f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.84f, 0.38f, 0.157f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.84f, 0.38f, 0.157f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.84f, 0.38f, 0.157f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.84f, 0.38f, 0.157f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.84f, 0.38f, 0.157f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.84f, 0.38f, 0.157f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.84f, 0.38f, 0.157f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.84f, 0.38f, 0.157f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.84f, 0.38f, 0.157f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.84f, 0.38f, 0.157f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.84f, 0.38f, 0.157f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.84f, 0.38f, 0.157f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.84f, 0.38f, 0.157f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.84f, 0.38f, 0.157f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 0.84f, 0.38f, 0.157f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.84f, 0.38f, 0.157f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.84f, 0.38f, 0.157f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.84f, 0.38f, 0.157f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.84f, 0.38f, 0.157f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.84f, 0.38f, 0.157f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.84f, 0.38f, 0.157f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.84f, 0.38f, 0.157f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.84f, 0.38f, 0.157f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.84f, 0.38f, 0.157f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.84f, 0.38f, 0.157f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.84f, 0.38f, 0.157f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.84f, 0.38f, 0.157f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.84f, 0.38f, 0.157f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.84f, 0.38f, 0.157f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.84f, 0.38f, 0.157f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.84f, 0.38f, 0.157f, 0.0f, 1.0f
};
2.
定义数组存放10个向量,model matrix使用这些向量将立方体translate到world space中的不同位置。
glm::vec3 cubePositions[] = {
glm::vec3( 0.0f, 0.0f, 0.0f),
glm::vec3( 2.0f, 5.0f, -15.0f),
glm::vec3(-1.5f, -2.2f, -2.5f),
glm::vec3(-3.8f, -2.0f, -12.3f),
glm::vec3( 2.4f, -0.4f, -3.5f),
glm::vec3(-1.7f, 3.0f, -7.5f),
glm::vec3( 1.3f, -2.0f, -2.5f),
glm::vec3( 1.5f, 2.0f, -2.5f),
glm::vec3( 1.5f, 0.2f, -1.5f),
glm::vec3(-1.3f, 1.0f, -1.5f)
};
3.
定义glm::mat4 coordTransform(std::size_t modelIndex)
函数返回变换矩阵,参数std::size_t modelIndex表示model matrix的索引,需要使第1、4、7和10个立方体随时间转动,而第2、3、5、6、8和9个则保持静止。model matrix很容易写出来:
glm::mat4 modelMatrix;
if(modelIndex % 3 == 0)
modelMatrix = glm::rotate(glm::translate(glm::mat4(1.0f), cubePositions[modelIndex]), glm::radians(20.0f * modelIndex) + (float)glfwGetTime(), glm::vec3(1.0f, 0.0f, 0.3f));
else
modelMatrix = glm::rotate(glm::translate(glm::mat4(1.0f), cubePositions[modelIndex]), glm::radians(20.0f * modelIndex), glm::vec3(1.0f, 0.0f, 0.3f));
经过model matrix变换,所有顶点都在world space中,假设此时摄像机的坐标为(0, 0, 4),为了以摄像机为坐标原点重新建立坐标系,需要将所有顶点沿z轴负方向translate4个单位。写出view matrix:
glm::mat4 viewMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -4.0f));
之后调用perspective(FoV, aspect, near, far)
函数创建projection matrix,四个参数分别表示视野张开的角度,窗口宽高比,近平面位置和远平面位置。projection matrix的推导和计算可以参考OpenGL Projection Matrix的内容。这里直接调用函数即可:
glm::mat4 projectionMatrix = glm::perspective(glm::radians(45.0f), (float)window_width/(float)window_height, 0.1f, 1000.0f);
最后做两次矩阵的乘法得到最终的变换矩阵并返回:
glm::mat4 transMatrix = projectionMatrix * viewMatrix * modelMatrix;
4.
调用glEnable(GL_DEPTH_TEST)
使能depth test,之后开始渲染:
while(!glfwWindowShouldClose(window)){
processInput(window);
glClearColor(0.03f, 0.4f, 0.57f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture[1]);
ourShader.use();
glUniform1f(glGetUniformLocation(ourShader.ID, "ratio"), fragmentRatio);
for(size_t modelIndex = 0; modelIndex < 10; ++modelIndex){
glUniformMatrix4fv(glGetUniformLocation(ourShader.ID, "transform"), 1, GL_FALSE, glm::value_ptr(coordTransform(modelIndex)));
glDrawArrays(GL_TRIANGLES, 0, 36);
}
glBindVertexArray(VAO);
glfwSwapBuffers(window);
glfwPollEvents();
}
OpenGL(6)——坐标系的更多相关文章
- OpenGL中坐标系的理解(一)
在OpenGL中,存在着至少存在着三种矩阵,对应着函数glMatrixMode()的三个参数:GL_MODELVIEW,GL_PROJECTION,GL_TEXTURE. 以下主要描述GL_MODEL ...
- cocos2d-x OpenGL ES 坐标系总结
很多教程都说cocos2d-x OpenGL ES世界坐标系原点在左下角,但至于为什么在左下角却从来没有人提过,这导致大部分人觉得这是OpenGL ES的规定,事实上这是错的.OpenGL ES的坐标 ...
- opengl视图变换 投影变换推导
视图变换在opengl中,视图变换的输入是:(1)眼睛位置(或者说相机位置)eys:(2)眼睛朝向的中心center,(就是眼睛朝哪里看);(3)头的方向up.任何一点经过视图变换后都会转化到眼睛坐标 ...
- OpenGL的gluPerspective和gluLookAt的关系[转]
函数原型void gluLookAt(GLdoble eyex, GLdouble eyey, GLdouble eyez, GLdouble centerx, GLdouble centery, ...
- OPenGL中三维图形的矩阵变换
对于二维的图形开发,拿简单的图片显示来说,我们主要的目的:就是在一块显示buffer中,不停的把每个像素进行着色,然后就可以绘制出来了.为了速度,很多其他的加速方法,但原理基本上就是这样了. 很直观, ...
- OpenGL中的拾取模式( Picking)
1. Opengl中的渲染模式有三种:(1)渲染模式,默认的模式:(2)选择模式, (3)反馈模式.如下 GLint glRenderMode(GLenum mode) mode可以选取以下三种模式之 ...
- 基于Cocos2d-x学习OpenGL ES 2.0系列——编写自己的shader(2)
在上篇文章中,我给大家介绍了如何在Cocos2d-x里面绘制一个三角形,当时我们使用的是Cocos2d-x引擎自带的shader和一些辅助函数.在本文中,我将演示一下如何编写自己的shader,同时, ...
- (转)OpenGL混合的基本知识
今天介绍关于OpenGL混合的基本知识.混合是一种常用的技巧,通常可以用来实现半透明.但其实它也是十分灵活的,你可以通过不同的设置得到不同的混合结果,产生一些有趣或者奇怪的图象. 混合是什么呢?混合就 ...
- cocos2d-x坐标系详解
cocos2d-x官方文档 笛卡尔坐标系 不同坐标系简介 笛卡尔坐标系 你可能上学的时候就已经知道“笛卡尔坐标系”了,它在几何课本里经常用到.如果你已经忘得差不多了,下面这些图片可以很快唤起你的记忆: ...
- 04: OpenGL ES 基础教程03 纹理
前言 1:常用类: 1:纹理的作用 正文 一:常用类 上下文 顶点数据缓存 着色器 baseEffect 一:纹理 1.1: 纹理可以控制渲染的每个像素的颜色. 1.2: 纹素:与像素一样,保存每 ...
随机推荐
- 关于反编译pyc的一点技巧
现在最流行的是用 https://github.com/rocky/python-uncompyle6 但是有些python小版本不一样,比如2.7.6的某版本,开头的magic number在这个项 ...
- 题解 [ZJOI2010]基站选址
题解 [ZJOI2010]基站选址 题面 解析 首先考虑一个暴力的DP, 设\(f[i][k]\)表示第\(k\)个基站设在第\(i\)个村庄,且不考虑后面的村庄的最小费用. 那么有\(f[i][k] ...
- 添加QQ群
点击右边添加 <a target=" src="//pub.idqqimg.com/wpa/images/group.png" alt="陆小果哥 ...
- 客户端xml
package lct.conference.common; import java.io.IOException;import java.io.PrintWriter;import java.net ...
- FileInputStream读取的两种方法:逐字节读;以字节数组读取
1:read() : 从输入流中读取数据的下一个字节,返回0到255范围内的int字节值.如果因为已经到达流末尾而没有可用的字节,则返回-1.在输入数据可用.检测到流末尾或者抛出异常前,此方法一直阻塞 ...
- WORD粘贴图片+DEDE
自动导入Word图片,或者粘贴Word内容时自动上传所有的图片,并且最终保留Word样式,这应该是Web编辑器里面最基本的一个需求功能了.一般情况下我们将Word内容粘贴到Web编辑器(富文本编辑器) ...
- 运算符重载之new与delete
关于new/delete,援引C++ Primer中的一段话: 某些应用程序对内存分配有特殊的要求,因此我们无法直接将标准的内存管理机制直接应用于这些程序.他们常常需要自定义内存分配的细节,比如使用关 ...
- linux系统编程--线程同步
同步概念 所谓同步,即同时起步,协调一致.不同的对象,对“同步”的理解方式略有不同. 如,设备同步,是指在两个设备之间规定一个共同的时间参考: 数据库同步,是指让两个或多个数据库内容保持一致,或者按需 ...
- Singleton模式(单例模式) 饿汉式和懒汉式
目的:整个应用中有且只有一个实例,所有指向该类型实例的引用都指向这个实例. 好比一个国家就只有一个皇帝(XXX),此时每个人叫的“皇帝”都是指叫的XXX本人; 常见单例模式类型: 饿汉式单例:直接将对 ...
- zabbix监控项整理Items-key
agent.hostname:hostname,字符串 agent.ping:可用性检查,可用返回1:不可用返回空 agent.version:agent程序的版本,返回字符串 kernel.maxf ...