美文网首页OpenGL+MetalOpenGLOpenGL
OpenGL ES入门02-OpenGL ES着色器

OpenGL ES入门02-OpenGL ES着色器

作者: 秦明Qinmin | 来源:发表于2017-02-10 20:32 被阅读1549次

    前言

    本文是关于OpenGL ES的系统性学习过程,记录了自己在学习OpenGL ES时的收获。
    这篇文章的目标是学习OpenGL ES着色器语言。
    环境是Xcode8.1+OpenGL ES 2.0
    目前代码已经放到github上面,OpenGL ES入门02-OpenGL ES着色器

    欢迎关注我的 OpenGL ES入门专题

    概述

    着色器(Shader)是运行在GPU上的小程序。这些小程序为图形渲染管线的某个特定部分而运行。从基本意义上来说,着色器只是一种把输入转化为输出的程序。着色器也是一种非常独立的程序,因为它们之间不能相互通信;它们之间唯一的沟通只有通过输入和输出。

    效果

    三角形绘制

    着色器语言

    着色器是使用一种叫GLSL的类C语言写成的。GLSL是为图形计算量身定制的,它包含一些针对向量和矩阵操作的有用特性。

    着色器分类

    顶点着色器 是一个可编程的处理单元,执行顶点变换、纹理坐标变换、光照、材质等顶点的相关操作,每顶点执行一次。替代了传统渲染管线中顶点变换、光照以及纹理坐标的处理。

    attribute vec3 position;
    attribute vec3 color;
    varying vec3 outColor;
    
    void main()
    {
        gl_Position = vec4(position, 1.0);
        outColor = color;
    }
    

    片元着色器 是一个处理片元值及其相关联数据的可编程单元,片元着色器可执行纹理的访问、颜色的汇总、雾化等操作,每片元执行一次。

    precision mediump float;
    varying vec3 outColor;
    void main()
    {
        gl_FragColor = vec4(outColor, 1.0);
    }
    
    

    数据类型

    类型 说明
    float 浮点型
    bool 布尔型
    int 整形
    vec2 包含了2个浮点数的向量
    vec3 包含了3个浮点数的向量
    vec4 包含了4个浮点数的向量
    ivec2 包含了2个整数的向量
    ivec3 包含了3个整数的向量
    ivec4 包含了4个整数的向量
    bvec2 包含了2个布尔数的向量
    bvec3 包含了3个布尔数的向量
    bvec4 包含了4个布尔数的向量
    mat2 2*2维矩阵
    mat3 3*3维矩阵
    mat4 4*4维矩阵
    sampler1D 1D纹理采样器
    sampler2D 2D纹理采样器
    sampler3D 3D纹理采样器
    samplerCube Cube纹理采样器

    常量

    const 可以用来修饰任何基本数据类型。通常const变量在声明的同时要进行初始化,结构体字段不能使用const修饰吗,但是变量可以,并通过构造器进行初始化。包含数组的数组和结构体不能声明为常量,因为它们不能被初始化。

    onst vec4 color = vec4 (1.0, 1.0, 1.0, 1.0);
    

    存储修饰符

    attribute 变量(属性变量)只能用于顶点着色器中,不能用于片元着色器。 一般用该变量来表示一些顶点数据,如:顶点坐标、纹理坐标、颜色等。

    uniforms 是一种从CPU中的应用向GPU中的着色器发送数据的方式,但uniform和顶点属性有些不同。首先,uniform是全局的(Global)。全局意味着uniform变量必须在每个着色器程序对象中都是独一无二的,而且它可以被着色器程序的任意着色器在任意阶段访问。第二,无论你把uniform值设置成什么,uniform会一直保存它们的数据,直到它们被重置或更新。

    samplers 一种特殊的 uniform,用于呈现纹理。sampler 可用于顶点着色器和片元着色器。

    varying 变量(易变变量)是从顶点着色器传递到片元着色器的数据变量。顶点着色器可以使用易变变量来传递需要插值的颜色、法向量、纹理坐标等任意值。 在顶点与片元shader程序间传递数据是很容易的,一般在顶点shader中修改varying变量值,然后片元shader中使用该值,当然,该变量在顶点及片元这两段shader程序中声明必须是一致的 。例如:上面代码中应用程序中由顶点着色器传入片元着色器中的vColor变量。

    精度修饰符

    精度修饰符

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

    precision highp float;
    precision highp int;
    precision lowp sampler2D;
    precision lowp samplerCube;
    

    内建变量

    gl_Position 顶点着色器内建变量,表示变换后点的空间位置。 顶点着色器从应用程序中获得原始的顶点位置数据,这些原始的顶点数据在顶点着色器中经过平移、旋转、缩放等数学变换后,生成新的顶点位置。新的顶点位置通过在顶点着色器中写入gl_Position传递到渲染管线的后继阶段继续处理。

    *** gl_PointSize*** 顶点着色器内置变量,设置栅格化点的直径,也就是点的大小,通常用于点精灵,粒子等绘制。

    gl_FragColor 片元着色器内置变量,用来保存片元着色器计算完成的片元颜色值,此颜色值将送入渲染管线的后继阶段进行处理。

    加载着色器

    static GLuint createGLShader(const char *shaderText, GLenum shaderType)
    {
        GLuint shader = glCreateShader(shaderType);
        glShaderSource(shader, 1, &shaderText, NULL);
        glCompileShader(shader);
        
        int compiled = 0;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
        if (!compiled) {
            GLint infoLen = 0;
            glGetShaderiv (shader, GL_INFO_LOG_LENGTH, &infoLen);
            if (infoLen > 1) {
                char *infoLog = (char *)malloc(sizeof(char) * infoLen);
                if (infoLog) {
                    glGetShaderInfoLog (shader, infoLen, NULL, infoLog);
                    GLlog("Error compiling shader: %s\n", infoLog);
                    free(infoLog);
                }
            }
            glDeleteShader(shader);
            return 0;
        }
        return shader;
    }
    

    1、创建着色器,通过glCreateShader创建着色器,type为着色器的类型GL_VERTEX_SHADER 和 GL_FRAGMENT_SHADER

    glCreateShader (GLenum type) //type: GL_VERTEX_SHADER 或者 GL_FRAGMENT_SHADER
    

    2、添加着色器源程序,将着色器源码关联到一个着色器对象shader上。string是一个有count行GLchar类型的字符串组成的数组,用来表示着色器的源代码数据。string可以以NULL结尾,也可以不是。如果length为NULL则string给出的每行都是以NULL结尾,否则length中必须有count个表示string长度的元素。(也就是说字符串以NULL结尾我们不用指定长度,否则必须制定每行的长度)

    glShaderSource (GLuint shader, GLsizei count, const GLchar* const *string, const GLint* length)
    

    3、编译着色器源程序

    glCompileShader (GLuint shader)
    

    4、删除着色器

    glDeleteShader (GLuint shader)
    

    5、容错处理,通过glGetShaderiv获取编译状态,通过glGetShaderInfoLog获取错误信息。

    #define GL_COMPILE_STATUS                                0x8B81
    #define GL_INFO_LOG_LENGTH                               0x8B84
    glGetShaderiv (GLuint shader, GLenum pname, GLint* params) 
    glGetShaderInfoLog (GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* infolog)
    

    创建着色器程序

    GLuint createGLProgram(const char *vertext, const char *frag)
    {
        GLuint program = glCreateProgram();
        
        GLuint vertShader = createGLShader(vertext, GL_VERTEX_SHADER);
        GLuint fragShader = createGLShader(frag, GL_FRAGMENT_SHADER);
        
        if (vertShader == 0 || fragShader == 0) {
            return 0;
        }
        
        glAttachShader(program, vertShader);
        glAttachShader(program, fragShader);
        
        glLinkProgram(program);
        GLint success;
        glGetProgramiv(program, GL_LINK_STATUS, &success);
        if (!success) {
            GLint infoLen;
            glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
            if (infoLen > 1) {
                GLchar *infoText = (GLchar *)malloc(sizeof(GLchar)*infoLen + 1);
                if (infoText) {
                    memset(infoText, 0x00, sizeof(GLchar)*infoLen + 1);
                    glGetProgramInfoLog(program, infoLen, NULL, infoText);
                    GLlog("%s", infoText);
                    free(infoText);
                }
            }
            glDeleteShader(vertShader);
            glDeleteShader(fragShader);
            glDeleteProgram(program);
            return 0;
        }
        
        glDetachShader(program, vertShader);
        glDetachShader(program, fragShader);
        glDeleteShader(vertShader);
        glDeleteShader(fragShader);
      
        return program;
    }
    

    1、创建着色器程序

     glCreateProgram (void)
    

    2、装配着色器

    glAttachShader (GLuint program, GLuint shader)
    

    3、链接着色器程序

    glLinkProgram (GLuint program)
    

    4、卸载着色器程序

    glDetachShader (GLuint program, GLuint shader)
    

    5、使用着色器程序

     glUseProgram (GLuint program)
    

    6、容错处理

    #define GL_LINK_STATUS                                   0x8B82
    #define GL_INFO_LOG_LENGTH                               0x8B84
    glGetProgramiv (GLuint program, GLenum pname, GLint* params)
    glGetProgramInfoLog (GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog)
    

    三角形绘制

    - (void)setupGLProgram
    {
        NSString *vertFile = [[NSBundle mainBundle] pathForResource:@"vert.glsl" ofType:nil];
        NSString *fragFile = [[NSBundle mainBundle] pathForResource:@"frag.glsl" ofType:nil];
        _program = createGLProgramFromFile(vertFile.UTF8String, fragFile.UTF8String);
        
        glUseProgram(_program);
    }
    
    - (void)setupVertexData
    {
       // 需要加static关键字,否则数据传输存在问题 
       static GLfloat vertices[] = {
            0.0f,  0.5f, 0.0f,
            -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f
        };
        GLint posSlot = glGetAttribLocation(_program, "position");
        glVertexAttribPointer(posSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices);
        glEnableVertexAttribArray(posSlot);
        
       //颜色数据
        static GLfloat colors[] = {
            0.0f, 1.0f, 1.0f,
            1.0f, 0.0f, 1.0f,
            1.0f, 1.0f, 0.0f
        };
        GLint colorSlot = glGetAttribLocation(_program, "color");
        glVertexAttribPointer(colorSlot, 3, GL_FLOAT, GL_FALSE, 0, colors);
        glEnableVertexAttribArray(colorSlot);
    }
    
    - (void)render
    {
        glClearColor(1.0, 1.0, 0.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);
        
        glViewport(0, 0, self.frame.size.width, self.frame.size.height);
    
    // 在这里初始化数据,可以加static关键字,也可以不加
    //    GLfloat vertices[] = {
    //         0.0f,  0.5f, 0.0f,
    //        -0.5f, -0.5f, 0.0f,
    //         0.5f, -0.5f, 0.0f
    //    };
    //    GLint posSlot = glGetAttribLocation(_program, "position");
    //    glVertexAttribPointer(posSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices);
    //    glEnableVertexAttribArray(posSlot);
    //    
    //    GLfloat colors[] = {
    //        0.0f, 1.0f, 0.0f,
    //        0.0f, 1.0f, 0.0f,
    //        0.0f, 1.0f, 0.0f
    //    };
    //    GLint colorSlot = glGetAttribLocation(_program, "color");
    //    glVertexAttribPointer(colorSlot, 3, GL_FLOAT, GL_FALSE, 0, colors);
    //    glEnableVertexAttribArray(colorSlot);
        
        [self setupVertexData];
        
        glDrawArrays(GL_TRIANGLES, 0, 3);
        
        //将指定 renderbuffer 呈现在屏幕上,在这里我们指定的是前面已经绑定为当前 renderbuffer 的那个,在 renderbuffer 可以被呈现之前,必须调用renderbufferStorage:fromDrawable: 为之分配存储空间。
        [_context presentRenderbuffer:GL_RENDERBUFFER];
    }
    
    glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)
    indx 指定要修改的顶点着色器中顶点变量id;
    size 指定每个顶点属性的组件数量。必须为1、2、3或者4。
    type 指定数组中每个组件的数据类型。可用的符号常量有GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT,GL_UNSIGNED_SHORT, GL_FIXED, 和 GL_FLOAT,初始值为GL_FLOAT;
    normalized 指定当被访问时,固定点数据值是否应该被归一化(GL_TRUE)或者直接转换为固定点值(GL_FALSE);
    stride 指定连续顶点属性之间的偏移量。如果为0,那么顶点属性会被理解为:它们是紧密排列在一起的。初始值为0;
    ptr 顶点数据指针。
    

    参考链接

    https://www.khronos.org/opengles/sdk/docs/reference_cards/OpenGL-ES-2_0-Reference-card.pdf

    http://www.cnblogs.com/kesalin/archive/2012/11/25/opengl_es_tutorial_02.html

    相关文章

      网友评论

        本文标题:OpenGL ES入门02-OpenGL ES着色器

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