OpenGL ES2学习笔记3-- 使用Shader

渲染流水线

OpenGL ES 1.0和1.1使用的渲染方式是Fixed Function Pipeline,从2.0版本开始,转而使用Programmable Shader Pipeline。要想使用OpenGL ES 2.0,必须使用一种语法很像C语言的语言GLSL来编写Shader程序。

封装OpenGL API

直接通过OpenGL API来使用GLSL太繁琐了,因此我进行了简单的抽象和封装。

GLWrapper

import javax.media.opengl.GL2ES2;

public abstract class GLWrapper {
    
    protected GL2ES2 getGL() {
        return Glob.getInstance().getGL();
    }
    
}
GLWrapper类只有一个方法getGL(),返回 javax.media.opengl.GL2ES2接口。关于GL2ES2接口的更多信息,请看 这个网页。Glob对象稍后介绍。

GLObject

public abstract class GLObject extends GLWrapper {
    
    protected int objectId;
    
}
GLObject继承GLWrapper,用来抽象OpenGL管理的对象(应该在GPU内存里)。

shadercompiler和ShaderObject

import static javax.media.opengl.GL2ES2.GL_FRAGMENT_SHADER;
import static javax.media.opengl.GL2ES2.GL_VERTEX_SHADER;

public class shadercompiler extends GLWrapper {
    
    public ShaderObject compiLevertexShader(String shadercode) {
        return compileShader(GL_VERTEX_SHADER,shadercode);
    }
    
    public ShaderObject compileFragmentShader(String shadercode) {
        return compileShader(GL_FRAGMENT_SHADER,shadercode);
    }
    
    private ShaderObject compileShader(int shaderType,String shadercode) {
        ShaderObject obj = new ShaderObject();
        
        obj.objectId = getGL().glCreateShader(shaderType);
        getGL().glShaderSource(obj.objectId,1,                new String[] {shadercode},new int[] {shadercode.length()},0);
        getGL().glCompileShader(obj.objectId);

        return obj;
    }
    
}
public class ShaderObject extends GLObject {
    
}

前面说GLSL的语法和C语言很像,其实不光是语法,编译和链接过程也很像。shadercompiler抽象Shader编译器,ShaderObject抽象编译后的Shader对象。Vertex ShaderFragment Shader的详细信息可以从OpenGL wiki上查到。

ShaderLinker和ShaderProgram

public class ShaderLinker extends GLWrapper {
    
    public ShaderProgram linkProgram(ShaderObject... shaders) {
        ShaderProgram program = new ShaderProgram();
        program.objectId =  getGL().glCreateProgram();

        for (ShaderObject shader : shaders) {
            getGL().glAttachShader(program.objectId,shader.objectId);
        }

        getGL().glLinkProgram(program.objectId);
        return program;
    }
    
}
public class ShaderProgram extends GLObject {
    
    public void use() {
        getGL().gluseProgram(objectId);
    }
    
}
类似C语言的链接过程,ShaderLinker把编译好的ShaderObject链接一个ShaderProgram,之后就可以使用这个ShaderProgram了。

Glob

Glob单例类定义一个方法来编译和链接Shader程序:

import javax.media.opengl.GL2ES2;

public class Glob {
    
    private static final Glob INSTANCE = new Glob();
    public static Glob getInstance() {
        return INSTANCE;
    }
    
    private GL2ES2 gl;
    private final shadercompiler shadercompiler;
    private final ShaderLinker shaderLinker;
    
    private Glob() {
        shadercompiler = new shadercompiler();
        shaderLinker = new ShaderLinker();
    }

    public ShaderProgram compileAndLink(String vertexshadercode,String fragmentshadercode) {
        ShaderObject vertexShader = shadercompiler.compiLevertexShader(vertexshadercode);
        ShaderObject fragmentShader = shadercompiler.compileFragmentShader(fragmentshadercode);
        ShaderProgram program = shaderLinker.linkProgram(vertexShader,fragmentShader);
        
        getGL().glDeleteShader(vertexShader.objectId);
        getGL().glDeleteShader(fragmentShader.objectId);
        
        return program;
    }
    
    ...
    
}

类图

编译和链接过程示意图


在OpenGL Console里试验

打开OpenGL Console,运行下面的脚本

import javax.media.opengl.GL

def vertexShader = """
  void main() {
    gl_Position = vec4(0.0,0.0,1.0);
    gl_PointSize = 10.0;
  }
"""

def fragmentShader = """
  void main() {
    gl_FragColor = vec4(1.0,1.0);
  }
"""
def shaderProgram = glob.compileAndLink(vertexShader,fragmentShader)
shaderProgram.use()

gl.glClearColor(0.8f,0.6f,0.4f,1.0f)
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
画布应该被清除成像下面这样的淡黄色:


代码解释

上面的脚本只是定义了两个字符串,一个是Vertex Shader代码一个是Fragment Shader代码,然后调用Glob对象的compileAndLink()方法编译和链接Shader,最后清屏。本篇文章里的Shader没有起任何作用,只是证明我们的代码可以工作。在下一篇文章中,我会用Shader在画布上画一个点。

相关文章

背景:    8月29日,凌晨4点左右,某服务告警,其中一个...
https://support.smartbear.comeadyapi/docs/soapui/steps/g...
有几个选项可用于执行自定义JMeter脚本并扩展基线JMeter功能...
Scala和Java为静态语言,Groovy为动态语言Scala:函数式编程,...
出处:https://www.jianshu.com/p/ce6f8a1f66f4一、一些内部...
在运行groovy的junit方法时,报了这个错误:java.lang.Excep...