【WebGL】attribute方式实例化绘制

news2025/2/23 20:24:08

背景

一般有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.coloradmin.cn/o/2304067.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

光明谷推出AT指令版本的蓝牙音箱SOC 开启便捷智能音频开发新体验

前言 在蓝牙音箱市场竞争日益激烈的当下&#xff0c;开发一款性能卓越且易于上手的蓝牙音箱&#xff0c;成为众多厂商追求的目标。而光明谷科技有限公司推出的 AT 指令版本的蓝牙音箱 SOC&#xff0c;无疑为行业带来了全新的解决方案&#xff0c;以其诸多独特卖点&#xff0c;迅…

TIP: Flex-DLD

Article: Flex-DLD: Deep Low-Rank Decomposition Model With Flexible Priors for Hyperspectral Image Denoising and Restoration, 2024 TIP. 文章的主要思想是用network来学low-rank decomposition的两个matrix&#xff08;input是random input&#xff09;. 文章的framew…

MFC开发:如何创建第一个MFC应用程序

文章目录 一、概述二、MFC 的主要组件三、创建一个MFC窗口四、控件绑定消息函数 一、概述 MFC 是微软提供的一个 C 类库&#xff0c;用于简化 Windows 应用程序的开发。它封装了 Windows API&#xff0c;提供面向对象的接口&#xff0c;帮助开发者更高效地创建图形用户界面&am…

Java与C语言中取模运算符%的区别对比

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: Java 文章目录 &#x1f4af;前言&#x1f4af;C语言中的取模运算符 %基本行为示例 注意事项示例&#xff1a;负数取模 &#x1f4af;Java中的取模运算符 %基本行为示例 对浮点数的支持示例&#xff1a;浮点数取模 符…

Zabbix 7.2实操指南:基于OpenEuler系统安装Zabbix 7.2

原文出处&#xff1a;乐维社区 部署环境 openEuler 22.03 LTS PHP 8.0 Apache Mysql 8.0 MySQL数据库 6.0 以上版本需要安装mysql8.0以上版本的数据库&#xff08;以mysql为例子&#xff09;。 欧拉系统自带 mysql8.0 的源&#xff0c;无需要安装额外的源。 安装mysql …

Win11 24h2 不能正常使用ensp的问题(已解决)

因为Win11 24h2的内核大小更改&#xff0c;目前virtualbox在7.1.4中更新解决了。所以Win11 24H2系统版本无法使用 5.x.xx的virtualbox版本&#xff0c;virtualbox对于这个5.x.xx版本早已停止维护&#xff0c;所以这个以后不会有调整。 对应的报错代码是 virtualbox错误代码&…

蓝桥杯——按键

一&#xff1a;按键得原理图 二&#xff1a;按键的代码配置 step1 按键原理图对应引脚配置为输入状态 step2 在GPIO中将对应引脚设置为上拉模式 step3 在fun.c中写按键扫描函数 写完后的扫描函数需放在主函数中不断扫描 扫描函数主要通过两个定义变量的值来判断&#xf…

Linux环境基础开发工具的使用(三)

五、Linux项目自动化构建工具-make/Makefile make&#xff1a;是一条指令。 makefile&#xff1a;是一个当前目录下的文件。 第一行&#xff1a;依赖关系。 第二行&#xff1a;依赖方法。 clean是空依赖关系。 编译文件清理 背景 会不会写makefile&#xff0c;从一个侧面说…

electron提升软件运行权限,以管理员权限运行

大家有任何想法&#xff0c;都可以联系博主沟通。 本系列为实战文章&#xff0c;最终实现的桌面工具软件&#xff0c;获取方式&#xff1a;百度网盘地址&#xff1a;https://pan.baidu.com/s/1yrl0jYpti7QCn8CHBRT2lw?pwd1234 正文开始 前言一、提升electron运行权限的三种方…

安科瑞能源物联网平台助力企业实现绿色低碳转型

安科瑞顾强 随着全球能源结构的转型和“双碳”目标的推进&#xff0c;能源管理正朝着智能化、数字化的方向快速发展。安科瑞电气股份有限公司推出的微电网智慧能源管理平台&#xff08;EMS 3.0&#xff09;&#xff0c;正是这一趋势下的创新解决方案。该平台集成了物联网&…

