写给高级前端的WebGL教程(02) Shader相关

755 阅读1分钟

GLSL语言

以下两段代码为GLSL语言着色器代码。

顶点着色器:每个几何顶点执行一次。

attribute vec3 a_position;
void main(void){
    gl_Position = vec4(a_position, 1.0);
}`

片元着色器:每个像素执行一次。

void main(void){
    gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0);
}

编译GLSL程序

生成Shader程序对象,获取其句柄。其中,type的取值可以为:context.VERTEX_SHADER(顶点着色器)、context.FRAGMENT_SHADER(片元着色器)。

let shader = context.createShader(type);

附加源代码,编译程序。

context.shaderSource(shader, source);
context.compileShader(shader);

完整代码如下:

function createShader(context, type, source){
    let shader = context.createShader(type);
    context.shaderSource(shader, source);
    context.compileShader(shader);
    // 查看程序是否报错
    if (!context.getShaderParameter(shader, context.COMPILE_STATUS)) {
        throw new Error('Shader程序编译出错!');
    }
    return shader;
}

链接GLSL程序

生成着色器程序对象:

let program = context.createProgram();

附加shader对象:

context.attachShader(program, shader);

链接GLSL程序:

context.linkProgram(program);

完整代码如下:

class ShaderProgram{

    constructor(context, shaders){
        let program = context.createProgram();
        for(let shader of shaders){
            context.attachShader(program, shader);
        }
        context.linkProgram(program);
        if (!context.getProgramParameter(program, context.LINK_STATUS)) {
            throw new Error('Shader程序链接出错!');
        }        
        this.m_program = program;
        this.m_context = context;
    }

    useProgram(){
        this.m_context.useProgram(this.m_program);
    }
    
    bindAttribLocation(index, name){
        this.m_context.bindAttribLocation(this.m_program, index, name);
    }
}

应用案例

let shaderProgram = new ShaderProgram(context,
    [createVertexShader(context, 
        `attribute vec3 a_position;
        void main(void){
            gl_Position = vec4(a_position, 1.0);
        }`), 
    createFragmentShader(context, 
        `void main(void){
            gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0);
        }`)
    ]);
shaderProgram.bindAttribLocation(a_position, "a_position");
shaderProgram.useProgram();