美文网首页
一、由最简单的GLSL ES程序开始

一、由最简单的GLSL ES程序开始

作者: 猫爸iYao | 来源:发表于2019-03-05 11:14 被阅读0次

    基于OpenGL ES 2.0 for Android。

    自OpenGL ES 2.0开始,可编程着色器(Programmable Shader)开始逐渐替代1.x时代的部分固定功能图形API。它的出现使得OpenGL ES编程更加灵活和强大。OpenGL ES 2.0至今仍是最流行、最广泛、也是最可靠的移动端图形API。

    最开始,可编程着色器采用一种混合开发的方式,这种方式存在着不直观、程序复杂的问题,对开发者很不友好。因此,混合开发方式并没有完全释放可编程着色器强大的可编程能量。后来,一种更加直观的GPU程序设计语言出现了,它就是GLSL(OpenGL Shading Language)。它的出现真正促进了OpenGL的发展和繁荣。

    GLSL不仅仅是一门开发语言,它同样是一个GPU图形程序设计标准。它具有以下几种显而易见的好处:

    • 具有跨平台的特性,能够支持各种主流操作系统;
    • 所有支持OpenGL的GPU都可以支持GLSL开发的程序;
    • 不同的GPU厂商可以在统一标准下实现自己特定的优化。

    程序的入口main函数

    GLSL是基于C语言修改的编程语言。一个最简单的顶点着色器(Vertex Shader)是这样的:

    void main() {
        gl_Position = vec4(0);
    }
    

    所有的GLSL实现的着色器都有一个类似于C语言入口函数的函数:void main(void),它同样是GLSL程序的入口函数。其中,参数类型void是可选的。

    众所周知,一个类C程序从源代码到可执行文件通常需要经过编译和链接。GLSL也不例外,不过它的编译和链接方式从命令行/GUI变成了OpenGL ES的API。

    一个编译并使用GLSL着色器程序的完整过程是这样的:

    1. 创建着色器对象(Shader Object),并获得对象引用
    2. 为着色器对象注入GLSL源代码,并编译GLSL源代码
    3. 创建一个GLSL程序对象(Program Object),并挂载着色器对象
    4. 链接GLSL程序
    5. 使用GLSL程序

    在Android中,通常是这样一段代码。

    //GLSL 顶点着色器源代码
    val shaderCodeString = """
        void main() {
            glPosition = vec4(0);
        }
    """.trimIndent()
    
    //1,创建着色器对象(Shader Object),并获得对象引用
    val shaderHandle = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER)
    
    //2,为着色器对象注入GLSL源代码,并编译GLSL源代码
    GLES20.glShaderSource(shaderHandle, shaderCodeString)
    GLES20.glCompileShader(shaderHandle)
    
    //3,创建一个GLSL程序对象(Program Object),并挂载着色器对象
    val program = GLES20.glCreateProgram()
    GLES20.glAttachShader(program, shaderHandle)
    
    //4,链接GLSL程序
    GLES20.glLinkProgram(program)
    
    //5,使用GLSL程序
    GLES20.glUseProgram(program)
    

    尽管这是一个完整的GLSL程序使用流程,但是这样做的结果是程序运行到第四步,链接GLSL程序(GLES20.glLinkProgram(program)),就已经出错了。

    那么,如何获取到整个过程中的的错误信息呢?GLES方法glGetError()可以获得函数执行的错误代码,当然这种方式只能获得一些简单的信息。我们可以通过它定位到出错的函数,然后去查阅OpenGL ES官方文档了解函数的报错信息和正确用法。另外,glGetProgramInfoLog(int program)可以获取GLSL程序编译过程中较为详细的日志。例如,上面例子中,我们可以在步骤4执行后得到如下日志信息:

    Link failed because of missing fragment shader.

    日志显示,链接过程失败了,原因是缺少片元着色器。要知道OpenGL ES的着色器有两大类:顶点着色器、片元着色器(Fragment Shader)。这两类着色器必须成对且仅有一对出现在一个GLSL程序中才能够链接通过。一个正确的GLSL程序应当创建一个顶点着色器和一个片元着色器并挂载到同一个GLSL程序对象,之后再链接,使用。

    下面看一看重新整理后的流程:

    1. 创建并编译一个顶点着色器对象
    2. 创建并编译一个片元着色器对象
    3. 创建一个GLSL程序对象,并挂载1,2中创建的着色器对象,然后链接GLSL程序对象
    4. 使用3中的GLSL程序对象
    fun loadProgram(vertexCode: String, fragmentCode: String): Int  {
        //1. 创建并编译一个顶点着色器对象
        val vertexHandle = loadShader(GLES20.GL_VERTEX_SHADER, vertexCode)
        //2. 创建并编译一个片元着色器对象 
        val fragmentHandle =loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentCode)
        //3. 创建一个GLSL程序对象,并挂载1,2中创建的着色器对象,然后链接GLSL程序对象
        return linkProgram(vertexHandle, fragmentHandle)
    }
    
    fun loadShader(type: Int, code: String): Int {
        val shader = GLES20.glCreateShader(type)
        GLES20.glShaderSource(shader, code)
        GLES20.glCompileShader(shader)
        return shader
    }
    
    fun linkProgram(vertexShader: Int, fragmentShader: Int): Int {
        val program = GLES20.glCreateProgram()
        GLES20.glAttachShader(program, vertexShader)
        GLES20.glAttachShader(program, fragmentShader)
        GLES20.glLinkProgram(program)
        return program
    }
    

    虽然整个流程十分简单,但是新手由于对API不熟悉,仍可能出错。因此学会使用诸如glGetError()glGetProgramInfoLog(int)glGetShaderInfoLog(int)等调试工具很重要。一个好的习惯是开发阶段每一次API调用都打印日志,这能够帮助新手快速定位问题,节约时间。同时,尽量实现封装,减少重复代码出错率。

    相关文章

      网友评论

          本文标题:一、由最简单的GLSL ES程序开始

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