本文是通过三角形扇来绘制一个圆的。通过颜色插值来实现圆的渐变色。实现代码如下所示:

# 1、着色器代码

  • 顶点着色器代码
<script type="shader-source" id="vertexShader">
    //浮点数设置为中等精度
    precision mediump float;
    //接收 JavaScript 传递过来的点的坐标(X, Y)
    attribute vec2 a_Position;
    attribute vec4 a_Color;
    varying vec4 v_Color;
    void main(){
        // 最终的顶点坐标。
        gl_Position = vec4(a_Position, 0.0, 1.0);
        // 将顶点颜色传递给片元着色器,进行颜色插值计算
        v_Color = a_Color;
    }
</script>
  • 片元着色器代码
    //浮点数设置为中等精度
    precision mediump float;
    //全局变量,用来接收 JavaScript传递过来的颜色。
    varying vec4 v_Color;
    void main(){
        // 点的最终颜色。
        gl_FragColor = v_Color;
    }

# 2、javascript代码部分

  • WebGL上下文的获取
function init() {
    //获取canvas
    //通过getElementById()方法获取canvas画布
    const canvas = document.getElementById('canvas');
    //设置canvas尺寸为满屏
    //注意设置canvas的尺寸必须在获取WebGL上下文之前调用
    resizeCanvas(canvas);
    //通过方法getContext()获取WebGL上下文
    const gl = canvas.getContext('webgl');
    //创建着色器程序
    const program = initShader(gl);
    //跟着色器中的代码交互
    initBuffer(gl,program)
}
  • 初始化着色器函数
function initShader(gl) {
    //顶点着色器源码
    const vertexShaderSource = document.getElementById('vertexShader').innerText;
    //片元着色器源码
    const fragmentShaderSource = document.getElementById('fragmentShader').innerText;
    const vertexShader = gl.createShader(gl.VERTEX_SHADER);
    const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(vertexShader, vertexShaderSource);
    gl.shaderSource(fragmentShader, fragmentShaderSource);
    gl.compileShader(vertexShader);
    gl.compileShader(fragmentShader);
    const program = gl.createProgram();
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);
    gl.useProgram(program);
    return program;
}
  • 初始化缓冲区及与着色器的交互
function initBuffer(gl,program) {
    // 定义组成矩形的两个三角形,共计四个顶点,每个顶点包含2个坐标分量和4个颜色分量,其中 V0,V1,V2代表左下角三角形,V1,V2,V3代表右上角三角形。
    const x=Math.round(canvas.width/2);
    const y=Math.round(canvas.height/2);
    let positions = createCircleVertex(x, y, 200, 350);

    let a_Position = gl.getAttribLocation(program, 'a_Position');
    let a_Color = gl.getAttribLocation(program, 'a_Color');

    gl.enableVertexAttribArray(a_Position);
    gl.enableVertexAttribArray(a_Color);
    // 创建缓冲区
    let buffer = gl.createBuffer();
    // 绑定缓冲区为当前缓冲
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    // 设置 a_Position 属性从缓冲区读取数据方式
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 24, 0);
    // 设置 a_Color 属性从缓冲区读取数据方式
    gl.vertexAttribPointer(a_Color, 4, gl.FLOAT, false, 24, 8);
    // 向缓冲区传递数据
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
    render(gl,positions.length/6);
}
  • 设置画布的大小
//设置画布的大小
//设置canvas的大小
function resizeCanvas(canvas, width, height) {
    if (canvas.width !== width) {
        canvas.width = width ? width : window.innerWidth;
    }
    if (canvas.height !== height) {
        canvas.height = height ? height : window.innerHeight;
    }
}
  • 创建圆的坐标及颜色数据
function createCircleVertex(x, y, radius, n) {
    let positions = [...coordTransform(x, y), 1, 1, 0, 1];
    for (let i = 0; i <= n; i++) {
        let angle = (i * Math.PI * 2) / n;
        const [positionX, positionY] = coordTransform(x + radius * Math.sin(angle), y + radius * Math.cos(angle))
        positions.push(positionX, positionY,1,0,0,1)
    }
    return positions;
}
  • 将屏幕坐标换算成设备坐标系中的坐标
function coordTransform(x, y) {
    const { width, height } = canvas;
    // 将 canvas 的坐标值 转换为 [-1.0, 1.0]的范围。
    const posisionX = 2 * x / width - 1;
    // canvas的 Y 轴坐标方向和 设备坐标系的相反。
    const positionY = -(2 * y / height - 1);
    return [posisionX, positionY];
}
  • WebGL渲染
 function render(gl,count) {
    //设置清屏颜色为黑色。
    gl.clearColor(0, 0, 0, 1);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.TRIANGLE_FAN, 0, count);
}

参考

WebGL零基础入门教程(郭隆邦) (opens new window) WebGL 入门与实践 (opens new window) WebGL官方文档 (opens new window)

Last Updated: 9/24/2024, 6:06:00 PM