webGL之初始化gl、program

549 阅读2分钟

使用webGL绘制一个“Hello World” 并不是很轻松。它要涉及初始化WebGLRenderingContext初始化、WebGLProgram的初始化、模型顶点数据制作,以及最终渲染和绘制等:

  1. 我们要生成或找到画布对象;
  2. 初始化WebGLRenderingContext、WebGLProgram 即gl和program;
  3. 获取模型网格顶点数据
  4. 实现模型绘制

在这一节中我们来主要阐述如何初始化WebGLRenderingContext、WebGLProgram

**index.ts**

import RenderContext from './libs/renderContext';
import { Modal, Shape} from './libs';
const container = document.getElementById('root');
function main() {
    //初始化canvas画布 以及 RenderContext中的gl和program
    RenderContext.init({ 
        container,
        width: container.offsetWidth,
        height: container.offsetHeight,
    });
    
    //获取gl和program
    const gl = RenderContext.getGL()
    const program = RenderContext.getProgram();
   
    **  使用gl和program来绘制图形  **
}

main();
其中RenderContext是一个全局的单例,控制gl和program的初始化的过程,并提供导出获取gl和program的方法
**renderContext.ts**

import { initGL } from './boot/initGL'
import { initProgram } from './boot/initProgram'
interface Options {
    container: HTMLElement;
    canvas?: HTMLCanvasElement;
    width: number;
    height: number;
}
//创建一个单例 RenderContext
export default class RenderContext{ 
    static gl: WebGLRenderingContext;//绑定的gl
    static program: WebGLProgram; //绑定的program
    static canvas: HTMLCanvasElement; //绑定的canvas
    
    //初始化canvas
    static init(options: Options) { 
        if (RenderContext.canvas) return;
        if (options.canvas) {
                RenderContext.canvas = options.canvas;
        } else {
            const canvasHtml = document.createElement('canvas');
            canvasHtml.width = options.width;
            canvasHtml.height = options.height;
            canvasHtml.id = 'game-canvas';
            options.container.appendChild(canvasHtml);
            RenderContext.canvas = canvasHtml;
        }
    }
    //初始化gl和program
    static initContext(){
        if (RenderContext.gl) return;
        if (!RenderContext.canvas) return;
        const gl = initGL(RenderContext.canvas);
        if (gl) {
            const program = initProgram(gl);
            (gl as any).canvas.width = (gl as any).canvas.clientWidth;
            (gl as any).canvas.height = (gl as any).canvas.clientHeight;
            RenderContext.program = program;
            RenderContext.gl = gl;
        } 
    }
    
    //获取单例gl
    static getGL() {
        RenderContext.initContext()
        return RenderContext.gl;
    }
    
    //获取单例program
    static getProgram() {
        RenderContext.initContext()
        return RenderContext.program;
    }
}
initGL方法是通过canvas对象来获取WebGLRenderingContext
**boot/initGL.ts**

// 初始化gl
export function initGL(canvas: HTMLCanvasElement): WebGLRenderingContext | void {
    if (canvas) {
        const gl:WebGLRenderingContext  = canvas.getContext("webgl");
        if (!gl) {
            throw "gl initialize fail"
        }
        return gl;
    }
}
initProgram方法用于初始化WebGLProgram,并将program与gl关联;
**boot/initProgram.ts**

import { vertexShaderSource, fragmentShaderSource } from './shader'

//创建的是顶点着色器、片元着色器的方法
const loadShader = (gl: WebGLRenderingContext, type: GLenum, source: string) => {
    const shader = gl.createShader(type); //创建的是顶点着色器、片元着色器类型
    gl.shaderSource(shader, source);
    gl.compileShader(shader); // 编译shader L代码
    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        const error = `An error occurred compiling this shaders: ${gl.getShaderInfoLog(shader)}`
        gl.deleteShader(shader);
        throw error;
    }
    return shader
}

//初始化program
export const initProgram = (gl: WebGLRenderingContext) => {
    //创建顶点、片元着色器
    const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
    const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
    
    //创建program
    const program: WebGLProgram = gl.createProgram();
    
    //将着色器绑定到program
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);

    // 将该program链接到gl
    gl.linkProgram(program);
    return program;
}
每创建一个WebGLProgram都需要:顶点着色器vertexShader、片元着色器fragmentShader
**boot/shader.ts**

/**
 * a_position 顶点[-1,1]数据 (0, 0)
 * u_resolution 分辨率 (1920, 1680)
 * zeroToOne 将顶点左边转化为[0,1]
 * zeroToTwo 顶点坐标转换为[0,2]
 * clipSpace 将顶点坐标转换[-1,1]
 * gl_Position 设置点数据
 * 
 */
export const vertexShaderSource =`
attribute vec2 a_position;
uniform vec2 u_resolution;
void main() {
    vec2 zeroToOne = a_position / u_resolution;
    vec2 zeroToTwo = zeroToOne * 2.0;
    vec2 clipSpace = zeroToTwo - 1.0;
    gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
}`;
export const fragmentShaderSource =`
precision mediump float;
uniform vec4 u_color;
void main() {
    gl_FragColor = u_color;
}`;