• OpenGL2.0OpenGL3.0 的不同与共同点 在OpenGL2.0中vertex shader 中 可以不指定 version 如果不指定 version 对于iOS来说 默认认为是OpenGL2.0 OpenGL2.0中不能使用 in out inout 关键字 , in out...


    OpenGL2.0 OpenGL3.0 的不同与共同点


    OpenGL2.0vertex shader 可以不指定 version 如果不指定 version 对于iOS来说 默认认为是OpenGL2.0  OpenGL2.0中不能使用 in out inout 关键字 in out inout 关键字 OpenGL3.0中的关键字

    但是对于要使用  

    OpenGL3.0fragment shader 中的vec4必须要指定 精度 否则就会报错

    报的错误为:ERROR: 0:8: 'vec4' : declaration must include a precision qualifier for type


    iOS android

    shader中使用

    #version 300 es 指定使用OpenGL3.0

    #version 100 es 指定使用OpenGL2.0 (不指定version 默认为OpenGL2.0)


    如果shader文件没有声明版本,则会认为它使用的是OpenGL ES 2.0shader版本1.0。为了使版本号相匹配,OpenGL ES 3.0shader版本直接从1.0跳到了3.0


    GLSL ES 3.0删除gl_FragDatagl_FragColor内置片段输出变量。而是使用out限定符声明自己的片段输出变量。


    顶点缓冲区对象VBO与顶点数组对象VAO

    VBO可以将顶点数据保存在显存中,绘制时直接从显存中取数据,减少了数据传输的开销。

    通过VBO我们可以将顶点属性数据保存在显存中,当绘制时问题又来了,需要调用好几个函数,过程挺复杂的。为了解决这个问题,OpenGL又引入了VAO(Vertex Array Object)来关联VBO中的数据 VAO详解),有了VAO,任何数组形式的GL函数调用都会添加到VAO的绘制列表当中(直到解除VAO绑定),当需要绘制的时候,我们仅需要重新绑定VAO,那么之前创建的绘制列表将会重新激活,使得绘制代码更加简洁。


    glDrawArrays

    glDrawElements


     默认精度修饰符 precision 解释


    默认精度修饰符

             precision precision-qualifier type;

             precision可以用来确定默认精度修饰符。type可以是intfloat或采样器类型,precision-qualifier可以是lowp, mediump, 或者highp。任何其他类型和修饰符都会引起错误。如果typefloat类型,那么该精度(precision-qualifier)将适用于所有无精度修饰符的浮点数声明(标量,向量,矩阵)。如果typeint类型,那么该精度(precision-qualifier)将适用于所有无精度修饰符的整型数声明(标量,向量)。包括全局变量声明,函数返回值声明,函数参数声明,和本地变量声明等。没有声明精度修饰符的变量将使用和它最近的precision语句中的精度。


             在顶点语言中有如下预定义的全局默认精度语句:

    precision highp float;

    precision highp int;

    precision lowp sampler2D;

    precision lowp samplerCube;


             在片元语言中有如下预定义的全局默认精度语句:

    precision mediump int;

    precision lowp sampler2D;

    precision lowp samplerCube;

             片元语言没有默认的浮点数精度修饰符。因此,对于浮点数,浮点数向量和矩阵变量声明,要么声明必须包含一个精度修饰符,要不默认的精度修饰符在之前已经被声明过了。


    修饰顺序

            当需要使用多个修饰时,它们必须遵循严格的顺序:

    (1)invariant-qualifier   storage-qualifier   precision-qualifier

    (2)storage-qualifier    parameter-qualifier    precision-qualifier



    参考内容:

    http://www.xuebuyuan.com/1980605.html

    展开全文
  • 因为项目上的原因,最近开始接触如何在iOS上绘制图形,其中所需要用到的知识就是OpenGL ES 2.0(Open Graphics Library for embedded systems)中的相关内容!本篇文章是为了从未接触过的朋友而准备的也是为了自己...

    http://blog.csdn.net/liu132641/article/details/46826121

    因为项目上的原因,最近开始接触如何在iOS上绘制图形,其中所需要用到的知识就是OpenGL ES 2.0(Open Graphics Library for embedded systems)中的相关内容!本篇文章是为了从未接触过的朋友而准备的也是为了自己加深基础概念的印象和将来方便查询,故此有了以下的内容,大神飘过哈。

    在开始项目之前,需要对计算机图形学有一定的了解,所以以下会简单的介绍一些在OpenGL ES里边所需要了解的几个概念:

    1、OpenGL针对桌面上的图形绘制库。OpenGL ES是针对嵌入式系统平台而从OpenGL 衍生出来的一个精简版图形绘制库。

    2、以下是在嵌入式设备(iPhone,iPad,iPod等,另外的android设备上也是类似)的运行过程。

             


      2.1、GPU(图形处理单元):能够结合几何,颜色,灯光和其他数据而产生一个屏幕图像的硬件组件。

      2.2、iOS中的OpenGL ES框架是使用GLKit(iOS5引入的),该框架简化了许多常用的编程任务,同时部分隐藏了OpenGL ES1.0于2.0之间的差异,所以可以较好的提高编程的效率

    3、渲染:用3D数据生成2D图像的过程就叫做渲染,其中GPU控制的缓存是高效渲染的关键。

    4、为GPU提供需要处理的3D数据:为了在嵌入式设备的屏幕上绘制图像,首先需要做的一件事情就是需要为图形处理硬件(GPU)提供需要处理的3D数据,虽然有各种各样的为GPU提供数据的方式,但是目前为止,最好的一种方式就是使用“缓存”---图形处理器所能够控制和管理的连续RAM。

                其中为缓存提供数据的方式有7个步骤:

    1>生成 generate(告诉OpenGL ES为GPU控制的缓存生成一个独一无二的标识符);

    2>绑定bind (告诉OpenGL ES接下来的运算使用哪一个缓存);

    3>缓存数据buffer data(让OpenGL ES为当前绑定的缓存分配并初始化足够的连续内存,该过程通常是从CPU控制的内存复制数据到分配的内存)

    4>启用(Enable)或者禁止(Disable)(告诉OpenGL ES在接下来的渲染中是否使用缓存中的数据);

    5>设置指针set Pointers(告诉OpenGL ES在缓存中的数据的类型和所有需要访问的数据的内存偏移);

    6>绘图Draw(告诉OpenGL ES使用当前绑定并启用的缓存中的数据渲染整个场景或者某个场景的一部分);

    7>删除Delete(告诉OpenGL ES删除以前生成的缓存并释放相关的资源)

      帧缓存:前、后帧缓存。帧缓存是用来接收渲染结果的缓冲区。前帧缓存决定了屏幕上显示的像素颜色,同时和后帧缓存进行切换

              上下文(context):用于配置OpenGL ES的保存在特定平台的软件数据结构中的信息会被封装到一个OpenGL ES的上下文。

    5、另外的由于OpenGL ES是在计算机上绘制图形,所以需要对数学中的涉及到的如 笛卡尔坐标系,向量,法向量等概念需要补足,另外设计到线性代数里的矩阵运算,高数里边的设计到向量运算,以及空间解析几何的知识需要数学并灵活运用到算法中


    展开全文
  • 这一天,打开关,关了搜,搜了再打开,反反复复找老罗的博客文章,看来还是得建个置顶,方便自已,方便他人。 再次感谢老罗,越往后研究,越...罗朝辉 《OpenGL ES 2.0 iOS教程》完整列表 [移动开发] [OpenGL

    这一天,打开关,关了搜,搜了再打开,反反复复找老罗的博客文章,看来还是得建个置顶,方便自已,方便他人。

    再次感谢老罗,越往后研究,越感觉到,老罗之前“啰里巴嗦”的这些基本知识,眼下真如宝典般有用!谢谢。

    水总是在渴的时侯被人感觉带得少了,其余时间再少的水,都歉多,歉沉!


    罗朝辉 《OpenGL ES 2.0 iOS教程》完整列表


    [移动开发] [OpenGL ES 08]Per-Pixel Light及卡通效果

    [OpenGL ES 08]Per-Pixel Light及卡通效果 罗朝辉 (http://blog.csdn.com/kesalin/) 本文遵循“署名-非商业用途-保持一致”创作公用协议   这是《OpenGL ES 教程》的第九篇,前八篇请参考如下链接: [OpenGL ES 01]iOS上OpenGL ES之初体验 [OpenGL ES 0...

    3D 3d OpenGL ES opengl es

    飘飘白云 2013-01-11 21:02 阅读(2055) 评论(7)

    [移动开发] [OpenGL ES 07-1]光照原理

    [OpenGL ES 07-1]光照原理 罗朝辉 (http://blog.csdn.net/kesalin/) 本文遵循“署名-非商业用途-保持一致”创作公用协议   这是《OpenGL ES 教程》的第七篇,前六篇请参考如下链接: [OpenGL ES 01]iOS上OpenGL ES之初体验 [OpenGL ES 02]OpenGL ES渲染管...

    OpenGL ES opengl es 图形

    飘飘白云 2012-12-29 21:21 阅读(2238) 评论(0)

    [移动开发] [OpenGL ES 06]使用VBO:顶点缓存

    [OpenGL ES 06]使用VBO:顶点缓存 罗朝辉 (http://www.cnblogs.com/kesalin/) 本文遵循“署名-非商业用途-保持一致”创作公用协议   这是《OpenGL ES 教程》的第六篇,前五篇请参考如下链接: [OpenGL ES 01]iOS上OpenGL ES之初体验 [OpenGL ES 02]OpenGL...

    OpenGL ES opengl es vbo

    飘飘白云 2012-12-20 22:46 阅读(2144) 评论(2)

    [移动开发] [OpenGL ES 05]相对空间变换及颜色

    [OpenGL ES 05]相对空间变换及颜色 罗朝辉 (http://blog.csdn.net/kesalin) 本文遵循“署名-非商业用途-保持一致”创作公用协议 这是《OpenGL ES 教程》的第五篇,前四篇请参考如下链接: [OpenGL ES 01]iOS上OpenGL ES之初体验 [OpenGL ES 02]OpenGL ES渲染管线与着...

    飘飘白云 2012-12-11 10:53 阅读(2141) 评论(1)

    [移动开发] [OpenGL ES 04]3D变换实践篇:平移,旋转,缩放

    [OpenGL ES 04]3D变换实践篇:平移,旋转,缩放 罗朝辉 (http://blog.csdn.net/kesalin) 本文遵循“署名-非商业用途-保持一致”创作公用协议 前言 前文《[OpenGL ES 03]3D变换:模型,视图,投影与Viewport》中已经详细介绍了3D变换相关的数学知识,也讲了基本的模型变换:平移,旋转与缩放,如果你还没有阅读前文或对这...

    飘飘白云 2012-12-07 22:15 阅读(3281) 评论(18)

    [移动开发] [OpenGL ES 02]OpenGL ES渲染管线与着色器

    [OpenGL ES 02]OpenGL ES渲染管线与着色器  罗朝辉 (http://blog.csdn.net/kesalin) 本文遵循“署名-非商业用途-保持一致”创作公用协议 前言 在前文《[OpenGL ES 01]OpenGL ES之初体验》中我们学习了如何在 iOS 平台上设置OpenGL ES 环境,主要是设置 CAEAGLLayer 属性,创建 E...

    OpenGL ES opengl es

    飘飘白云 2012-11-25 22:28 阅读(5005) 评论(6)

    [移动开发] [OpenGL ES 01]OpenGL ES之初体验

    [OpenGL ES 01]OpenGL ES之初体验 罗朝辉 (http://blog.csdn.net/kesalin) 本文遵循“署名-非商业用途-保持一致”创作公用协议   一,什么是 OpenGL ES? OpenGL ES 是专门为手持设备制定的 3D 规范,它是 OpenGL 的简化版,该规范由khronos.org制定,目前最新规范版本为 3.0。 Ope...

    飘飘白云 2012-11-25 08:38 阅读(8951) 评论(11)

    [移动开发] [OpenGL ES 07-2]Per-Vertex Light及深度缓存

    [OpenGL ES 07-2]Per-Vertex Light及深度缓存 罗朝辉 (http://blog.csdn/net/kesalin/) 本文遵循“署名-非商业用途-保持一致”创作公用协议   这是《OpenGL ES 2.0 教程》的第八篇,前七篇请参考如下链接: [OpenGL ES 01]iOS上OpenGL ES之初体验 [Open...

    3D 3d light Light OpenGlES 光照

    飘飘白云 2013-01-02 09:46 阅读(1629) 评论(4)

    [移动开发] [OpenGL ES 03]3D变换:模型,视图,投影与Viewport

    [OpenGL ES 03]3D变换:模型,视图,投影与Viewport 罗朝辉 (http://blog.csdn.net/kesalin) 本文遵循“署名-非商业用途-保持一致”创作公用协议 系列文章: [OpenGL ES 01]OpenGL ES之初体验 [OpenGL ES 02]OpenGL ES渲染管线与着色器 前言 本来打算直接写教程 04...

    3D 3d OpenGL ES opengl es

    飘飘白云 2012-12-06 21:05 阅读(4425) 评论(5)


    展开全文
  • OpenGL ES 是可以在iphone上实现2D和3D图形编程的低级API。 如果你之前接触过 cocos2d,sparrow,corona,unity 这些框架,你会发现其实它们都是基于OpenGL上创建的。 多数程序员选择使用这些框架,而不是直接调用...

    本教程源码地址下载:https://github.com/wanglixin1999/HelloGL


    OpenGL ES 是可以在iphone上实现2D和3D图形编程的低级API。

    如果你之前接触过 cocos2d,sparrow,corona,unity 这些框架,你会发现其实它们都是基于OpenGL上创建的。

    多数程序员选择使用这些框架,而不是直接调用OpenGL,因为OpenGL实在是太难用了。

    而这篇教程,就是为了让大家更好地入门而写的。 

    在这个系列的文章中,你可以通过一些实用又容易上手的实验,创建类似hello world的APP。例如显示一些简单的立体图形。

    流程大致如下:

    ·创建一个简单的OpenGL app

    ·编译并运行 vertex & fragment shaders

    ·通过vertex buffer,在屏幕上渲染一个简单矩形

    ·使用投影和 model-view 变形。

    ·渲染一个可以 depth testing的3D对象。

    说明:

    我并非OpenGL的专家,这些完全是通过自学得来的。如果大家发现哪些不对的地方,欢迎指出。

    OpenGL ES1.0 和 OpenGL ES2.0

    第一件你需要搞清楚的事,是OpenGL ES 1.0 和 2.0的区别。

    他们有多不一样?我只能说他们很不一样。

    OpenGL ES1.0:

    针对固定管线硬件(fixed pipeline),通过它内建的functions来设置诸如灯光、,vertexes(图形的顶点数),颜色、camera等等的东西。

    OpenGL ES2.0:

    针对可编程管线硬件(programmable pipeline),基于这个设计可以让内建函数见鬼去吧,但同时,你得自己动手编写任何功能。

    “TMD”,你可能会这么想。这样子我还可能想用2.0么?

    但2.0确实能做一些很cool而1.0不能做的事情,譬如:toon shader(贴材质).

    利用opengles2.0,甚至还能创建下面的这种很酷的灯光和阴影效果:

    OpenGL ES2.0只能够在iphone 3GS+、iPod Touch 3G+ 和所有版本的ipad上运行。庆幸现在大多数用户都在这个范围。

    开始吧

    尽管Xcode自带了OpenGL ES的项目模板,但这个模板自行创建了大量的代码,这样会让初学者感到迷惘。

    因此我们通过自行编写的方式来进行,通过一步一步编写,你能更清楚它的工作机制。

    启动Xcode,新建项目。

    点击下一步,把这个项目命名为HelloOpenGL,点击下一步,选择存放目录,点击“创建”。

    接下来,你要在这个OpenGLView.m 文件下加入很多代码。

    1)  添加必须的framework (框架)

    加入:OpenGLES.frameworks 和 QuartzCore.framework

    在项目的Groups&Files 目录下,选择target “HelloOpenGL”,展开Link Binary with Libraries部分。这里是项目用到的框架。

    “+”添加,选择OpenGLES.framework, 重复一次把QuartzCore.framework也添加进来。

    2)修改OpenGLView.h

    如下:引入OpenGL的Header,创建一些后面会用到的实例变量。

    1. #import <UIKit/UIKit.h> 
    2. #import <QuartzCore/QuartzCore.h> 
    3. #include <OpenGLES/ES2/gl.h> 
    4. #include <OpenGLES/ES2/glext.h> 
    5.   
    6. @interface OpenGLView : UIView { 
    7.     CAEAGLLayer* _eaglLayer; 
    8.     EAGLContext* _context; 
    9.     GLuint _colorRenderBuffer; 
    10.   
    11. @end 

    3)设置layer class 为 CAEAGLLayer

    1. + (Class)layerClass { 
    2.     return [CAEAGLLayer class]; 

    想要显示OpenGL的内容,你需要把它缺省的layer设置为一个特殊的layer。(CAEAGLLayer)。这里通过直接复写layerClass的方法。

    4) 设置layer为不透明(Opaque)

    1. - (void)setupLayer { 
    2.     _eaglLayer = (CAEAGLLayer*) self.layer; 
    3.     _eaglLayer.opaque = YES; 

    因为缺省的话,CALayer是透明的。而透明的层对性能负荷很大,特别是OpenGL的层。

    (如果可能,尽量都把层设置为不透明。另一个比较明显的例子是自定义tableview cell)

    5)创建OpenGL context

    1. - (void)setupContext {    
    2.     EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2; 
    3.     _context = [[EAGLContext alloc] initWithAPI:api]; 
    4.     if (!_context) { 
    5.         NSLog(@"Failed to initialize OpenGLES 2.0 context"); 
    6.         exit(1); 
    7.     } 
    8.   
    9.     if (![EAGLContext setCurrentContext:_context]) { 
    10.         NSLog(@"Failed to set current OpenGL context"); 
    11.         exit(1); 
    12.     } 

    无论你要OpenGL帮你实现什么,总需要这个 EAGLContext

    EAGLContext管理所有通过OpenGL进行draw的信息。这个与Core Graphics context类似。

    当你创建一个context,你要声明你要用哪个version的API。这里,我们选择OpenGL ES 2.0.

    (容错处理,如果创建失败了,我们的程序会退出)

    6)创建render buffer (渲染缓冲区)

    1. - (void)setupRenderBuffer { 
    2.     glGenRenderbuffers(1, &_colorRenderBuffer); 
    3.     glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);         
    4.     [_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];     

    Render buffer 是OpenGL的一个对象,用于存放渲染过的图像。

    有时候你会发现render buffer会作为一个color buffer被引用,因为本质上它就是存放用于显示的颜色。

    创建render buffer的三步:

    1.     调用glGenRenderbuffers来创建一个新的render buffer object。这里返回一个唯一的integer来标记render buffer(这里把这个唯一值赋值到_colorRenderBuffer)。有时候你会发现这个唯一值被用来作为程序内的一个OpenGL 的名称。(反正它唯一嘛)

    2.     调用glBindRenderbuffer ,告诉这个OpenGL:我在后面引用GL_RENDERBUFFER的地方,其实是想用_colorRenderBuffer。其实就是告诉OpenGL,我们定义的buffer对象是属于哪一种OpenGL对象

    3.     最后,为render buffer分配空间。renderbufferStorage

    7)创建一个 frame buffer (帧缓冲区)

    1. - (void)setupFrameBuffer {     
    2.     GLuint framebuffer; 
    3.     glGenFramebuffers(1, &framebuffer); 
    4.     glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); 
    5.     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,  
    6.         GL_RENDERBUFFER, _colorRenderBuffer); 
    7.  } 

    Frame buffer也是OpenGL的对象,它包含了前面提到的render buffer,以及其它后面会讲到的诸如:depth buffer、stencil buffer 和 accumulation buffer。

    前两步创建frame buffer的动作跟创建render buffer的动作很类似。(反正也是用一个glBind什么的)

    而最后一步  glFramebufferRenderbuffer 这个才有点新意。它让你把前面创建的buffer render依附在frame buffer的GL_COLOR_ATTACHMENT0位置上。

    8)清理屏幕

    1. - (void)render { 
    2.     glClearColor(0, 104.0/255.0, 55.0/255.0, 1.0); 
    3.     glClear(GL_COLOR_BUFFER_BIT); 
    4.     [_context presentRenderbuffer:GL_RENDERBUFFER]; 

    为了尽快在屏幕上显示一些什么,在我们和那些 vertexes、shaders打交道之前,把屏幕清理一下,显示另一个颜色吧。(RGB 0, 104, 55,绿色吧)

    这里每个RGB色的范围是0~1,所以每个要除一下255.

    下面解析一下每一步动作:

    1.      调用glClearColor ,设置一个RGB颜色和透明度,接下来会用这个颜色涂满全屏。

    2.      调用glClear来进行这个“填色”的动作(大概就是photoshop那个油桶嘛)。还记得前面说过有很多buffer的话,这里我们要用到GL_COLOR_BUFFER_BIT来声明要清理哪一个缓冲区。

    3.      调用OpenGL context的presentRenderbuffer方法,把缓冲区(render buffer和color buffer)的颜色呈现到UIView上。

    9)把前面的动作串起来修改一下OpenGLView.m

    1. // Replace initWithFrame with this 
    2. - (id)initWithFrame:(CGRect)frame 
    3.     self = [super initWithFrame:frame]; 
    4.     if (self) {         
    5.         [self setupLayer];         
    6.         [self setupContext];                 
    7.         [self setupRenderBuffer];         
    8.         [self setupFrameBuffer];                 
    9.         [self render];         
    10.     } 
    11.     return self; 
    12.   
    13. // Replace dealloc method with this 
    14. - (void)dealloc 
    15.     [_context release]; 
    16.     _context = nil; 
    17.     [super dealloc]; 

    10)把App Delegate和OpenGLView 连接起来

    在HelloOpenGLAppDelegate.h 中修改一下:

    1. // At top of file 
    2. #import "OpenGLView.h" 
    3.   
    4. // Inside @interface 
    5. OpenGLView* _glView; 
    6.   
    7. // After @interface 
    8. @property (nonatomic, retain) IBOutlet OpenGLView *glView; 

    接下来修改.m文件:

    1. // At top of file 
    2. @synthesize glView=_glView; 
    3.   
    4. // At top of application:didFinishLaunchingWithOptions 
    5. CGRect screenBounds = [[UIScreen mainScreen] bounds];     
    6. self.glView = [[[OpenGLView alloc] initWithFrame:screenBounds] autorelease]; 
    7. [self.window addSubview:_glView]; 
    8.   
    9. // In dealloc 
    10. [_glView release]; 

    一切顺利的话,你就能看到一个新的view在屏幕上显示。

    这里是OpenGL的世界。

    添加shaders:顶点着色器和片段着色器

    在OpenGL ES2.0 的世界,在场景中渲染任何一种几何图形,你都需要创建两个称之为“着色器”的小程序。

    着色器由一个类似C的语言编写- GLSL。知道就好了,我们不深究。

    这个世界有两种着色器(Shader):

    ·Vertex shaders – 在你的场景中,每个顶点都需要调用的程序,称为“顶点着色器”。假如你在渲染一个简单的场景:一个长方形,每个角只有一个顶点。于是vertex shader 会被调用四次。它负责执行:诸如灯光、几何变换等等的计算。得出最终的顶点位置后,为下面的片段着色器提供必须的数据。

    ·Fragment shaders – 在你的场景中,大概每个像素都会调用的程序,称为“片段着色器”。在一个简单的场景,也是刚刚说到的长方形。这个长方形所覆盖到的每一个像素,都会调用一次fragment shader。片段着色器的责任是计算灯光,以及更重要的是计算出每个像素的最终颜色。

    下面我们通过简单的例子来说明。

    打开你的xcode,File\New\New File… 选择iOS\Other\Empty, 点击下一步。命名为:

    SimpleVertex.glsl 点击保存。

    打开这个文件,加入下面的代码:

    1. attribute vec4 Position; // 1 
    2. attribute vec4 SourceColor; // 2 
    3.   
    4. varying vec4 DestinationColor; // 3 
    5.   
    6. void main(void) { // 4 
    7.     DestinationColor = SourceColor; // 5 
    8.     gl_Position = Position; // 6 

    我们一行一行解析:

    1 “attribute”声明了这个shader会接受一个传入变量,这个变量名为“Position”。在后面的代码中,你会用它来传入顶点的位置数据。这个变量的类型是“vec4”,表示这是一个由4部分组成的矢量。

    2 与上面同理,这里是传入顶点的颜色变量。

    3 这个变量没有“attribute”的关键字。表明它是一个传出变量,它就是会传入片段着色器的参数。“varying”关键字表示,依据顶点的颜色,平滑计算出顶点之间每个像素的颜色。

    文字比较难懂,我们一图胜千言:

    图中的一个像素,它位于红色和绿色的顶点之间,准确地说,这是一个距离上面顶点55/100,距离下面顶点45/100的点。所以通过过渡,能确定这个像素的颜色。

    4 每个shader都从main开始– 跟C一样嘛。

    5 设置目标颜色 = 传入变量:SourceColor

    6 gl_Position 是一个内建的传出变量。这是一个在 vertex shader中必须设置的变量。这里我们直接把gl_Position = Position; 没有做任何逻辑运算。

    一个简单的vertex shader 就是这样了,接下来我们再创建一个简单的fragment shader。

    新建一个空白文件:

    File\New\New File… 选择iOS\Other\Empty

    命名为:SimpleFragment.glsl 保存。

    打开这个文件,加入以下代码:

    1. varying lowp vec4 DestinationColor; // 1 
    2.   
    3. void main(void) { // 2 
    4.     gl_FragColor = DestinationColor; // 3 

    下面解析:

    1 这是从vertex shader中传入的变量,这里和vertex shader定义的一致。而额外加了一个关键字:lowp。在fragment shader中,必须给出一个计算的精度。出于性能考虑,总使用最低精度是一个好习惯。这里就是设置成最低的精度。如果你需要,也可以设置成medp或者highp.

    2 也是从main开始嘛

    3 正如你在vertex shader中必须设置gl_Position, 在fragment shader中必须设置gl_FragColor.

    这里也是直接从 vertex shader中取值,先不做任何改变。

    还可以吧?接下来我们开始运用这些shader来创建我们的app。

    编译 Vertex shader 和 Fragment shader

    目前为止,xcode仅仅会把这两个文件copy到application bundle中。我们还需要在运行时编译和运行这些shader。

    你可能会感到诧异。为什么要在app运行时编译代码?

    这样做的好处是,我们的着色器不用依赖于某种图形芯片。(这样才可以跨平台嘛)

    下面开始加入动态编译的代码,打开OpenGLView.m

    在initWithFrame: 方法上方加入:

    1. - (GLuint)compileShader:(NSString*)shaderName withType:(GLenum)shaderType { 
    2.   
    3.     // 1 
    4.     NSString* shaderPath = [[NSBundle mainBundle] pathForResource:shaderName  
    5.         ofType:@"glsl"]; 
    6.     NSError* error; 
    7.     NSString* shaderString = [NSString stringWithContentsOfFile:shaderPath  
    8.         encoding:NSUTF8StringEncoding error:&error]; 
    9.     if (!shaderString) { 
    10.         NSLog(@"Error loading shader: %@", error.localizedDescription); 
    11.         exit(1); 
    12.     } 
    13.   
    14.     // 2 
    15.     GLuint shaderHandle = glCreateShader(shaderType);     
    16.   
    17.     // 3 
    18. constchar* shaderStringUTF8 = [shaderString UTF8String];     
    19.     int shaderStringLength = [shaderString length]; 
    20.     glShaderSource(shaderHandle, 1, &shaderStringUTF8, &shaderStringLength); 
    21.   
    22.     // 4 
    23.     glCompileShader(shaderHandle); 
    24.   
    25.     // 5 
    26.     GLint compileSuccess; 
    27.     glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess); 
    28.     if (compileSuccess == GL_FALSE) { 
    29.         GLchar messages[256]; 
    30.         glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]); 
    31.         NSString *messageString = [NSString stringWithUTF8String:messages]; 
    32.         NSLog(@"%@", messageString); 
    33.         exit(1); 
    34.     } 
    35.   
    36.     return shaderHandle; 
    37.   

    下面解析:

    1 这是一个UIKit编程的标准用法,就是在NSBundle中查找某个文件。大家应该熟悉了吧。

    2 调用 glCreateShader来创建一个代表shader 的OpenGL对象。这时你必须告诉OpenGL,你想创建 fragment shader还是vertex shader。所以便有了这个参数:shaderType

    3 调用glShaderSource ,让OpenGL获取到这个shader的源代码。(就是我们写的那个)这里我们还把NSString转换成C-string

    4 最后,调用glCompileShader 在运行时编译shader

    5 大家都是程序员,有程序的地方就会有fail。有程序员的地方必然会有debug。如果编译失败了,我们必须一些信息来找出问题原因。 glGetShaderiv 和 glGetShaderInfoLog  会把error信息输出到屏幕。(然后退出)

    我们还需要一些步骤来编译vertex shader 和frament shader。

    - 把它们俩关联起来

    - 告诉OpenGL来调用这个程序,还需要一些指针什么的。

    在compileShader: 方法下方,加入这些代码

    1. - (void)compileShaders { 
    2.   
    3.     // 1 
    4.     GLuint vertexShader = [self compileShader:@"SimpleVertex"  
    5.         withType:GL_VERTEX_SHADER]; 
    6.     GLuint fragmentShader = [self compileShader:@"SimpleFragment"  
    7.         withType:GL_FRAGMENT_SHADER]; 
    8.   
    9.     // 2 
    10.     GLuint programHandle = glCreateProgram(); 
    11.     glAttachShader(programHandle, vertexShader); 
    12.     glAttachShader(programHandle, fragmentShader); 
    13.     glLinkProgram(programHandle); 
    14.   
    15.     // 3 
    16.     GLint linkSuccess; 
    17.     glGetProgramiv(programHandle, GL_LINK_STATUS, &linkSuccess); 
    18.     if (linkSuccess == GL_FALSE) { 
    19.         GLchar messages[256]; 
    20.         glGetProgramInfoLog(programHandle, sizeof(messages), 0, &messages[0]); 
    21.         NSString *messageString = [NSString stringWithUTF8String:messages]; 
    22.         NSLog(@"%@", messageString); 
    23.         exit(1); 
    24.     } 
    25.   
    26.     // 4 
    27.     glUseProgram(programHandle); 
    28.   
    29.     // 5 
    30.     _positionSlot = glGetAttribLocation(programHandle, "Position"); 
    31.     _colorSlot = glGetAttribLocation(programHandle, "SourceColor"); 
    32.     glEnableVertexAttribArray(_positionSlot); 
    33.     glEnableVertexAttribArray(_colorSlot); 

    下面是解析:

    1       用来调用你刚刚写的动态编译方法,分别编译了vertex shader 和 fragment shader

    2       调用了glCreateProgram glAttachShader  glLinkProgram 连接 vertex 和 fragment shader成一个完整的program。

    3       调用 glGetProgramiv  lglGetProgramInfoLog 来检查是否有error,并输出信息。

    4       调用 glUseProgram  让OpenGL真正执行你的program

    5       最后,调用 glGetAttribLocation 来获取指向 vertex shader传入变量的指针。以后就可以通过这写指针来使用了。还有调用 glEnableVertexAttribArray来启用这些数据。(因为默认是 disabled的。)

    最后还有两步:

    1 在 initWithFrame方法里,在调用render之前要加入这个:

    1. [self compileShaders]; 

    2 在@interface in OpenGLView.h 中添加两个变量:

    1. GLuint _positionSlot; 
    2. GLuint _colorSlot; 

    编译!运行!

    如果你仍能正常地看到之前那个绿色的屏幕,就证明你前面写的代码都很好地工作了。

    为这个简单的长方形创建 Vertex Data!

    在这里,我们打算在屏幕上渲染一个正方形,如下图:

    在你用OpenGL渲染图形的时候,时刻要记住一点,你只能直接渲染三角形,而不是其它诸如矩形的图形。所以,一个正方形需要分开成两个三角形来渲染。

    图中分别是顶点(0,1,2)和顶点(0,2,3)构成的三角形。

    OpenGL ES2.0的一个好处是,你可以按你的风格来管理顶点。

    打开OpenGLView.m文件,创建一个纯粹的C结构以及一些array来跟踪我们的矩形信息,如下:

    1. typedef struct { 
    2.     float Position[3]; 
    3.     float Color[4]; 
    4. } Vertex; 
    5.   
    6. const Vertex Vertices[] = { 
    7.     {{1, -1, 0}, {1, 0, 0, 1}}, 
    8.     {{1, 1, 0}, {0, 1, 0, 1}}, 
    9.     {{-1, 1, 0}, {0, 0, 1, 1}}, 
    10.     {{-1, -1, 0}, {0, 0, 0, 1}} 
    11. }; 
    12.   
    13. const GLubyte Indices[] = { 
    14.      0, 1, 2, 
    15.      2, 3, 0 
    16. }; 

    这段代码的作用是:

    1 一个用于跟踪所有顶点信息的结构Vertex (目前只包含位置和颜色。)

    2 定义了以上面这个Vertex结构为类型的array。

    3 一个用于表示三角形顶点的数组。

    数据准备好了,我们来开始把数据传入OpenGL

    创建Vertex Buffer 对象

    传数据到OpenGL的话,最好的方式就是用Vertex Buffer对象。

    基本上,它们就是用于缓存顶点数据的OpenGL对象。通过调用一些function来把数据发送到OpenGL-land。(是指OpenGL的画面?)

    这里有两种顶点缓存类型– 一种是用于跟踪每个顶点信息的(正如我们的Vertices array),另一种是用于跟踪组成每个三角形的索引信息(我们的Indices array)。

    下面我们在initWithFrame中,加入一些代码:

    1. [self setupVBOs]; 

    下面是定义这个setupVBOs:

    1. - (void)setupVBOs { 
    2.   
    3.     GLuint vertexBuffer; 
    4.     glGenBuffers(1, &vertexBuffer); 
    5.     glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); 
    6.     glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW); 
    7.   
    8.     GLuint indexBuffer; 
    9.     glGenBuffers(1, &indexBuffer); 
    10.     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); 
    11.     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW); 
    12.   

    如你所见,其实很简单的。这其实是一种之前也用过的模式(pattern)。

    glGenBuffers - 创建一个Vertex Buffer 对象

    1. glBindBuffer – 告诉OpenGL我们的vertexBuffer 是指GL_ARRAY_BUFFER 

    glBufferData – 把数据传到OpenGL-land

    想起哪里用过这个模式吗?要不再回去看看frame buffer那一段? 

    万事俱备,我们可以通过新的shader,用新的渲染方法来把顶点数据画到屏幕上。

    用这段代码替换掉之前的render:

    1. - (void)render { 
    2.     glClearColor(0, 104.0/255.0, 55.0/255.0, 1.0); 
    3.     glClear(GL_COLOR_BUFFER_BIT); 
    4.   
    5.     // 1 
    6.     glViewport(0, 0, self.frame.size.width, self.frame.size.height); 
    7.   
    8.     // 2 
    9.     glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE,  
    10.         sizeof(Vertex), 0); 
    11.     glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE,  
    12.         sizeof(Vertex), (GLvoid*) (sizeof(float) *3)); 
    13.   
    14.     // 3 
    15.     glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]),  
    16.         GL_UNSIGNED_BYTE, 0); 
    17.   
    18.     [_context presentRenderbuffer:GL_RENDERBUFFER]; 

    1       调用glViewport 设置UIView中用于渲染的部分。这个例子中指定了整个屏幕。但如果你希望用更小的部分,你可以更变这些参数。

    2       调用glVertexAttribPointer来为vertex shader的两个输入参数配置两个合适的值。

    第二段这里,是一个很重要的方法,让我们来认真地看看它是如何工作的:

    ·第一个参数,声明这个属性的名称,之前我们称之为glGetAttribLocation

    ·第二个参数,定义这个属性由多少个值组成。譬如说position是由3个float(x,y,z)组成,而颜色是4个float(r,g,b,a)

    ·第三个,声明每一个值是什么类型。(这例子中无论是位置还是颜色,我们都用了GL_FLOAT)

    ·第四个,嗯……它总是false就好了。

    ·第五个,指 stride 的大小。这是一个种描述每个 vertex数据大小的方式。所以我们可以简单地传入 sizeof(Vertex),让编译器计算出来就好。

    ·最好一个,是这个数据结构的偏移量。表示在这个结构中,从哪里开始获取我们的值。Position的值在前面,所以传0进去就可以了。而颜色是紧接着位置的数据,而position的大小是3个float的大小,所以是从 3 * sizeof(float) 开始的。

    回来继续说代码,第三点:

    3       调用glDrawElements ,它最后会在每个vertex上调用我们的vertex shader,以及每个像素调用fragment shader,最终画出我们的矩形。

    它也是一个重要的方法,我们来仔细研究一下:

    ·第一个参数,声明用哪种特性来渲染图形。有GL_LINE_STRIP 和 GL_TRIANGLE_FAN。然而GL_TRIANGLE是最常用的,特别是与VBO 关联的时候。

    ·第二个,告诉渲染器有多少个图形要渲染。我们用到C的代码来计算出有多少个。这里是通过个 array的byte大小除以一个Indice类型的大小得到的。

    ·第三个,指每个indices中的index类型

    ·最后一个,在官方文档中说,它是一个指向index的指针。但在这里,我们用的是VBO,所以通过index的array就可以访问到了(在GL_ELEMENT_ARRAY_BUFFER传过了),所以这里不需要.

    编译运行的话,你就可以看到这个画面喇。

    你可能会疑惑,为什么这个长方形刚好占满整个屏幕。在缺省状态下,OpenGL的“camera”位于(0,0,0)位置,朝z轴的正方向。

    当然,后面我们会讲到projection(投影)以及如何控制camera。

    增加一个投影

    为了在2D屏幕上显示3D画面,我们需要在图形上做一些投影变换,所谓投影就是下图这个意思:

    基本上,为了模仿人类的眼球原理。我们设置一个远平面和一个近平面,在两个平面之前,离近平面近的图像,会因为被缩小了而显得变小;而离远平面近的图像,也会因此而变大。

    打开SimpleVertex.glsl,做一下修改:

    1. // Add right before the main 
    2. uniform mat4 Projection; 
    3.   
    4. // Modify gl_Position line as follows 
    5. gl_Position = Projection * Position; 

    这里我们增加了一个叫做projection的传入变量。uniform 关键字表示,这会是一个应用于所有顶点的常量,而不是会因为顶点不同而不同的值。

    mat4 是 4X4矩阵的意思。然而,Matrix math是一个很大的课题,我们不可能在这里解析。所以在这里,你只要认为它是用于放大缩小、旋转、变形就好了。

    Position位置乘以Projection矩阵,我们就得到最终的位置数值。

    无错,这就是一种被称之“线性代数”的东西。我在大学时期后,早就忘大部分了。

    其实数学也只是一种工具,而这种工具已经由前面的才子解决了,我们知道怎么用就好。

    Bill Hollings,cocos3d的作者。他编写了一个完整的3D特性框架,并整合到cocos2d中。(作者:可能有一天我也会弄一个3D的教程)无论任何,Cocos3d包含了Objective-C的向量和矩阵库,所以我们可以很好地应用到这个项目中。

    这里,http://d1xzuxjlafny7l.cloudfront.net/downloads/Cocos3DMathLib.zip

    有一个zip文件,(作者:我移除了一些不必要的依赖)下载并copy到你的项目中。记得选上:“Copy items into destination group’s folder (if needed)” 点击Finish。

    在OpenGLView.h 中加入一个实例变量:

    1. GLuint _projectionUniform; 

    然后到OpenGLView.m文件中加上:

    1. // Add to top of file 
    2. #import "CC3GLMatrix.h" 
    3.   
    4. // Add to bottom of compileShaders 
    5. _projectionUniform = glGetUniformLocation(programHandle, "Projection"); 
    6.   
    7. // Add to render, right before the call to glViewport 
    8. CC3GLMatrix *projection = [CC3GLMatrix matrix]; 
    9. float h =4.0f* self.frame.size.height / self.frame.size.width; 
    10. [projection populateFromFrustumLeft:-2 andRight:2 andBottom:-h/2 andTop:h/2 andNear:4 andFar:10]; 
    11. glUniformMatrix4fv(_projectionUniform, 1, 0, projection.glMatrix); 
    12.   
    13. // Modify vertices so they are within projection near/far planes 
    14. const Vertex Vertices[] = { 
    15.     {{1, -1, -7}, {1, 0, 0, 1}}, 
    16.     {{1, 1, -7}, {0, 1, 0, 1}}, 
    17.     {{-1, 1, -7}, {0, 0, 1, 1}}, 
    18.     {{-1, -1, -7}, {0, 0, 0, 1}} 
    19. }; 

    ·通过调用  glGetUniformLocation 来获取在vertex shader中的Projection输入变量

    ·然后,使用math library来创建投影矩阵。通过这个让你指定坐标,以及远近屏位置的方式,来创建矩阵,会让事情比较简单。

    ·你用来把数据传入到vertex shader的方式,叫做 glUniformMatrix4fv. 这个CC3GLMatrix类有一个很方便的方法 glMatrix,来把矩阵转换成OpenGL的array格式。

    ·最后,把之前的vertices数据修改一下,让z坐标为-7. 

    编译后运行,你应该可以看到一个稍稍有点距离的正方形了。

    尝试移动和旋转吧

    如果总是要修改那个vertex array才能改变图形,这就太烦人了。

    而这正是变换矩阵该做的事(又来了,线性代数)

    在前面,我们修改了应用到投影矩阵的vertex array来达到移动图形的目的。何不试一下,做一个变形、放大缩小、旋转的矩阵来应用?我们称之为“model-view”变换。

    再回到 SimpleVertex.glsl

    1. // Add right after the Projection uniform 
    2. uniform mat4 Modelview; 
    3.   
    4. // Modify the gl_Position line 
    5. gl_Position = Projection * Modelview * Position; 

    就是又加了一个 Uniform的矩阵而已。顺便把它应用到gl_Position当中。

    然后到 OpenGLView.h中加上一个变量:

    1. GLuint _modelViewUniform; 

    到OpenGLView.m中修改:

    1. // Add to end of compileShaders 
    2. _modelViewUniform = glGetUniformLocation(programHandle, "Modelview"); 
    3.   
    4. // Add to render, right before call to glViewport 
    5. CC3GLMatrix *modelView = [CC3GLMatrix matrix]; 
    6. [modelView populateFromTranslation:CC3VectorMake(sin(CACurrentMediaTime()), 0, -7)]; 
    7. glUniformMatrix4fv(_modelViewUniform, 1, 0, modelView.glMatrix); 
    8.   
    9. // Revert vertices back to z-value 0 
    10. const Vertex Vertices[] = { 
    11.     {{1, -1, 0}, {1, 0, 0, 1}}, 
    12.     {{1, 1, 0}, {0, 1, 0, 1}}, 
    13.     {{-1, 1, 0}, {0, 0, 1, 1}}, 
    14.     {{-1, -1, 0}, {0, 0, 0, 1}} 
    15. }; 

    ·获取那个model view uniform的传入变量

    ·使用cocos3d math库来创建一个新的矩阵,在变换中装入矩阵。

    ·变换是在z轴上移动-7,而为什么sin(当前时间) 呢?

    哈哈,如果你还记得高中时候的三角函数。sin()是一个从-1到1的函数。已PI(3.14)为一个周期。这样做的话,约每3.14秒,这个函数会从-1到1循环一次。

    ·把vertex 结构改回去,把z坐标设回0.

    编译运行,就算我们把z设回0,也可以看到这个位于中间的正方形了。

    什么?一动不动的?

    当然了,我们只是调用了一次render方法。

    接下来,我们在每一帧都调用一次看看。

    理想状态下,我们希望OpenGL的渲染频率跟屏幕的刷新频率一致。

    幸运的是,Apple为我们提供了一个CADisplayLink的类。这个很好用的,马上就用吧。

    在OpenGLView.m文件,修改如下:

    1. // Add new method before init 
    2. - (void)setupDisplayLink { 
    3.     CADisplayLink* displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render:)]; 
    4.     [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];     
    5.   
    6. // Modify render method to take a parameter 
    7. - (void)render:(CADisplayLink*)displayLink { 
    8.   
    9. // Remove call to render in initWithFrame and replace it with the following 
    10. [self setupDisplayLink]; 

    这就行了,有CADisplayLink在每一帧都调用你的render方法,我们的图形看起身就好似被sin()周期地变型了。现在这个方块会前前后后地来回移动。

    不费功夫地旋转

    让图形旋转起来,才算得上有型。

    再到OpenGLView.h 中,添加成员变量。

    1. float _currentRotation; 

    在OpenGLView.m的render中,在populateFromTranslation的调用后面加上:

    1. _currentRotation += displayLink.duration *90; 
    2. [modelView rotateBy:CC3VectorMake(_currentRotation, _currentRotation, 0)]; 

    ·添加了一个叫_currentRotation的float,每秒会增加90度。

    ·通过修改那个model view矩阵(这里相当于一个用于变型的矩阵),增加旋转。

    ·旋转在x、y轴上作用,没有在z轴的。

    编译运行,你会看到一个很有型的翻转的3D效果。

    不费功夫地变成3D方块?

    之前的只能算是2.5D,因为它还只是一个会旋转的面而已。现在我们把它改造成3D的。

    把之前的vertices、indices数组注释掉吧。

    然后加上新的:

    1. const Vertex Vertices[] = { 
    2.     {{1, -1, 0}, {1, 0, 0, 1}}, 
    3.     {{1, 1, 0}, {1, 0, 0, 1}}, 
    4.     {{-1, 1, 0}, {0, 1, 0, 1}}, 
    5.     {{-1, -1, 0}, {0, 1, 0, 1}}, 
    6.     {{1, -1, -1}, {1, 0, 0, 1}}, 
    7.     {{1, 1, -1}, {1, 0, 0, 1}}, 
    8.     {{-1, 1, -1}, {0, 1, 0, 1}}, 
    9.     {{-1, -1, -1}, {0, 1, 0, 1}} 
    10. }; 
    11.   
    12. const GLubyte Indices[] = { 
    13.     // Front 
    14. 0, 1, 2, 
    15.     2, 3, 0, 
    16.     // Back 
    17. 4, 6, 5, 
    18.     4, 7, 6, 
    19.     // Left 
    20. 2, 7, 3, 
    21.     7, 6, 2, 
    22.     // Right 
    23. 0, 4, 1, 
    24.     4, 1, 5, 
    25.     // Top 
    26. 6, 2, 1,  
    27.     1, 6, 5, 
    28.     // Bottom 
    29. 0, 3, 7, 
    30.     0, 7, 4     
    31. }; 

    编译运行,你会看到一个方块了。

    但这个方块有时候让人觉得假,因为你可以看到方块里面。

    这里还有一个叫做 depth testing(深度测试)的功能,启动它,OpenGL就可以跟踪在z轴上的像素。这样它只会在那个像素前方没有东西时,才会绘画这个像素。

    到OpenGLView.h中,添加成员变量。

    1. GLuint _depthRenderBuffer;

    在OpenGLView.m:

    1. // Add new method right after setupRenderBuffer 
    2. - (void)setupDepthBuffer { 
    3.     glGenRenderbuffers(1, &_depthRenderBuffer); 
    4.     glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderBuffer); 
    5.     glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, self.frame.size.width, self.frame.size.height);     
    6.   
    7. // Add to end of setupFrameBuffer 
    8. glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthRenderBuffer); 
    9.   
    10. // In the render method, replace the call to glClear with the following 
    11. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
    12. glEnable(GL_DEPTH_TEST); 
    13.   
    14. // Add to initWithFrame, right before call to setupRenderBuffer 
    15. [self setupDepthBuffer]; 

    ·setupDepthBuffer方法创建了一个depth buffer。这个与前面的render/color buffer类似,不再重复了。值得注意的是,这里使用了glRenderbufferStorage, 然不是context的renderBufferStorage(这个是在OpenGL的view中特别为color render buffer而设的)。

    ·接着,我们调用glFramebufferRenderbuffer,来关联depth buffer和render buffer。还记得,我说过frame buffer中储存着很多种不同的buffer?这正是一个新的buffer。

    ·在render方法中,我们在每次update时都清除深度buffer,并启用depth  testing。

    编译运行,看看这个教程最后的效果。

    一个选择的立方块,用到了OpenGL ES2.0。

    本教程源码地址下载:https://github.com/wanglixin1999/HelloGL

    转载自:http://blog.csdn.net/wanglixin1999/article/details/51792505

    展开全文
  • 标签: 教程 iOS OpenGL ES tutorial 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://jimmyzhouj.blog.51cto.com/2317513/884763

    http://jimmyzhouj.blog.51cto.com/2317513/884763



    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://jimmyzhouj.blog.51cto.com/2317513/884763

     

     

    iOS OpenGL ES2.0教程    Lesson03  旋转

     

    注:可供翻译的课程只有前两课。从这节课起,我试着根据我对OpenGL ES的理解写接下去的课程,希望能和大家一起学习。

     

     

    在上节课中,我们绘制了一个三角形。在这一课里,我们学习如何旋转。我们创建一个正方形,并让它沿Y轴旋转。

     

     

     

    绘制一个长方形

     

    首先,我们要在屏幕上绘制一个长方形,如下图所示: 

     

     

    要注意的是,在用OpenGL ES绘制图形的时候,是不能直接绘制矩形或其他多边形的,只能绘制三角形。所以,我们这里需要将通过绘制两个三角形来得到一个长方形。在图中分别绘制由顶点(0, 1, 2)和顶点(2, 3, 0)构成的三角形。

     

    如果我们用上节课所使用的方法,每个三角形需要指定3个顶点的信息,那么两个三角形则需要指定6个顶点。这6个顶点中有重复的数据,实际上只需要指定4个不同的顶点就可以了。我们可以用新的方法来实现这个功能。在文件Lesson03.mm中:

     

     

    1. //--------------------------------------------------- 
    2. //create a rectangle 
    3. std::vector<float> geometryData; 
    4.  
    5. //vertex 0, left/buttom 
    6. geometryData.push_back(-0.5); geometryData.push_back(-0.5); geometryData.push_back(0.0); geometryData.push_back(1.0); 
    7.  
    8. // 1, right/buttom 
    9. geometryData.push_back(0.5); geometryData.push_back(-0.5); geometryData.push_back(0.0); geometryData.push_back(1.0);   
    10.  
    11. // 2, right/ups 
    12. geometryData.push_back(0.5); geometryData.push_back(0.5); geometryData.push_back(0.0); geometryData.push_back(1.0); 
    13.  
    14. // 3, left/up 
    15. geometryData.push_back(-0.5); geometryData.push_back(0.5); geometryData.push_back(0.0); geometryData.push_back(1.0); 
    16.  
    17. //generate an ID for our geometry buffer in the video memory and make it the active one 
    18. glGenBuffers(1, &m_geometryBuffer); 
    19. glBindBuffer(GL_ARRAY_BUFFER, m_geometryBuffer); 
    20.  
    21. //send the data to the video memory 
    22. glBufferData(GL_ARRAY_BUFFER, geometryData.size() * sizeof(float), &geometryData[0], GL_STATIC_DRAW); 

     

     

    指定4个顶点的数据,顶点按照添加的顺序,其索引值分别为0, 1, 2, 3。然后将顶点数据传送给OpenGL,和上一课用的方法一样。屏幕显示范围是从(-1, 1),中心点位置是(0, 0),所以 (0.5,0.5)指的是右上1/4屏幕的中间,其他点以此类推。

     

     

     

    1. //create a color buffer, to make our triangle look pretty 
    2. std::vector<float> colorData; 
    3.  
    4. //3 floats define one color value (red, green and blue) with 0 no intensity and 1 full intensity 
    5. //each color triplet is assigned to the vertex at the same position in the buffer, so first color -> first vertex 
    6.  
    7. //vertex 0 is red 
    8. colorData.push_back(1.0); colorData.push_back(0.0); colorData.push_back(0.0);     
    9. // 1 is blue 
    10. colorData.push_back(0.0); colorData.push_back(0.0); colorData.push_back(1.0);     
    11. // 2 is green 
    12. colorData.push_back(0.0); colorData.push_back(1.0); colorData.push_back(0.0); 
    13. // 3 is blue 
    14. colorData.push_back(0.0); colorData.push_back(0.0); colorData.push_back(1.0); 
    15.  
    16. //generate an ID for the color buffer in the video memory and make it the active one 
    17. glGenBuffers(1, &m_colorBuffer); 
    18. glBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer); 
    19. //send the data to the video memory 
    20. glBufferData(GL_ARRAY_BUFFER, colorData.size() * sizeof(float), &colorData[0], GL_STATIC_DRAW); 

     

    指定4个顶点的颜色,并将数据传递到VBO中。

     

     

     

     

    1. //create vertex indices 
    2. std::vector<GLubyte> indexData; 
    3.  
    4. //3 byte define 1 triangle 
    5. indexData.push_back(0); indexData.push_back(1); indexData.push_back(2); 
    6. indexData.push_back(2); indexData.push_back(3); indexData.push_back(0);     
    7.  
    8. m_indexNumber = indexData.size(); 
    9.  
    10. //generate an ID for the index buffer in video memory 
    11. glGenBuffers(1, &m_indexBuffer); 
    12. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer); 
    13. //send data to video memory 
    14. glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexData.size() * sizeof(GLubyte), &indexData[0], GL_STATIC_DRAW); 

     

    要注意这里新添加的内容。新创建一个数组indexData, 保存的是组成每个三角形的顶点的索引。比如第一个三角形由顶点0,1,2组成,第二个三角形由

    顶点2,3,1组成,注意顶点的指定顺序要按照逆时针次序。定义了一个变量m_indexNumber,用来保存绘制图形一共需要使用的元素的数目。

    然后在图像内存中生产一个缓冲区,绑定这个缓冲区。这里用的是GL_ELEMENT_ARRAY_BUFFER,表示之后用glDrawELements绘制图元的时候,会到这个缓冲区来

    获得顶点的索引值。

     

     

     

    1. //initiate the drawing process, we want a triangle, start at index 0 and draw 3 vertices 
    2.   glDrawArrays(GL_TRIANGLES, 0, 3); 
    3.  
    4. //initiate the drawing process 
    5. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer); 
    6. glDrawElements(GL_TRIANGLES, m_indexNumber, GL_UNSIGNED_BYTE, 0); 

     

    draw()方法中,我们不再使用前一课用的glDrawArray()方法,而是调用glDrawElement()方法,从索引数据来绘制图元。

    函数需要四个参数:第一个参数是要绘制的图元,我们这里画的是三角形,选择GL_TRIANGLES。第二个参数是要绘制的元素的数目,即是索引数组的元素数目,

    这里我们用m_indexNumber。第三个参数是索引数组中元素的类型。最后一个参数是指向索引数组的指针。因为我们之前已经把索引数组传送到OpenGL的

    GL_ELEMENT_ARRAY_BUFFER缓冲区里去了,所以这里我们直接用0。

     

    编译运行程序,长方形就显示出来了。

     

     

     

     

    旋转

     

    下面,我们要让这个长方形沿X轴正方向移动0.5,然后沿Y轴旋转。

     

    首先,看shader.vert文件:

     

     

    1. // modelview 
    2. uniform mat4 modelview; 
    3.  
    4.  
    5. //the shader entry point is the main method 
    6. void main() 
    7. {     
    8.     colorVarying = color; //save the color for the fragment shader 
    9.  
    10.     gl_Position = modelview * position; //copy the position     

     

    我们增加了一个变量modelview,是一个4*4的矩阵。限定符uniform表示这个变量是从应用程序传递给着色器的,并且在绘制图元时保持为常量。

    现在gl_Position的值是变换矩阵modelview乘给定的顶点位置。

     

    注:关于这部分使用到的向量和矩阵库,来源于raywenderrlich写的OpenGL ES2.0 iphone开发指引,最初来源于Cocos3d框架。( http://www.raywenderlich.com/3664/opengl-es-2-0-for-iphone-tutorial)

     

     

    在Lesson03.h中,增加一个变量m_modelview,保存着色器程序中uniform变量modelview的位置。

     

    1. //modelview uniform in the shader 
    2. int m_modelview; 

     

     

     

    1. Lesson03.mm: 
    2.     //get the attachment points for modelview 
    3.     m_modelview = glGetUniformLocation(m_shader->getProgram(), "modelview"); 
    4.     //check 
    5.     if (m_modelview < 0) { 
    6.         NSLog(@"Could not query uniform location"); 
    7.     } 

     

    调用glGetUniformLoaction函数,将着色器程序和变量的名字传给函数,函数会返回该变量的位置。这个值我们保存起来,之后要用到。

     

     

    再来看draw()函数,增加了如下代码:

     

     

    1. //modelview  
    2.  
    3. CC3GLMatrix *mvMatrix = [CC3GLMatrix matrix]; 
    4. [mvMatrix populateFromTranslation:CC3VectorMake(0.2, 0 , 0)]; 
    5.  
    6. m_currentRotation += 1.0; 
    7. [mvMatrix rotateBy:CC3VectorMake(0, m_currentRotation, 0)]; 
    8.  
    9. glUniformMatrix4fv(m_modelview, 1, 0, mvMatrix.glMatrix); 

     

    首先,创建一个CC3GLMatrix的实例对象。关于CC3GLMatrix类,我也不是太熟悉,现在我们只要知道它有一个property成员 glMatrix,glMatrix表示的是一个4*4的浮点数矩阵数组。

    然后,populateFromTranslation函数给glMatrix矩阵提供位置移动的数据,参数为指定的向量vector(x, y, z),表示在x, y, z轴上移动的距离。这样得到的矩阵glMatrix就可以通过矩阵乘法移动指定顶点的位置了。在这里,表示向x轴正方向移动0.5,其他两个方向不动。

    rotateBy函数提供在x, y, z轴上的旋转数据。我们设定的值是,只沿y轴旋转,旋转角度为m_currentRotation,每次调用draw()函数,旋转角度增加1度。

        最后glUniformMatrix4fv函数用我们得到的变换矩阵来更新着色器程序里的uniform变量矩阵modelview。第一个参数是要修改的uniform变量的索引位置,第二个参数是要修改的矩阵数据,这里是1个。第三个参数是第四个参数是按行主序还是列主序指定的,这里我们用GL_FALSE(0),表明是列主序指定的。最后一个参数是我们前面设定的变换矩阵。

     

     

    现在,编译并运行程序,我们就可以在屏幕上看到程序运行的结果了。长方形沿x轴正方向移动了0.2,并且沿Y轴旋转。附件是教程的工程文件。

     

    还有一个问题,旋转角度在180-360度之间时,看不到我们的长方形了。那是因为在那个范围里时,按照我们顶点的指定顺序,这两个三角形的顶点都是按顺时针方向指定的,OpenGL认为这个面是背向我们的,就没有让它显示了。下一课,我们把它改造成3D的。

     


    展开全文
  • ios openGL 基础知识

    2019-03-23 17:22:43
    ios的设备比如iphone、ipod touch以及ipad设备支持的是openGL ES2.0. 这里只简单介绍最基础的有关openGL的一些东西。 一. 3D渲染 图形处理单元GPU就是能够结合几何、颜色、灯光和其他数据而产生一个屏幕图像的...
  • OpenGL ES 是可以在iphone上实现2D和3D图形编程的低级API。 如果你之前接触过 cocos2d,sparrow,corona,unity 这些框架,你会发现其实它们都是基于OpenGL上创建的。 多数程序员选择使用这些框架,而不是直接调用...
  • IOS OpenGL 学习 (一)

    2019-07-13 15:41:14
    IOS OpenGL 学习 (一)OpenGL 简介OpenGL 基础理论知识1. 坐标系与变换2. OpenGL 显示图形流程 OpenGL 简介 什么是OpenGL 1、OpenGL是一种应用程序编程接口,它是一种可以对图形硬件设备特性进行访问的软件库。 ...
  • 由于MystEngine是跨平台的,支持Windows Mac & IOS,因此选择OpenGL做为Graphics Api ,为了简化渲染流程,因此摒弃了Fix pipeline。所以最后移动平台是用ES2.0,而PC&Mac 上使用OpenGL2.0以上版本。 问题是...
  • iOS OpenGL渲染YUV数据

    2017-09-15 10:48:30
    本文主要介绍使用OpenGL ES来渲染I420(YUV420P) , NV12(YUV420SP)的方法,关于YUV的知识,可以看这里《YUV颜色编码解析》,同样会用到一些简单的OpenGL shader知识,可以看看OpenGL的着色器语言。为了书写方便,以下...
  • iOS OpenGL ES入门详解

    2017-08-19 19:20:38
    //联系人:石虎  QQ: 1224614774昵称:嗡嘛呢叭咪哄 OpenGL ES入门详解  转入地址:... 1.决定你要支持的OpenGL ES的版本。目前,OpenGL ES包含1.1和2.0两个版本,iPhone 3G
  • iOS OpenGL 加载3D模型

    2017-09-14 10:26:56
    https://www.raywenderlich.com/48293/how-to-export-blender-models-to-opengl-es-part-1 https://www.raywenderlich.com/50398/opengl-es-transformations-gestures Recently, we published Blender ...
  • 环境:IOS 依赖库:GLKit 目标:借助openGL离屏渲染在不影响前端展示的情况下渲染出所需要的模型并截图保存(因为前端展示的和需要的图片背景、角度等参数不同) //print screen when model rotates to angle ...
  • - 掌握OpenGL ES 的详细工作流程 - 可绘制多种图形 - 随意更改绘制图形的颜色以及底色 - 正式入门OpenGL ES 效果如图:原文 涉及到的英语: 我们需要掌握一些简单地英文,方便我们记忆,我整理
  • Nehe iOS OpenGL ES 2.0教程

    2014-06-30 23:29:22
    [ jimmyzhouj 翻译] Nehe iOS OpenGL ES 2.0教程 引子:  最近要学习iOS 上的OpenGL ES的内容,在互联网上找了一些教程来看。发现关于OpenGL ES2.0的教程不多。想起了知名的Nehe OpenGL 教程,就上nehe....
  • iOS OpenGL ES Guide

    2018-01-26 16:56:48
    OpenGL ES 小结 概述 OpenGL ES (Open Graphics Library for Embedded Systems)是访问类似 iPhone 和 iPad 的现代嵌入式系统的 2D 和 3D 图形加速硬件的标准。 把程序提供的几何数据转换为屏幕上的图像的...
  • opengl es (OpenGL for Embedded Systems)是OpenGL针对嵌入式系统的图形API. Android, IOS, 以及PC, 都支持这个规范. opengl es 2.0 API文档 https://www.khronos.org/registry/OpenGL-Refpages/es2.0/ opengl es...
1 2 3 4 5 ... 20
收藏数 7,072
精华内容 2,828
热门标签
关键字:

2.0 ios opengl