泰然

 找回密码
 注册泰然

[iTyran原创]Xcode创建的默认iOS OpenGL ES 2.0 project代码分析

2011-12-2 10:50| 发布者: u0u0| 查看: 15022| 评论: 23|原作者: u0u0

摘要: Xcode创建的默认iOS OpenGL ES 2.0 project代码分析,着色器,shader
Xcode创建的默认iOS OpenGL ES 2.0 project代码分析
作者:
u0u0 - iTyran
版权声明:iTyran原创作品,谢绝转载!否则将追究法律责任。
前面iTyran翻译、转载的OpenGL教程都是使用文章作者自己的project,并没有提及默认的工程。默认工程的代码比较多,不利于刚开始初学入门。进过几天的研究,摸清楚了大部分的逻辑,在这与大家共同探讨学习。

工程居于xcode4.2 for iOS5创建,做了部分修改。
下载地址:http://ityran.com/thread-672-1-1.html

预备知识:
完成了从零开始学习OpenGL ESOpenGL ES2.0–Iphone开发指引 的学习。

1.使用GLKView简化OpenGL初始化。
在前面的教程里面,介绍了如何从一个UIView来建立OpenGL工程,里面做了很多初始化工作,比如:创建render buffer 和 frame buffer等。这些重复性的工作,apple提供了一个基础类GLKView给我使用。

在TTAppDelegate.m中初始化了一个TTViewController为rootViewController。
self.viewController = [[[TTViewController alloc]initWithNibName:@"TTViewController" bundle:nil] autorelease];
self.window.rootViewController = self.viewController;
点击TTViewController.xib 我们可以看到它的属性是GLKView,而不是通常的UIView。


TTViewController继承于GLKViewController,GLKViewController在标准viewcontroller函数的基础上添加了OpenGL ES rendering loop相关的函数。

2. EAGLContext初始化
EAGLContext是OpenGL ES RenderingContext的iOS实现。
每个程序有自己的EAGLContext,这保证了各个OpenGL ES程序互不干扰。
在EAGLContext需要在调用任何OpenGL ES api前创建并初始化。
下面的代码在- (void)viewDidLoad函数的最前面。
创建了一个OpenGL ES 2.0的EAGLContext,并设为view的context。
self.context = [[[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES2] autorelease];
if (!self.context) {     NSLog(@"Failedto create ES context"); } GLKView *view = (GLKView *)self.view; view.context = self.context; view.drawableDepthFormat =GLKViewDrawableDepthFormat24;

3. setupGL初始化
在解析[self setupGL]前,我们来看下这个工程运行的结果。


2个不同颜色的正方体在旋转,但是这里只有一个顶点法线数组gCubeVertexData。
仔细观察代码,会发现这里有2个着色器,
一个是GLKBaseEffect,为了方便OpenGL ES 1.0转移到2.0的通用着色器。
一个是OpenGL ES 2.0新添加的可编程着色器,使用跨平台的着色语言GLSL

为了方便观察,我在原始代码上加了2个宏来控制打开其中某一个还是两个一起。
#define SHADER_1
#define SHADER_2
从宏块可以方便的看出哪些代码是属于哪个着色器相关的,哪些代码是共用的。
屏蔽某一个宏可以屏蔽某个正方体的显示输出。

回到setupGL,第一行代码是
[EAGLContext setCurrentContext:self.context];
在某个线程调用OpenGL api前,需要设置api作用与哪个context,
这个函数设置当前线程操作的context。
注意:不要多个线程同时操控同一个context。

然后是GLSL着色器初始化,后面再详细分析。
#if defined (SHADER_2)
    [selfloadShaders];
#endif
GLKBaseEffect着色器初始化。
#if defined (SHADER_1)
   self.effect= [[[GLKBaseEffect alloc] init] autorelease];
   self.effect.light0.enabled = GL_TRUE;
   self.effect.light0.diffuseColor = GLKVector4Make(1.0f, 0.4f, 0.4f,1.0f);
#endif

最后是顶点数组和法线的初始化。
//在涉及到消隐等情况(可能遮挡),都要开启深度测。
//glEnable(GL_DEPTH_TEST),硬件上打开了深度缓存区,当有新的同样XY坐标的片断到来时,
//比较两者的深度。开启这个选项,在绘制每一帧前需要glClear(GL_DEPTH_BUFFER_BIT),后面会看到。   
  glEnable(GL_DEPTH_TEST); //这里使用VertexArray Objects加载顶点法线数据。    glGenVertexArraysOES(1, &_vertexArray);    glBindVertexArrayOES(_vertexArray);      glGenBuffers(1, &_vertexBuffer);    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);    glBufferData(GL_ARRAY_BUFFER, sizeof(gCubeVertexData), gCubeVertexData,GL_STATIC_DRAW);    glEnableVertexAttribArray(GLKVertexAttribPosition); // GLKVertexAttribPosition顶点属性指针类型:顶点坐标 // 3 一个顶点坐标由几个值来表示,x,y,z // GL_FLOAT 每个数值的数据类型 //直接使用24并不优雅,24 = sizoef(GLfloat) * 6; 到下一个顶点坐标数据的步长。    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE,24, BUFFER_OFFSET(0));    glEnableVertexAttribArray(GLKVertexAttribNormal); //直接使用12并不优雅,12 = sizoef(GLfloat) * 3;    glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 24,BUFFER_OFFSET(12));
  
glBindVertexArrayOES(0); }

