iOS开发-OpenGL ES入门教程2

作者: 落影loyinglin | 来源:发表于2016-03-28 16:56 被阅读11029次

教程

OpenGLES入门教程1-Tutorial01-GLKit
这次的是shader编译链接glsl入门简单图形变换

OpenGL ES系列教程在这里
OpenGL ES系列教程的代码地址 - 你的star和fork是我的源动力,你的意见能让我走得更远。

效果展示

核心思路

不采用GLKBaseEffect,编译链接自定义的着色器(shader),用简单的glsl语言来实现顶点和片元着色器,并对图片用简单的图形变换。

具体细节

1、shader编译

  • c语言编译流程:预编译、编译、汇编、链接
  • glsl的编译过程类似c语言,主要有glCompileShader、glAttachShader、glLinkProgram三步;
- (GLuint)loadShaders:(NSString *)vert frag:(NSString *)frag {
    GLuint verShader, fragShader;
    GLint program = glCreateProgram();
    
    //编译
    [self compileShader:&verShader type:GL_VERTEX_SHADER file:vert];
    [self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:frag];
    
    glAttachShader(program, verShader);
    glAttachShader(program, fragShader);
        
    //释放不需要的shader
    glDeleteShader(verShader);
    glDeleteShader(fragShader);
    
    return program;
}

- (void)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file {
    //读取字符串
    NSString* content = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
    const GLchar* source = (GLchar *)[content UTF8String];
    
    *shader = glCreateShader(type);
    glShaderSource(*shader, 1, &source, NULL);
    glCompileShader(*shader);
}

2、glsl入门

glsl是OpenGL的着色器语言,有c基础可以很快上手,注意以下几点:

  • 着色器有顶点着色器和片元着色器两种;参考下图,顶点着色器在第一个,片元着色器在最后一个;注意,在顶点着色器中处理顶点,片元着色器处理像素点颜色,那么对于一条线段,顶点着色器只会处理俩个顶点的坐标、颜色等信息,线段上的点会由插值生成。
  • 如下,是一个顶点着色器。出现了attribute、uniform、varying这类修饰符,遇到这些可以看 这里 ,有详细的概念介绍。
    需要注意的是,glsl是严格的类型匹配,int和float进行运算会出错。
    顶点着色器的目标是输出顶点,所以gl_Position必须赋值
attribute vec4 position;
attribute vec2 textCoordinate;
uniform mat4 rotateMatrix;
varying lowp vec2 varyTextCoord;
void main()
{
    varyTextCoord = textCoordinate;
    
    vec4 vPos = position;

    vPos = vPos * rotateMatrix;

    gl_Position = vPos;
}
  • 如下,这是一个片元着色器。注意,在片元着色器,数字变量都要有类似lowp的精度描述。
    片元着色器的目标是输出像素颜色,gl_FragColor必须赋值
varying lowp vec2 varyTextCoord;
uniform sampler2D colorMap;
void main()
{
    gl_FragColor = texture2D(colorMap, varyTextCoord);
}

这里有一个详细的博客,讲得很好。

3、简单图形变换

几何变换有比例、旋转、平移、对称、错切,这里我们介绍简单的旋转变换。
先给出结论:对于一个图形进行旋转变换,相当于对每个顶点乘以一个旋转变换矩阵。矩阵如下:


旋转矩阵.png

对于顶点的变换,我们可以放在OC代码里面来实现,把顶点变换完成后,把顶点输入到OpenGLES;也可以在glsl代码实现,把顶点变换交给gpu来完成。这里我们采用的是后者。
如下:

    GLuint rotate = glGetUniformLocation(self.myProgram, "rotateMatrix");
    
    float radians = 10 * 3.14159f / 180.0f;
    float s = sin(radians);
    float c = cos(radians);
    
    //z轴旋转矩阵
    GLfloat zRotation[16] = { //
        c, -s, 0, 0.2, //
        s, c, 0, 0,//
        0, 0, 1.0, 0,//
        0.0, 0, 0, 1.0//
    };
    
    //设置旋转矩阵
    glUniformMatrix4fv(rotate, 1, GL_FALSE, (GLfloat *)&zRotation[0]);
    

细心的开发者会发现,这里的z轴旋转矩阵和上面给出来的旋转矩阵并不一致。
究其原因就是OpenGLES是列主序矩阵,对于一个一维数组表示的二维矩阵,会先填满每一列(a[0][0]、a[1][0]、a[2][0]、a[3][0])。
把矩阵赋值给glsl对应的变量,然后就可以在glsl里面计算出旋转后的矩阵。

