Skip to content

绘制基本图形:三角形与矩形

一、缓冲区对象

在WebGL中,缓冲区对象(Buffer Objects)用于存储顶点数据,这些数据可以是顶点的位置、颜色、纹理坐标等。通过将顶点数据存储在缓冲区对象中,可以高效地传递数据到GPU,从而提高渲染性能。

(一)创建和绑定缓冲区对象

缓冲区对象的创建和绑定是通过gl.createBuffergl.bindBuffer完成的。以下是一个简单的示例:

javascript
// 创建缓冲区对象
const buffer = gl.createBuffer();

// 绑定缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);

// 将顶点数据存储到缓冲区对象中
const vertices = [
    -0.5, -0.5,
     0.5, -0.5,
     0.0,  0.5
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
  • gl.createBuffer:创建一个新的缓冲区对象。
  • gl.bindBuffer:将缓冲区对象绑定到一个目标(如gl.ARRAY_BUFFER)。
  • gl.bufferData:将数据存储到当前绑定的缓冲区对象中。gl.STATIC_DRAW表示数据不会频繁更改。

二、绘制三角形

(一)设置顶点数据

绘制三角形需要定义三个顶点的位置。以下是一个完整的示例,展示如何设置顶点数据并绘制一个三角形:

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebGL Triangle Example</title>
</head>
<body>
    <canvas id="webglCanvas" width="800" height="600"></canvas>
    <script>
        const canvas = document.getElementById('webglCanvas');
        const gl = canvas.getContext('webgl');

        if (!gl) {
            alert('Your browser does not support WebGL');
        }

        // 顶点着色器代码
        const vertexShaderSource = `
            attribute vec2 a_position;
            void main() {
                gl_Position = vec4(a_position, 0.0, 1.0);
            }
        `;

        // 片元着色器代码
        const fragmentShaderSource = `
            precision mediump float;
            void main() {
                gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 红色
            }
        `;

        // 创建顶点着色器
        const vertexShader = gl.createShader(gl.VERTEX_SHADER);
        gl.shaderSource(vertexShader, vertexShaderSource);
        gl.compileShader(vertexShader);

        // 创建片元着色器
        const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
        gl.shaderSource(fragmentShader, fragmentShaderSource);
        gl.compileShader(fragmentShader);

        // 创建程序
        const program = gl.createProgram();
        gl.attachShader(program, vertexShader);
        gl.attachShader(program, fragmentShader);
        gl.linkProgram(program);

        if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
            alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(program));
        }

        // 使用程序
        gl.useProgram(program);

        // 定义顶点数据
        const vertices = [
            -0.5, -0.5,
             0.5, -0.5,
             0.0,  0.5
        ];

        // 创建缓冲区对象
        const buffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

        // 获取attribute变量的位置
        const positionLocation = gl.getAttribLocation(program, 'a_position');

        // 绑定缓冲区到attribute变量
        gl.enableVertexAttribArray(positionLocation);
        gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);

        // 清空画布
        gl.clearColor(0.0, 0.0, 0.0, 1.0); // 设置背景颜色为黑色
        gl.clear(gl.COLOR_BUFFER_BIT);

        // 绘制三角形
        gl.drawArrays(gl.TRIANGLES, 0, 3);
    </script>
</body>
</html>

(二)代码解析

  1. 顶点数据:定义了三角形的三个顶点位置。
  2. 缓冲区对象:将顶点数据存储到缓冲区对象中。
  3. 顶点着色器:将顶点位置传递到gl_Position
  4. 片元着色器:设置片元颜色为红色。
  5. 绘制命令gl.drawArrays用于绘制三角形。

三、绘制矩形

矩形可以通过两个三角形组成。以下是一个完整的示例,展示如何绘制一个矩形:

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebGL Rectangle Example</title>
</head>
<body>
    <canvas id="webglCanvas" width="800" height="600"></canvas>
    <script>
        const canvas = document.getElementById('webglCanvas');
        const gl = canvas.getContext('webgl');

        if (!gl) {
            alert('Your browser does not support WebGL');
        }

        // 顶点着色器代码
        const vertexShaderSource = `
            attribute vec2 a_position;
            void main() {
                gl_Position = vec4(a_position, 0.0, 1.0);
            }
        `;

        // 片元着色器代码
        const fragmentShaderSource = `
            precision mediump float;
            void main() {
                gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); // 绿色
            }
        `;

        // 创建顶点着色器
        const vertexShader = gl.createShader(gl.VERTEX_SHADER);
        gl.shaderSource(vertexShader, vertexShaderSource);
        gl.compileShader(vertexShader);

        // 创建片元着色器
        const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
        gl.shaderSource(fragmentShader, fragmentShaderSource);
        gl.compileShader(fragmentShader);

        // 创建程序
        const program = gl.createProgram();
        gl.attachShader(program, vertexShader);
        gl.attachShader(program, fragmentShader);
        gl.linkProgram(program);

        if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
            alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(program));
        }

        // 使用程序
        gl.useProgram(program);

        // 定义顶点数据
        const vertices = [
            -0.5, -0.5,  // 左下
             0.5, -0.5,  // 右下
             0.5,  0.5,  // 右上
            -0.5,  0.5   // 左上
        ];

        // 创建缓冲区对象
        const buffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

        // 获取attribute变量的位置
        const positionLocation = gl.getAttribLocation(program, 'a_position');

        // 绑定缓冲区到attribute变量
        gl.enableVertexAttribArray(positionLocation);
        gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);

        // 清空画布
        gl.clearColor(0.0, 0.0, 0.0, 1.0); // 设置背景颜色为黑色
        gl.clear(gl.COLOR_BUFFER_BIT);

        // 绘制矩形(两个三角形)
        gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
    </script>
