基于Cocos2d-x学习OpenGL ES 2.0系列——初识MVP(3)
在上一篇文章中,我在介绍vertex shader的时候挖了一个坑:CC_MVPMatrix。它其实是一个uniform,每一个Cocos2d-x预定义的shader都包含有这个uniform,但是如果你在shader里面不使用这个变量的话,OpenGL底层会把它优化掉。
但是,CC_MVPMatrix是在什么时候设置进来的呢?我在shader里面明明没有看到它,它从哪儿来的?别急,请继续往下读。
初识Uniform
在回答上面几个问题之前,让我们先来介绍一下什么是uniform。简单来说,uniform是shader里面的一种变量,它是由外部程序设置进来的,它不像vertex的attribute,每个顶点都有一份数据。除非你显式地调用glUniformXXX函数来修改这个uniform的值,否则它的值恒定不变。接下来,让我们修改myFragmentShader.frag,给它添加一个新的uniform数据:
varying vec4 v_fragmentColor; uniform vec4 u_color; void main()
{
gl_FragColor = v_fragmentColor * u_color;
}
此时,我们需要在程序里面给这个u_color传值。它的基本做法与attribute的传值是一样的。
首先,我们需要获得这个uniform在shader里面的位置。
GLuint uColorLocation = glGetUniformLocation(glProgram->getProgram(), "u_color");
然后,我们可以通过glUniformXXX函数给这个uniform赋值:
float uColor[] = {1.0, 0.0, 0.0, 1.0};
glUniform4fv(uColorLocation,, uColor);
此时,我们就在C++代码和shader程序之间传递数据啦。编译并运行,我们会得到一个半红不红的三角形:
初识CC_MVPMatrix
CC_MVPMatrix是一个mat4类型的uniform,在shader代码被编译之前,它由Cocos2d-x框架插入进来的。
bool GLProgram::compileShader(GLuint * shader, GLenum type, const GLchar* source)
{
//部分代码省略
const GLchar *sources[] = {
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32 && CC_TARGET_PLATFORM != CC_PLATFORM_LINUX && CC_TARGET_PLATFORM != CC_PLATFORM_MAC)
(type == GL_VERTEX_SHADER ? "precision highp float;\n" : "precision mediump float;\n"),
#endif
"uniform mat4 CC_PMatrix;\n"
"uniform mat4 CC_MVMatrix;\n"
"uniform mat4 CC_MVPMatrix;\n"
"uniform vec4 CC_Time;\n"
"uniform vec4 CC_SinTime;\n"
"uniform vec4 CC_CosTime;\n"
"uniform vec4 CC_Random01;\n"
"uniform sampler2D CC_Texture0;\n"
"uniform sampler2D CC_Texture1;\n"
"uniform sampler2D CC_Texture2;\n"
"uniform sampler2D CC_Texture3;\n"
"//CC INCLUDES END\n\n",
source,
};
*shader = glCreateShader(type);
glShaderSource( *shader, sizeof(sources)/sizeof( *sources), sources, nullptr);
glCompileShader( *shader);
//下面的代码省略了...
}
从上面的代码,我们可以看到, 这里除了插入CC_MVPMatrix以外,还插入了其它一些uniform。只要你在后面的main函数里面不使用这些变量,最终shader program里面是看不到它们的。(被优化掉了)
CC_MVPMatrix的作用
CC_MVPMatrix本质上是一个变换矩阵,用来把一个世界坐标系中的点转换到Clipping space。当然,如果学过OpenGL的人都知道,3D物体从建模到最终显示到屏幕上面要经历以下几个阶段:
对象空间(Object Space)
世界空间(World Space)
照相机空间(Camera Space/Eye Space)
裁剪空间(Clipping Space)
设备空间(normalized device space)
视口空间(Viewport)
从对象空间到世界空间的变换通常叫做Model-To-World变换,从世界空间到照相机空间的变换叫做World-To-View变换,而从照相机空间到裁剪空间的变换叫做View-To-Projection。合起来,就是我们常常提到的MVP变换。这里面每一个变换都是乘以一个矩阵,3个矩阵相乘最后还是一个矩阵,也就是Cocos2d-x里面的CC_MVPMatrix啦。当然,实际开发过程中,我们往往会把MV变换放到一起,一般做法如下:
gl_Position = P * MV * ObjectPosition;
具体这些变换是怎么计算的,另外每一个计算的几何意义是什么。本系列教程暂不讨论,感兴趣的读者可以去看看我在本系列教程第一篇的最后推荐的一些资源。
修改CC_MVPMatrix
我们怎么样修改CC_MVPMatrix呢?前面介绍过uniform变量的修改方法在这里是适用的,我们可以先通过glGetUniformLocation来获取这个uniform的入口,然后调用glUniformMatrix4fv来给它传值就行了。
但是,等等。我该怎么计算这个矩阵的值呢?有两个函数glLookAt和glPerspective可以做这些事,具体的用法 ,大家可以参考CCDirector.cpp里面的代码。我就不在此处展开讨论了,另外强烈推荐大家运行此网页中的一个演示程序,用来加深于这两个函数的理解。
在Cocos2d-x里面,我们可以通过修改矩阵栈里面的ModelView和Projection栈顶元素,从而修改ModelView和Projection矩阵,最终达到修改CC_MVPMatrix的目的。
首先,让我们在onDraw函数的最开头加入下列代码:
// 将当前的模型视图矩阵入栈(在栈中保存一份当前模型视图矩阵的拷贝)
Director::getInstance()->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
// 将当前的模型视图矩阵重置为单位矩阵
Director::getInstance()->loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
Director::getInstance()->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
Director::getInstance()->loadIdentityMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
然后在onDraw函数返回前加入下列代码:
// 恢复投影矩阵和模型视图矩阵
Director::getInstance()->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
Director::getInstance()->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
这里,我们通过调用pushMatrix把当前矩阵压栈,这个操作会把原来栈顶上的元素拷贝一份并压入栈,这样我们后续对于此矩阵的操作可以通过调用popMatrix来撤销影响。此处,我们把ModelView和Projection矩阵都重置成了单位矩阵。而我们通过调用下列代码可以更新CC_MVPMatrix:
glProgram->setUniformsForBuiltins();
此时,如果我们运行程序,会得到一个黑屏(什么也显示不了)。
设备空间(normalized device space)
为了解决上述问题,我们只需要把对象的顶点数据修改为:
float vertercies[] = { -,-, //第一个点的坐标
, -, //第二个点的坐标
, }; //第三个点的坐标
为什么要这样呢?因为任何一个顶点乘以一个单位矩阵,它的值是不变的。而normalized device space空间的取值范围是-1~+1,而原来,顶点不是乘以单位矩阵,而是乘以了MVP变换矩阵,顶点坐标被正确转换为设备坐标显示出来了。如下图所示:
所以,如果我们要想显示同之前一模一样的三角形,就必须修改这个顶点数据,让它的取值范围落在Clipping Space以内。这也是我们在其它许多书本上面看到的规范的三角形的范例。
结语
最后,按照惯例,附上本教程的源码下载地址
推荐阅读
https://www.youtube.com/watch?v=-tonZsbHty8&index=26&list=PLRwVmtr-pp06qT6ckboaOhnm9FxmzHpbY
http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/
http://blog.db-in.com/cameras-on-opengl-es-2-x/ (强烈推荐)
基于Cocos2d-x学习OpenGL ES 2.0系列——初识MVP(3)的更多相关文章
- 基于Cocos2d-x学习OpenGL ES 2.0系列——纹理贴图(6)
在上一篇文章中,我们介绍了如何绘制一个立方体,里面涉及的知识点有VBO(Vertex Buffer Object).IBO(Index Buffer Object)和MVP(Modile-View-P ...
- 基于Cocos2d-x学习OpenGL ES 2.0系列——使用VBO索引(4)
在上一篇文章中,我们介绍了uniform和模型-视图-投影变换,相信大家对于OpenGL ES 2.0应该有一点感觉了.在这篇文章中,我们不再画三角形了,改为画四边形.下篇教程,我们就可以画立方体了, ...
- 基于Cocos2d-x学习OpenGL ES 2.0系列——你的第一个三角形(1)
前言 在本系列教程中,我会以当下最流行的2D引擎Cocos2d-x为基础,介绍OpenGL ES 2.0的一些基本用法.本系列教程的宗旨是OpenGL扫盲,让大家在使用Cocos2d-x过程中,知其然 ...
- 基于Cocos2d-x学习OpenGL ES 2.0系列——OpenGL ES渲染之Shader准备(7)
Cocos2d-x底层图形绘制是使用OpenGL ES协议的.OpenGL ES是什么呢? OpenGL ES(OpenGl for Embedded System)是OpenGL三维图形API的子集 ...
- 基于Cocos2d-x学习OpenGL ES 2.0系列——编写自己的shader(2)
在上篇文章中,我给大家介绍了如何在Cocos2d-x里面绘制一个三角形,当时我们使用的是Cocos2d-x引擎自带的shader和一些辅助函数.在本文中,我将演示一下如何编写自己的shader,同时, ...
- 基于Cocos2d-x学习OpenGL ES 2.0系列——OpenGL ES渲染之LayerColor(8)
在前面文章中讲述了Cocos2d-x引擎OpenGL渲染准备Shader方面,本文主要讲解使用LayerColor来讲述OpenGL的渲染过程. 1.LayerColor对象创建 添加LayerCol ...
- 基于Cocos2d-x学习OpenGL ES 2.0系列——你的第一个立方体(5)
在上篇文章中,我们介绍了VBO索引的使用,使用VBO索引可以有效地减少顶点个数,优化内存,提高程序效率. 本教程将带领大家一起走进3D--绘制一个立方体.其实画立方体本质上和画三角形没什么区别,所有的 ...
- 基于Cocos2d-x学习OpenGL ES 2.0之多纹理
没想到原文出了那么多错别字,实在对不起观众了.介绍opengl es 2.0的不多.相信介绍基于Cocos2d-x学习OpenGL ES 2.0之多纹理的,我是独此一家吧.~~ 子龙山人出了一个系列: ...
- OpenGL ES 2.0 渲染管线 学习笔记
图中展示整个OpenGL ES 2.0可编程管线 图中Vertex Shader和Fragment Shader 是可编程管线: Vertex Array/Buffer objects 顶点数据来源, ...
随机推荐
- org.eclipse.jdt.internal.compiler包下的类找不到
到maven库上下载jar包:org.eclipse.jdt.core-3.13.jar <!-- https://mvnrepository.com/artifact/org.eclipse. ...
- VC6.0启动File-open和Project-add file to project崩溃的解决方法
最近由于装了Office2010,VC6.0被整残了,file->open 和 Project->add file to project不能用,一用VC6.0就崩溃,查到是由于office ...
- netty5入门教程
1.Netty是什么? 本质:JBoss做的一个Jar包 目的:快速开发高性能.高可靠性的网络服务器和客户端程序 优点:提供异步的.事件驱动的网络应用程序框架和工具 通俗的说:一个好使的处理Socke ...
- Kafka_Zookeeper_环境搭建使用
Kafka 安装和测试 1. 简介 kafka (官网地址:http://kafka.apache.org)是一款分布式消息发布和订阅的系统,具有高性能和高吞吐率. i. 消息的发布(publish) ...
- Install Homebrew
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/instal ...
- LINQ操作符一:Select
一.什么是LINQ?它可以用来做什么 语言集成查询(Language Integrated Query,LINQ)是一系列标准查询操作符的集合,这些操作符几乎对每一种数据源的导航.过滤和执行操作都提供 ...
- HTTP API 设计指南(响应部分)
前言 这篇指南介绍描述了 HTTP+JSON API 的一种设计模式,最初摘录整理自 Heroku 平台的 API 设计指引 Heroku 平台 API 指引. 这篇指南除了详细介绍现有的 API 外 ...
- html块状元素、内联元素
html块状元素.内联元素 原文在这 块级元素的分类 块级元素按照其应用于结构还是内容分为三种:结构化块状元素,终端块状元素,多目标块状元素. 一.结构化块状元素 这类元素用于构造文档的结构,一个好的 ...
- JavaScript裸体识别技术
当第一次听说nude.js的时候,我非常怀疑这种浏览器端的裸体识别技术,有几个理由: 正常情况下,裸体识别应该在服务器端进行,这样,那些色情图片或色情视频才能在发送给浏览者前被发现. 我不相信完全依赖 ...
- 不可错过的10个超棒jQuery表单操作代码片段
jQuery 绝对是一个伟大的开源javascript类库,是帮助我们快速和高效开发前端应用的利器.可能大家在日常的开发过程中常常会处理表单相关的 javascript,在今天这篇代码片段分享文章中, ...