思考题

  • 1、为什么熊猫的反的?要如何解决?
  • 2、在这个样例中,顶点着色器调用次数和片元着色器调用次数哪个多?
  • 3、glsl里面的变量可以通过glUniform进行赋值,那么是否可以在编译成功后或者链接成功后直接进行赋值?

总结

这一篇的内容作为教程2难度有点大,特别是shader和glsl语言容易让人兴趣直接降到谷底,如果觉得难,可以暂时不用管glsl语言。
待熟悉GLKBaseEffect后,再回来学习glsl也不迟。
代码点我

思考题答案

1、纹理坐标系的(0, 0)在左下角;

2、片元着色器。顶点着色器调用次数与顶点数量有关,片元着色器调用与像素多少有关系。

3、一个一致变量在一个图元的绘制过程中是不会改变的,所以其值不能在glBegin/glEnd中设置。一致变量适合描述在一个图元中、一帧中甚至一个场景中都不变的值。一致变量在顶点shader和片断shader中都是只读的。首先你需要获得变量在内存中的位置,这个信息只有在连接程序之后才可获得。

相关文章

网友评论

  • 7136a1c01300:是否可以这样理解:通过 self.myView = (LearnView *)self.view将LearnView联接到LYGLViewController上面?
    我看教程1上面的代码:
    GLKView* view = (GLKView *)self.view;
    self.mContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
    view.context = self.mContext;
    这是否意味着此时ViewController使用的是默认的view呢?
  • 小沛2016:大神 我把你这个方法注释了 但还是可以运行 不知道这个方法有什么用呢

    [self destoryRenderAndFrameBuffer];
  • 海上飞鸟:您好,我想问下怎么样吧渲染出来的效果转换为CVPixelBufferRef,来进行视频写入操作
    落影loyinglin:@海上飞鸟 文集里的帧缓存那篇可以看看,再看看GPUImage 有示例代码
    海上飞鸟:@落影loyinglin 您有这方面的资料或则demo,可以让我学习下吗?
    落影loyinglin:@海上飞鸟 问的很好,这个需要用到帧缓存的渲染到纹理 加上corevideo的一个api
  • FongG:感谢作者的无私分享!

    望作者能解疑:纹理初始化后是如何和着色器关联起来的?

    纹理初始化、绑定默认是在GL_TEXTURE0单元中进行的,本来我以为是在可以通过一致变量
    glActiveTexture(GL_TEXTURE0); //切换成 GL_TEXTURE0
    glUniform1i(rotate, GL_TEXTURE2) //切换成 GL_TEXTURE2
    来控制着色器操作哪一个纹理单元,理想状态为不显示图像,但发现依然会显示。
    (但glActiveTexture设置为GL_TEXTURE1就不会显示了,这也是个问题....)

    拜托作者解疑,感谢
    落影loyinglin:@Jin_先生 gluniform的方法指定
    FongG:@落影loyinglin 那具体纹理是如何和着色器关联起来的呢
    落影loyinglin:@Jin_先生 glActiveTexture是改变当前纹理单元。每次只能操作当前的纹理单元。
  • littleDad:第二篇 有点复杂 ,乍一看很懵逼
    落影loyinglin:@Little_Dad glsl的编译是一大块
  • 月咏蝴蝶:你好,我想询问一下,屏蔽了旋转部分的代码,为什么图片都显示不出来?
    e2ed4f925712://z轴旋转矩阵
    GLfloat zRotation[16] = { //
    c, -s, 0, 0.2, //
    s, c, 0, 0,//
    0, 0, 1.0, 0,//
    0.0, 0, 0, 1.0//
    };
    为啥围绕z轴是这样写的
    月咏蝴蝶:@落影loyinglin :pray: 感谢!您的教程+推荐学习的learnOpenGL对我帮助非常大
    落影loyinglin:@月咏蝴蝶 跟坐标有关系,原始
    坐标不在gl默认的摄像机范围内
  • Y了个J:大神可以出点视频
  • Zszen:非常给力
  • Even3Yu:思考题1:
    GLfloat attrArr[] =
    {

    0.5f, -0.5f, 0.0f, 1.0f, 1.0f, //右下
    -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, // 左上
    -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, // 左下
    0.5f, 0.5f, 0.0f, 1.0f, 0.0f, // 右上
    -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, // 左上
    0.5f, -0.5f, 0.0f, 1.0f, 1.0f, // 右下
    };
  • e9137d13dc3a:作者,有什么方法可以看shader里面数据是怎样?运行调试查看?
  • 4b3bb5eef806:很好奇 glBindRenderbuffer 和 glBindBuffer 如果都指定的是当前数据是GL_RENDERBUFFER的话,这两个有什么区别吗。
  • 迷路的字母C:glsl程序没有代码提示吗?写着好费劲啊
  • FGNeverMore:你好,按照您的代码敲了一遍,顶点纹理数组修改了,但是出来的图片是带镜像效果的,改怎么修改
    FGNeverMore:已经尝试出来了~
  • 0x7472616d70:哥哥,搞不懂z轴的指向啊,熊猫是翻的,转不过来弯,请指教:sweat:
  • HHHHHHHHHHD:教程一有点难看懂的。里面涉及很多。还是要去看其他博客才可以知道。还有那个中心点的计算,,感觉好像有点问题。
  • d13b9556032b: float x = 1.0;
    float y = 1.0;
    float z = 0;
    //前三个是顶点坐标, 后面两个是纹理坐标
    GLfloat attrArr[] =
    {
    x, -y, z, 1.0f, 0.0f,
    -x, y, z, 0.0f, 1.0f,
    -x, -y, z, 0.0f, 0.0f,
    x, y, z, 1.0f, 1.0f,
    -x, y, z, 0.0f, 1.0f,
    x, -y, z, 1.0f, 0.0f,
    };


    GLKMatrix4 mvpMatrix;
    GLKMatrix4 glscale = GLKMatrix4MakeScale(0.2, 0.2, 1);
    GLKMatrix4 rotationZ = GLKMatrix4MakeRotation(GLKMathDegreesToRadians(180), 0, 0, 1);
    GLKMatrix4 rotationY = GLKMatrix4MakeRotation(GLKMathDegreesToRadians(180), 0, 1, 0);
    GLKMatrix4 translation = GLKMatrix4MakeTranslation(0.5f, 0.5f, 0);

    mvpMatrix = GLKMatrix4Multiply(rotationZ,glscale);
    mvpMatrix = GLKMatrix4Multiply(rotationY,mvpMatrix);
    mvpMatrix = GLKMatrix4Multiply(translation,mvpMatrix);

    glUniformMatrix4fv(mvpMatrixPosition, 1, GL_FALSE, mvpMatrix.m);


    attribute vec4 position;
    attribute vec2 textCoordinate;
    uniform mat4 MVPMatrix;

    varying lowp vec2 varyTextCoord;

    void main()
    {
    varyTextCoord = textCoordinate;

    vec4 vPos = position;

    vPos = MVPMatrix * vPos ;

    gl_Position = vPos;
    }

    把你的程序修改了,
  • d13b9556032b:楼主你好,顶点shader里面 那个矩阵为什么是右乘呢,一般不都是左乘吗
  • Somnus_chh:虽不明但觉厉,第一篇文章熊猫对称,没弄出来,看了这篇竟然把对称做出来了,。。。 :stuck_out_tongue_winking_eye:
    Pusswzy:@Goning +1
    Goning:@FH_iOS 纹理坐标左右互换即可
    FH_iOS:能提醒一下怎么做对称效果吗?
  • GSD_iOS:很不错! :+1: :+1:
  • 乐视薯片:楼主你好,最近在学习GPUImage,但是好多概念都不懂,需要从OpenGL 开始学吗?
    Pusswzy:@落影loyinglin 我觉得需要的
    落影loyinglin:@初心_媛 不需要
  • 899e2d74bdeb:请问问题1的答案是不是在加载纹理的时候(0,0)坐标在右上角,而纹理坐标的(0,0)是在左下角?导致图片颠倒?
    落影loyinglin:@哎疯 gl的坐标系 和 ios的坐标系不同导致
  • 小山Sam:@落影loyinglin 我加了一个滤镜效果,https://github.com/chencan/LearnOpenGLES/commit/9ba16ebcaab4de63b9590fe63d6da7cb504d6267 但glsl编译不过,能帮忙看看吗
    小山Sam:@落影loyinglin 是说精度有问题吗?
    落影loyinglin:@小山Sam 精度没问题
    落影loyinglin:@小山Sam 像素着色器
  • 小山Sam:“0.5f, -0.5f, -1.0f, 1.0f, 0.0f,”
    为啥第三个数字是-1.0f?改成0效果一样,改成-1.1就看不到熊猫了
    leezher:改成-1.1,坐标就越界了,就看不到了,opengl坐标系范围(-1.0-1.0)
  • 张霸天: 0.5f, -0.5f, -1.0f, 1.0f, 0.0f,
    -0.5f, 0.5f, -1.0f, 0.0f, 1.0f,
    -0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
    0.5f, 0.5f, -1.0f, 1.0f, 1.0f,
    -0.5f, 0.5f, -1.0f, 0.0f, 1.0f,
    0.5f, -0.5f, -1.0f, 1.0f, 0.0f,
    我觉得你这个应该要注释下,因为新手很难理解为什么坐标需要这么写,有重复的地方,为了节省index
    落影loyinglin:@zhangdazuiba 好建议
  • 此晨:自定义的 看不懂 例如 attribute vec4 position;
    attribute vec2 textCoordinate;
    uniform mat4 rotateMatrix;
    varying lowp vec2 varyTextCoord;
    void main()
    {
    varyTextCoord = textCoordinate;

    vec4 vPos = position;

    vPos = vPos * rotateMatrix;

    gl_Position = vPos;
    }


    函数里只用到了position rotateMatrix 为什么还要vec4 vPos = position varyTextCoord = textCoordinate不理解 为什么 二维等于四维
    leezher:vec4 vPos = position (四维=四维)
    varyTextCoord = textCoordinate(二维=二维)
    没有二维等于思维吧?!
    小山Sam:@Captain_FL 一行四列的矩阵,乘以四行四列的矩阵,结果就是一行四列的矩阵
  • 291824c9920c:我也知道纹理坐标原点是(0, 0)。。但是熊猫是反的,应该如何解决啊??
    291824c9920c:@落影loyinglin 翻转一下确实正了,但是在教程1和教程2中设置纹理坐标的方式是一样的,为什么教程1中的图片是正的呢?是因为系统的GLKBaseEffect对纹理坐标进行了翻转吗?
    落影loyinglin:@YapheeetS 考虑把纹理坐标翻转下
    291824c9920c:刚开始学,还望多多请教啊
  • 645ff0691817:請問為什麼
    https://github.com/loyinglin/LearnOpenGLES/blob/master/Tutorial02-shader入门/LearnOpenGLES/LearnView.m#L215

    這裡產出是self.myColorFrameBuffer,但是在Bind時卻是self.myColorRenderBuffer?
    而且似乎不管改成哪個對於結果都沒影響?

    謝謝。
    落影loyinglin:@junghao 你好,是笔误。正确的写法应该是glBindFramebuffer(GL_FRAMEBUFFER, self.myColorFrameBuffer);
    感谢指正。
    645ff0691817:@落影loyinglin 我是說
    self.myColorFrameBuffer = buffer;
    // 设置为当前 framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, self.myColorRenderBuffer);

    第一行指派給myColor-FRAME-Buffer
    但是下一行bind的時候,bind的是 myColor-RENDER-buffer
    整個.m的其他地方也沒有用到myColor-FRAME-Buffer
    所以是否筆誤了?
    落影loyinglin:@junghao 用不太严谨的描述framebuffer=colorBuffer+depthBuffer+STENCILBuffer
  • 陈阿票:太难了,看晕了
  • 魁拔2015:你好 那个熊猫的图标相反的原因是纹理坐标系的原因 那如果解决呢?我没有找到解决方案 希望指点一下。
    落影loyinglin:@魁拔2015 如果不用GLKit,可以在设置纹理坐标的时候,上下的坐标颠倒即可。也可以用正常的坐标,但是要修改变化矩阵。
    魁拔2015:@落影loyinglin 那如果不用GLKit呢 是否能够解决?
    落影loyinglin:@魁拔2015 用GLKit的话,读取的时候可以设置对应的参数即可,在后面教程有写。
  • 落影loyinglin:一个一致变量在一个图元的绘制过程中是不会改变的,所以其值不能在glBegin/glEnd中设置。一致变量适合描述在一个图元中、一帧中甚至一个场景中都不变的值。一致变量在顶点shader和片断shader中都是只读的。
    首先你需要获得变量在内存中的位置,这个信息只有在连接程序之后才可获得。
  • 陈_某_某:1.熊猫是反的,因为纹理坐标系(0,0)在左下角。。
    3.应该不可以吧,glsl跟c语言差不多。不是面向对象??
  • 陈_某_某:感觉有点难,教程1看了半天,感觉不理解,后来结合其它博客才勉强理解了。看了这个demo完全傻掉了。。。
    落影loyinglin:@_iOSer 我尽量简化多余的代码以及添加注释。光看代码不够,教程1后面附带的链接有更详细的解析。

本文标题:iOS开发-OpenGL ES入门教程2

本文链接:https://www.haomeiwen.com/subject/thhdlttx.html