关于VertexArray Objects请猛击。
这里我简单画了个关系图,可能更好理解。



4. loadShaders初始化GLSL着色器
与可编程着色器相关的几个函数如下:
- (BOOL)loadShaders;
- (BOOL)compileShader:(GLuint *)shadertype:(GLenum)type file:(NSString *)file;
- (BOOL)linkProgram:(GLuint)prog;
- (BOOL)validateProgram:(GLuint)prog;
validateProgram没有使用,compileShader和linkProgram可以作为公共库函数,不需要修改。
唯一需要根据需求修改的是loadShaders函数。

loadShaders大部分步骤都有英文注释,标准的加载流程:
1.创建程序。
2.创建并编译 顶点着色器和片段着色器。
3.把 顶点着色器和片段着色器 与 程序连接起来。
4.设置 顶点着色器和片段着色器 的输入参数。
5.链接程序。
6.获取 uniform 指针。
注意:这步只能在5成功后才能调用,在linkProgrom前,uniform位置是不确定的。
7.断开 顶点着色器和片段着色器 ,并释放它们。
注意:程序并没释放。

第4步是会变化的部分,第6步为可选。

先来看看Shader.vsh顶点着色器的代码。
// attribute 表示输入参数
//vec4 –4 个浮点数组成的向量
attribute vec4 position;
//vec3 –3 个浮点数组成的向量
attribute vec3 normal;
 
// varying在顶点着色器里面表示将作为片段着色器的输入。
//lowp表示低精度
varying lowp vec4 colorVarying;

//uniform和attribute一样是输入参数。
//可以简单这样理解,attribute为常量,uniform是变量。
//mat4 – 浮点数的 4X4 矩阵
uniform mat4 modelViewProjectionMatrix;
//mat3 – 浮点数的 3X3 矩阵
uniform mat3 normalMatrix;
void main() {     vec3eyeNormal = normalize(normalMatrix * normal);     vec3lightPosition = vec3(0.0, 0.0, 1.0);     vec4diffuseColor = vec4(0.4, 0.4, 1.0, 1.0);
    floatnDotVP = max(0.0, dot(eyeNormal, normalize(lightPosition)));         //计算出颜色输出给片段着色器。    colorVarying = diffuseColor * nDotVP;     //gl_Position保存了当前顶点的位置信息     gl_Position= modelViewProjectionMatrix * position; }

再来看片段着色器代码。
// varying在片段着色器里面表示从顶点着色器传过来的输入参数
//片段着色器不能直接传如参数,只能接收顶点着色器的输出。
varying lowp vec4 colorVarying;
void main() {    gl_FragColor = colorVarying; }
我们现在回头再看看loadShaders函数里面的第4步。
glBindAttribLocation(_program, ATTRIB_VERTEX,"position");
glBindAttribLocation(_program, ATTRIB_NORMAL, "normal");
"position"和"normal"与顶点着色器代码里面的两个attribute对应,
分别与setupGL加载的顶点数组里面的顶点和法线数据对应起来。

5.update更新数据
update是一个delegate 方法用来更新数据,不做UI更新。
好吧,这部分是很头痛的矩阵变换,暂时不去分析他的算法(其实是我不懂=,=)。

对于GLKBaseEffect着色器,下面的代码用来更新矩阵。
self.effect.transform.projectionMatrix = projectionMatrix;
self.effect.transform.modelviewMatrix = modelViewMatrix;
而可编程着色器,先保存在下面2个变量中,然后再draw的时候作为输入参数传递给着色器。
_normalMatrix =GLKMatrix3InvertAndTranspose(GLKMatrix4GetMatrix3(modelViewMatrix), NULL);
_modelViewProjectionMatrix =GLKMatrix4Multiply(projectionMatrix, modelViewMatrix);
6.drawInRect输出到屏幕
- (void)glkView:(GLKView *)viewdrawInRect:(CGRect)rect
{
   glClearColor(0.65f, 0.65f, 0.65f, 1.0f);
   //GL_DEPTH_BUFFER_BIT与前面的glEnable(GL_DEPTH_TEST)对应。
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //指定Draw使用的顶点数组。
   glBindVertexArrayOES(_vertexArray);
    
#if defined (SHADER_1)
    // Renderthe object with GLKit
    // prepareToDraw绑定着色器到当前的OpenGL ES context。
   [self.effect prepareToDraw];
    //画出第一个正方体
   glDrawArrays(GL_TRIANGLES, 0, 36);
#endif
    
#if defined (SHADER_2)
    // Renderthe object again with ES2
    //绑定着色器到当前的OpenGLES context。
   glUseProgram(_program);
    
   //Uniform变量参数输入。
   glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, 0,_modelViewProjectionMatrix.m);
   glUniformMatrix3fv(uniforms[UNIFORM_NORMAL_MATRIX], 1, 0,_normalMatrix.m);
    //画出第二个正方体    glDrawArrays(GL_TRIANGLES, 0, 36); #endif }
默认工程的不足:
1)顶点数组并没有优化,应使用顶点与索引的方式减少重复点。
   参考http://ityran.com/article-5-1.html有关glDrawElements的部分。