Spring Boot 中使用 @Transactional 注解配置事务管理

事务管理是应用系统开发中必不可少的一部分。Spring 为事务管理提供了丰富的功能支持。Spring 事务管理分为编程式和声明式的两种方式。编程式事务指的是通过编码方式实现事务&#xff1b;声明式事务基于 AOP,将具体业务逻辑与事务处理解耦。声明式事务管理使业务代码逻辑不受污…

动态链接器(九):.init和.init_array

ELF文件中的.init和.init_array段是程序初始化阶段的重要组成部分&#xff0c;用于在main函数执行前完成必要的初始化操作。 1 .init段和.init_array 段 1.1 作用 .init段包含编译器生成的初始化代码&#xff0c;通常由运行时环境&#xff08;如C标准库的启动例程&#xff0…

RT-Thread+STM32L475VET6——TF 卡文件系统

文章目录 前言一、板载资源二、具体步骤1.打开CubeMX进行USB配置1.1 使用外部高速时钟&#xff0c;并修改时钟树1.2 打开SPI1&#xff0c;参数默认即可(SPI根据自己需求调整&#xff09;1.3 打开串口&#xff0c;参数默认1.4 生成工程 2.配置SPI2.1 打开SPI驱动2.2 声明使用SPI…

[论文解析]OmniRe: Omni Urban Scene Reconstruction

OmniRe: Omni Urban Scene Reconstruction 论文地址&#xff1a;https://arxiv.org/abs/2408.16760 代码地址&#xff1a;https://github.com/ziyc/drivestudio 项目地址&#xff1a;https://ziyc.github.io/omnire/ 论文解读 总结 这篇论文代表了一种重建的方向&#xff0…

【微服务优化】ELK日志聚合与查询性能提升实战指南

网罗开发 &#xff08;小红书、快手、视频号同名&#xff09; 大家好&#xff0c;我是 展菲&#xff0c;目前在上市企业从事人工智能项目研发管理工作&#xff0c;平时热衷于分享各种编程领域的软硬技能知识以及前沿技术&#xff0c;包括iOS、前端、Harmony OS、Java、Python等…

Docker实战-使用docker compose搭建博客

docker run 部署 创建blog网络 [rootk8s-master ~]# docker network create blog 8f533a5a1ec65eae3f98c0ae5a76014a3ab1bf3c087ad952cdc100cc7a658948 [rootk8s-master ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 8f533a5a1ec6 blog bridge …

【JT/T 808协议】808 协议开发笔记 ② ( 终端注册 | 终端注册应答 | 字符编码转换网站 )

文章目录 一、消息头 数据1、消息头拼接2、消息 ID 字段3、消息体属性 字段4、终端手机号 字段5、终端流水号 字段 二、消息体 数据三、校验码计算四、最终计算结果五、终端注册应答1、分解终端应答数据2、终端应答 消息体 数据 六、字符编码转换网站 一、消息头 数据 1、消息头…

51单片机学习之旅——定时器

打开软件 1与其它等于其它&#xff0c;0与其它等于0 1或其它等于1&#xff0c;0或其它等于其它 TMODTMOD&0xF0;//0xF01111 0000进行与操作&#xff0c;高四位保持&#xff0c;低四位清零&#xff0c;高四位定时器1&#xff0c;低四位定时器0 TMODTMOD|0x01;//0x010000 0…

hot100_139. 单词拆分

hot100_139. 单词拆分 思路 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true。 注意&#xff1a;不要求字典中出现的单词全部都使用&#xff0c;并且字典中的单词可以重复使用。 示例 1&#xff1a; 输入:…

SQLMesh 系列教程7- 详解 seed 模型

SQLMesh 是一个强大的数据建模和管道管理工具&#xff0c;允许用户通过 SQL 语句定义数据模型并进行版本控制。Seed 模型是 SQLMesh 中的一种特殊模型&#xff0c;主要用于初始化和填充基础数据集。它通常包含静态数据&#xff0c;如参考数据和配置数据&#xff0c;旨在为后续的…