</body>
</html>

(二)代码解析

  1. 顶点数据:定义了矩形的四个顶点位置。
  2. 缓冲区对象:将顶点数据存储到缓冲区对象中。
  3. 顶点着色器:将顶点位置传递到gl_Position
  4. 片元着色器:设置片元颜色为绿色。
  5. 绘制命令gl.drawArrays使用gl.TRIANGLE_FAN模式绘制两个三角形,组成一个矩形。

四、索引绘制

索引绘制是一种优化技术,通过使用索引缓冲区来减少重复的顶点数据。以下是一个完整的示例,展示如何使用索引绘制矩形:

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebGL Indexed Rectangle Example</title>
</head>
<body>
    <canvas id="webglCanvas" width="800" height="600"></canvas>
    <script>
        const canvas = document.getElementById('webglCanvas');
        const gl = canvas.getContext('webgl');

        if (!gl) {
            alert('Your browser does not support WebGL');
        }

        // 顶点着色器代码
        const vertexShaderSource = `
            attribute vec2 a_position;
            void main() {
                gl_Position = vec4(a_position, 0.0, 1.0);
            }
        `;

        // 片元着色器代码
        const fragmentShaderSource = `
            precision mediump float;
            void main() {
                gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0); // 蓝色
            }
        `;

        // 创建顶点着色器
        const vertexShader = gl.createShader(gl.VERTEX_SHADER);
        gl.shaderSource(vertexShader, vertexShaderSource);
        gl.compileShader(vertexShader);

        // 创建片元着色器
        const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
        gl.shaderSource(fragmentShader, fragmentShaderSource);
        gl.compileShader(fragmentShader);

        // 创建程序
        const program = gl.createProgram();
        gl.attachShader(program, vertexShader);
        gl.attachShader(program, fragmentShader);
        gl.linkProgram(program);

        if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
            alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(program));
        }

        // 使用程序
        gl.useProgram(program);

        // 定义顶点数据
        const vertices = [
            -0.5, -0.5,  // 左下
             0.5, -0.5,  // 右下
             0.5,  0.5,  // 右上
            -0.5,  0.5   // 左上
        ];

        // 创建顶点缓冲区对象
        const vertexBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

        // 获取attribute变量的位置
        const positionLocation = gl.getAttribLocation(program, 'a_position');

        // 绑定缓冲区到attribute变量
        gl.enableVertexAttribArray(positionLocation);
        gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);

        // 定义索引数据
        const indices = [
            0, 1, 2,  // 第一个三角形
            0, 2, 3   // 第二个三角形
        ];

        // 创建索引缓冲区对象
        const indexBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);

        // 清空画布
        gl.clearColor(0.0, 0.0, 0.0, 1.0); // 设置背景颜色为黑色
        gl.clear(gl.COLOR_BUFFER_BIT);

        // 绘制矩形(使用索引绘制)
        gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);
    </script>
</body>
</html>

(二)代码解析

  1. 顶点数据:定义了矩形的四个顶点位置。
  2. 索引数据:定义了两个三角形的顶点索引。
  3. 顶点缓冲区对象:将顶点数据存储到缓冲区对象中。
  4. 索引缓冲区对象:将索引数据存储到缓冲区对象中。
  5. 顶点着色器:将顶点位置传递到gl_Position
  6. 片元着色器:设置片元颜色为蓝色。
  7. 绘制命令gl.drawElements使用索引缓冲区绘制两个三角形,组成一个矩形。

总结

通过缓冲区对象存储顶点数据,可以高效地将数据传递到GPU。绘制三角形和矩形是WebGL的基础操作,而索引绘制则是一种优化技术,可以减少重复的顶点数据,提高渲染性能。掌握这些基本操作后,你可以开始探索更复杂的图形渲染技术。

Theme by threelab