(转载)Cocos2dx-OpenGL ES2.0教程:初识MVP(3)

摘要:
每个precs2d-x预定义的着色器都包含此统一,它是着色器中的一个变量,voidmain(){gl_FragColor=v_fragmentColor*u_color;我们需要在程序中向此u_color传递一个值,

上一篇文章中,我在介绍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数据:

1
2
3
4
5
6
varying vec4 v_fragmentColor;
uniform vec4 u_color;
void main()
{
    gl_FragColor = v_fragmentColor * u_color;
}

此时,我们需要在程序里面给这个u_color传值。它的基本做法与attribute的传值是一样的。

首先,我们需要获得这个uniform在shader里面的位置。

1
GLuint uColorLocation = glGetUniformLocation(glProgram->getProgram(), "u_color");

然后,我们可以通过glUniformXXX函数给这个uniform赋值:

1
2
float uColor[] = {1.0, 0.0, 0.0, 1.0};
glUniform4fv(uColorLocation,1, uColor);

此时,我们就在c++代码和shader程序之间传递数据啦。编译并运行,我们会得到一个半红不红的三角形:

triangletriangle

初识CC_MVPMatrix

CC_MVPMatrix是一个mat4类型的uniform,在shader代码被编译之前,它由cocos2d-x框架插入进来的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
bool GLProgram::compileShader(GLuint * shader, GLenum type, const GLchar* source)
{
    //部分代码省略
    const GLchar *sources[] = {
        "uniform mat4 CC_PMatrix;
"
        "uniform mat4 CC_MVMatrix;
"
        "uniform mat4 CC_MVPMatrix;
"
        "uniform vec4 CC_Time;
"
        "uniform vec4 CC_SinTime;
"
        "uniform vec4 CC_CosTime;
"
        "uniform vec4 CC_Random01;
"
        "uniform sampler2D CC_Texture0;
"
        "uniform sampler2D CC_Texture1;
"
        "uniform sampler2D CC_Texture2;
"
        "uniform sampler2D CC_Texture3;
"
        "//CC INCLUDES END

",
        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变换放到一起,一般做法如下:

1
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函数的最开头加入下列代码:

1
2
3
4
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函数返回前加入下列代码:

1
2
Director::getInstance()->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
  Director::getInstance()->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

这里,我们通过调用pushMatrix把当前矩阵压栈,这个操作会把原来栈顶上的元素拷贝一份并压入栈,这样我们后续对于此矩阵的操作可以通过调用popMatrix来撤销影响。此处,我们把ModelView和Projection矩阵都重置成了单位矩阵。而我们通过调用下列代码可以更新CC_MVPMatrix:

1
glProgram->setUniformsForBuiltins();

此时,如果我们运行程序,会得到一个黑屏(什么也显示不了)。

设备空间(normalized device space)

为了解决上述问题,我们只需要把对象的顶点数据修改为:

1
2
3
float vertercies[] = { -1,-1,   //第一个点的坐标
        1, -1,   //第二个点的坐标
        0, 1};  //第三个点的坐标

为什么要这样呢?因为任何一个顶点乘以一个单位矩阵,它的值是不变的。而normalized device space空间的取值范围是-1~+1,如下图所示:

clippingspaceclippingspace

所以,如果我们要想显示同之前一模一样的三角形,就必须修改这个顶点数据,让它的取值范围落在Clipping Space以内。这也是我们在其它许多书本上面看到的规范的三角形的范例。

免责声明:文章转载自《(转载)Cocos2dx-OpenGL ES2.0教程:初识MVP(3)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇ES6中的类和对象golang orm 框架之 gorm下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

leaks工具查找内存泄露

作为一名iOS开发攻城狮,在苹果没有出ARC(自动内存管理机制)时,我们几乎有一半的开发时间都耗费在这么管理内存上.后来苹果很人性的出了ARC,虽然在很大程度上,帮助我们开发者节省了精力和时间.但是我们在开发过程中,由于种种原因,还是会出现内存泄露的问题.内存泄露是一个很严重的问题.下面就简单介绍下怎么使用Xcode7自带的Instruments中的Lea...

windows下的shellcode剖析浅谈[转自看雪]

标 题: 【原创】windows下的shellcode剖析浅谈作 者:snowdbg时 间: 2009-10-06,11:12链 接: http://bbs.pediy.com/showthread.php?t=99007  今天是中秋节,正好我的文章在今天基本完成,作为中秋礼物送给大家,由于本人水平有限希望大家多多批评指正!学习了好些日子了,思路总是乱...

网页布局

盒子模型     盒子模型应用块状标签,比如<div><p><ol><ul><h><table>等都可以应用盒子模型 盒子模型,比如<div>逻辑快,就如一个盒子一样,盒子里分为边框,内填充,外边距。外边框到内容标签的距离称为padding:padding-top,padd...

C++静态库中使用_declspec(dllexport) 不能导出函数的问题

在某项目中,有一些静态库,这些静态库中有类型命名的函数GET_XXX。在一次项目结构调整的时候,我想将调用这静态库的代码编译成DLL,并且将这些Get函数导出,我就直接就这些函数前面添加了_declspec(dllexport), 然后代码结构就成为: 静态库A: 包括很多 _declspec(dllexport) GET_XXX 函数。 动态库B:未直接...

数据库的触发器

一、触发器是一种特殊的存储过程,不能被显式调用,只能在对表进行insert、update、delete操作时被自动激活。所以触发器可以用来实现对表进行复杂的完整性约束。 二、 Sql Server为每个触发器都创建了两个专用表:Inserted表和Deleted表。这两个表由系统来维护,它们存在于内存中而不是数据库中。这两个表的结构总是与被该触发器作用的...

kestrel Server的源码分析

今天这一篇博客讲的是.net core 自带的kestrel server,当你开发微服务k8s部署在linux环境下,一般默认开启这个高性能服务,如果大家之前看过我的owin katana的博客,会发现.net core 的好多实现在之前.net standard 的版本已经实现过了,当时开发的asp.net 程序与IIS紧紧耦合在一起,后来的微软团队意...