当前位置: 首页 > article >正文

【WebGL】attribute方式实例化绘制

背景

一般有attribute和uniform两种方式进行实例化绘制

attribute方式实例化

这里需要注意

  • bufferData和bufferSubData方式的用法顺序和参数
    • gl.bufferData(target, sizeOrData, usage);

      • sizeOrData(实例化配合bufferSubData 更新数据一般使用这种先
        • 传入数字(size)指定要分配的缓冲区大小,单位是字节。此时,缓冲区会被分配相应大小的内存,但不会被填充具体的数据。当你后续会使用 bufferSubData 方法来逐步填充缓冲区数据,或者不确定具体数据但先需要分配内存时,会采用这种方式
        • 传入类型化数组,直接将类型化数组中的数据写入到缓冲区中,同时根据数组的大小为缓冲区分配相应的内存。常见的类型化数组有 Float32Array、Uint16Array 等。当你已经有了完整的顶点数据或索引数据,并且想一次性将它们写入缓冲区时,使用这种方式很方便。
    • bufferSubData (target, offset, data)
      方法用于更新已经绑定的缓冲区对象中的一部分数据。它允许你在不重新创建整个缓冲区的情况下,修改缓冲区中的特定数据区域,这在动态更新数据时非常有用,比如动画中的顶点位置变化。

  • vertexAttribDivisor(index, divisor)
    • index:指定要设置的顶点属性的索引,这个索引通常是通过 gl.getAttribLocation 方法获取的。
    • divisor:指定属性更新的频率,是一个无符号整数。具体含义如下:
      • divisor 为 0 时,表示该属性在每个顶点都更新,这是传统的绘制方式。
      • divisor 为 1 时,表示该属性在每个实例更新一次,这是实例化绘制中最常用的设置。
      • divisor 大于 1 时,表示该属性每 divisor 个实例更新一次
  • drawArraysInstanced
    • mode:指定绘制的图元类型,是一个枚举值。常见的取值有:
      • gl.POINTS:绘制一系列点。
      • gl.LINES:绘制一系列独立的线段。
      • gl.TRIANGLES:绘制一系列独立的三角形。
    • first:指定从顶点数组的第几个元素开始绘制,是一个无符号整数。
    • count:指定要绘制的顶点数量,是一个无符号整数。
    • primcount:指定要绘制的实例数量,是一个无符号整数。
attribute方式案例
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebGL 2 Instanced Rendering with mat4 Attribute using bufferSubData</title>
    <style>
        canvas {
            display: block;
        }
    </style>
</head>

<body>
    <canvas id="glCanvas" width="640" height="480"></canvas>
    <script>
        function main() {
            // 获取 canvas 元素和 WebGL 2 上下文
            const canvas = document.getElementById('glCanvas');
            const gl = canvas.getContext('webgl2');

            if (!gl) {
                alert('Unable to initialize WebGL 2. Your browser or machine may not support it.');
                return;
            }

            // 设置视口大小
            gl.viewport(0, 0, canvas.width, canvas.height);

            // 禁用深度测试
            gl.disable(gl.DEPTH_TEST);
            // 禁用混合
            gl.disable(gl.BLEND);

            // 顶点着色器代码
            const vertexShaderSource = `#version 300 es
             layout (location = 0) in vec2 a_position;
                layout (location = 1) in mat4 a_instanceMatrix;

                void main() {
                    // 使用矩阵变换顶点位置
                    vec4 pos = a_instanceMatrix * vec4(a_position, 0.0, 1.0);
                    gl_Position = pos;
                }
            `;

            // 片段着色器代码
            const fragmentShaderSource = `#version 300 es
                precision mediump float;
                out vec4 outColor;

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

            // 创建着色器程序
            function createShader(gl, type, source) {
                const shader = gl.createShader(type);
                gl.shaderSource(shader, source);
                gl.compileShader(shader);
                const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
                if (!success) {
                    console.error('Shader compilation error:', gl.getShaderInfoLog(shader));
                    gl.deleteShader(shader);
                    return null;
                }
                return shader;
            }

            function createProgram(gl, vertexShader, fragmentShader) {
                const program = gl.createProgram();
                gl.attachShader(program, vertexShader);
                gl.attachShader(program, fragmentShader);
                gl.linkProgram(program);
                const success = gl.getProgramParameter(program, gl.LINK_STATUS);
                if (!success) {
                    console.error('Program linking error:', gl.getProgramInfoLog(program));
                    gl.deleteProgram(program);
                    return null;
                }
                return program;
            }

            const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
            const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
            if (!vertexShader || !fragmentShader) {
                return;
            }
            const program = createProgram(gl, vertexShader, fragmentShader);
            if (!program) {
                return;
            }

            // 顶点数据
            const positions = [
                -0.1, -0.1,
                0.1, -0.1,
                0.0, 0.1
            ];

            // 预先分配足够的空间给矩阵数据
            const numInstances = 2;
            const matrixDataSize = numInstances * 4 * 4 * 4; // 每个 mat4 是 4x4 矩阵,每个元素是 float(4 字节)
            const matrixBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, matrixBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, matrixDataSize, gl.DYNAMIC_DRAW);

            // 初始实例化矩阵数据
            let mat4Data = new Float32Array([
                1, 0, 0, 0,
                0, 1, 0, 0,
                0, 0, 1, 0,
                -0.5, 0, 0, 1,

                1, 0, 0, 0,
                0, 1, 0, 0,
                0, 0, 1, 0,
                0.5, 0, 0, 1
            ]);

            // 使用 bufferSubData 更新矩阵数据
            gl.bindBuffer(gl.ARRAY_BUFFER, matrixBuffer);
            gl.bufferSubData(gl.ARRAY_BUFFER, 0, mat4Data);

            // 创建顶点缓冲区
            const positionBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

            // 获取属性位置
            const positionAttributeLocation = 0//gl.getAttribLocation(program, 'a_position');
            const matrixAttributeLocation = 1//gl.getAttribLocation(program, 'a_instanceMatrix');

            if (positionAttributeLocation === -1 || matrixAttributeLocation === -1) {
                console.error('Failed to get attribute location');
                return;
            }

            // 启用顶点位置属性
            gl.enableVertexAttribArray(positionAttributeLocation);
            gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
            gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);

            // 为矩阵的每个 vec4 分量设置 attribute
            const bytesPerMatrix = 4 * 4 * 4; // 4 个 vec4,每个 vec4 4 个浮点数,每个浮点数 4 字节
            for (let i = 0; i < 4; i++) {
                const loc = matrixAttributeLocation + i;
                gl.enableVertexAttribArray(loc);
                const offset = i * 4 * 4; // 每个 vec4 偏移 16 字节
                gl.bindBuffer(gl.ARRAY_BUFFER, matrixBuffer);
                gl.vertexAttribPointer(loc, 4, gl.FLOAT, false, bytesPerMatrix, offset);
                // 设置每个实例更新一次
                gl.vertexAttribDivisor(loc, 1);
            }

            // 渲染循环
            let time = 0;
            function render() {
                // 更新矩阵数据
                time += 0.01;
                mat4Data[12] = -0.5 + Math.sin(time) * 0.2; // 修改第一个矩阵的平移分量
                mat4Data[28] = 0.5 + Math.cos(time) * 0.2;  // 修改第二个矩阵的平移分量

                // 使用 bufferSubData 更新缓冲区数据
                gl.bindBuffer(gl.ARRAY_BUFFER, matrixBuffer);
                gl.bufferSubData(gl.ARRAY_BUFFER, 0, mat4Data);

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

                // 清除画布
                gl.clearColor(0.0, 0.0, 0.0, 1.0);
                gl.clear(gl.COLOR_BUFFER_BIT);

                // 绘制实例
                const instanceCount = numInstances;
                gl.drawArraysInstanced(gl.TRIANGLES, 0, positions.length / 2, instanceCount);

                // 请求下一帧渲染
                requestAnimationFrame(render);
            }

            // 开始渲染循环
            render();
        }

        main();
    </script>
</body>

</html>

http://www.kler.cn/a/558256.html

相关文章:

  • 深入理解 SQL 事务隔离级别:脏读、不可重复读和幻读
  • 编程考古-忘掉它,Delphi 8 for the Microsoft .NET Framework
  • Redis作为缓存和数据库的数据一致性问题
  • ES6中Object.defineProperty 的详细用法和使用场景以及例子
  • php文件上传
  • _vm.xxxxxx is not a function“ vue2错误
  • 贪心算法
  • 基于 DeepSeek LLM 本地知识库搭建开源方案(AnythingLLM、Cherry、Ragflow、Dify)认知
  • 使用Java爬虫获取1688 item_search_factory 接口的工厂搜索数据
  • 网页请求腾讯云环境的云函数
  • [Android]如何让APP快速被系统杀掉
  • 在 Mac ARM 架构的 macOS 系统上启用 F1 键作为 Snipaste 的截屏快捷键
  • 文本分类与情感分析算法
  • 图解【提示工程 VS 微调 VS RAG、全量微调 VS LoRA微调、TopK VS TopP】截图笔记
  • IGBT的损耗性分析
  • deepseek自动化代码生成
  • 知识图谱-学习计划
  • C语言 / C#改造冒泡排序函数bubble_sort()
  • 【练习】【回溯No.1】力扣 77. 组合
  • Windows 上编译 mebedtls 的鸿蒙库