2)演示了2种着色器的同时使用,可只选其一来使用。

附:GLSL数据类型
void – 用于没有返回值的函式
bool – 条件类型,其值可以是真或假
int – 带负号整数
float – 浮点数
vec2 – 2 个浮点数组成的向量
vec3 – 3 个浮点数组成的向量
vec4 – 4 个浮点数组成的向量
bvec2 – 2 个布林组成的向量
bvec3 – 3 个布林组成的向量
bvec4 – 4 个布林组成的向量
ivec2 – 2 个整数组成的向量
ivec3 – 3 个整数组成的向量
ivec4 – 4 个整数组成的向量
mat2 – 浮点数的 2X2 矩阵
mat3 – 浮点数的 3X3 矩阵
mat4 – 浮点数的 4X4 矩阵
sampler1D – 用来存取一维纹理的句柄(handle)(或:操作,作名词解。)
sampler2D – 用来存取二维纹理的句柄
sampler3D – 用来存取三维纹理的句柄
samplerCube – 用来存取立方映射纹理的句柄
sampler1Dshadow – 用来存取一维深度纹理的句柄
sampler2Dshadow – 用来存取二维深度纹理的句柄

参考The OpenGL ES Shading Language
下载地址:http://ityran.com/thread-673-1-1.html

参考文献:
OpenGLES Programming Guide for iOS
http://www.khronos.org/opengles/2_X/

8

鲜花

握手

雷人

路过

鸡蛋

刚表态过的朋友 (8 人)

发表评论

最新评论

引用 gz_sun 2013-5-9 14:55
非常好。棒极了。全部收下。
引用 3h2om 2012-11-16 13:21
能详细介绍下:
//Uniform变量参数输入。
   glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, 0,_modelViewProjectionMatrix.m);
   glUniformMatrix3fv(uniforms[UNIFORM_NORMAL_MATRIX], 1, 0,_normalMatrix.m);
引用 □白开水 2012-8-14 17:08
引用 qinwenzhou 2012-6-19 10:29
真的很好,初学者的福音
引用 kehaoran74 2012-3-7 20:23
牛吧的大大啊
引用 ale423 2012-2-22 23:36
学习了
引用 halley 2012-2-19 14:24
牛逼啊
引用 halley 2012-2-19 14:23
不错不错
引用 liaobuqide2 2012-2-19 12:23
看不懂挖
引用 Android 2012-2-2 19:18
很精彩的文章!
引用 go369 2012-1-18 01:04
非常好。棒极了。全部收下。
引用 Jack_zer 2012-1-9 15:40
蛮好的
引用 panzhiccp 2011-12-28 10:34
顶一个
引用 chichi1314 2011-12-28 04:12
学习
引用 chichi1314 2011-12-28 04:09
Mark了
引用 lucky 2011-12-26 12:30
simon tutorial is good as well
引用 lucky 2011-12-26 11:25
顶一下!!好东西呢 嘻嘻
引用 Andy 2011-12-2 18:41
up +1
引用 传说中的k 2011-12-2 16:46
好牛,先顶了,慢慢学习
引用 40k 2011-12-2 11:39
娃哈哈哈哈~我一向手快得

查看全部评论(23)

手机版|小黑屋|Archiver| 泰然论坛 ( 蜀ICP备13018980号-2 )

GMT+8, 2017-6-24 13:06 , Processed in 0.032870 second(s), 13 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

返回顶部