本教程详细介绍了如何在webgl画布上通过鼠标事件绘制单个像素。文章深入探讨了`vertexattrib2f`与`vertexattribpointer`的区别及适用场景,纠正了常见的`drawarrays`调用错误和缓冲区管理误区,并提供了完整的代码示例,帮助开发者理解webgl中javascript与gpu之间的数据通信机制。
在WebGL中实现交互式绘图,例如根据鼠标位置绘制像素,是理解JavaScript与GPU之间数据通信的关键一步。本教程将指导您如何在WebGL画布上,通过鼠标移动事件,在指定位置绘制单个像素。我们将重点讨论顶点属性的设置方式、drawArrays调用的正确使用,以及如何避免常见的缓冲区管理错误。
在WebGL中,顶点着色器通过attribute变量接收顶点数据。将数据传递给这些属性有两种主要方式:通过缓冲区(Buffer)或直接设置静态值。
当您需要绘制多个顶点,且这些顶点的数据(如位置、颜色、法线等)存储在一个数组中时,通常会使用缓冲区。
这种方法适用于绘制几何体或大量顶点数据。
当您只需要为一个属性设置一个固定的值,并且这个值在每次绘制调用中对所有顶点都相同,或者您只需要绘制一个点时,可以直接使用gl.vertexAttrib*f系列函数。
这种方法对于绘制单个点或为所有顶点设置一个统一的属性值非常高效,因为它避免了创建和管理缓冲区的开销。
在实现鼠标事件绘制像素时,开发者常遇到以下问题:
drawArrays调用参数错误:
vertexAttrib2f与vertexAttribPointer的混用:
rib2f来设置属性值。冗余的缓冲区设置:
我们将使用gl.vertexAttrib2f这种简洁高效的方法来绘制单个像素。
我们需要一个canvas元素和两个script标签来存放顶点着色器和片段着色器代码。
WebGL Mouse Draw Pixel
// 辅助函数:编译和链接着色器
function setup(ctx, vertSource, fragSource) {
const vs = ctx.createShader(ctx.VERTEX_SHADER);
ctx.shaderSource(vs, vertSource);
ctx.compileShader(vs);
if (!ctx.getShaderParameter(vs, ctx.COMPILE_STATUS)) {
console.error('Vertex shader compile error:', ctx.getShaderInfoLog(vs));
ctx.deleteShader(vs);
return null;
}
const fs = ctx.createShader(ctx.FRAGMENT_SHADER);
ctx.shaderSource(fs, fragSource);
ctx.compileShader(fs);
if (!ctx.getShaderParameter(fs, ctx.COMPILE_STATUS)) {
console.error('Fragment shader compile error:', ctx.getShaderInfoLog(fs));
ctx.deleteShader(fs);
return null;
}
const program = ctx.createProgram();
ctx.attachShader(program, vs);
ctx.attachShader(program, fs);
ctx.linkProgram(program);
if (!ctx.getProgramParameter(program, ctx.LINK_STATUS)) {
console.error('Program link error:', ctx.getProgramInfoLog(program));
ctx.deleteProgram(program);
return null;
}
return program;
}
const canvas = document.getElementById('canvas');
// 获取WebGL上下文,preserveDrawingBuffer: true 确保绘制内容在帧之间保留
const gl = canvas.getContext('webgl', { preserveDrawingBuffer: true });
if (!gl) {
alert('您的浏览器不支持WebGL!');
}
// 获取着色器源码
const vertShaderSource = document.getElementById('vert1').textContent;
const fragShaderSource = document.getElementById('frag1').textContent;
// 编译并链接着色器程序
const program = setup(gl, vertShaderSource, fragShaderSource);
if (!program) {
console.error("Failed to initialize WebGL program.");
}
gl.useProgram(program);
// 获取a_position属性的位置
const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
// **关键:禁用此属性的顶点属性数组,因为我们将使用gl.vertexAttrib2f设置静态值**
gl.disableVertexAttribArray(positionAttributeLocation);
// 获取u_resolution uniform的位置并设置画布分辨率
const resolutionUniformLocation = gl.getUniformLocation(program, 'u_resolution');
gl.uniform2f(resolutionUniformLocation, gl.canvas.width, gl.canvas.height);
// 监听鼠标移动事件
canvas.addEventListener('mousemove', (e) => {
// 获取canvas在视口中的位置和大小
const rect = canvas.getBoundingClientRect();
// 计算相对于canvas的X坐标
const x = e.clientX - rect.left;
// 计算相对于canvas的Y坐标,并进行翻转,因为WebGL的Y轴向上为正,而浏览器Y轴向下为正
const y = rect.height - (e.clientY - rect.top);
// 使用gl.vertexAttrib2f直接设置a_position属性的值
gl.vertexAttrib2f(positionAttributeLocation, x, y);
// 绘制一个点。第三个参数必须是1,因为我们只绘制一个顶点。
gl.drawArrays(gl.POINTS, 0, 1);
});
// 初始清空画布
gl.clearColor(0.0, 0.0, 0.0, 0.0); // 透明背景
gl.clear(gl.COLOR_BUFFER_BIT);顶点着色器负责将输入的像素坐标转换为WebGL的裁剪空间坐标(-1.0到+1.0)。
attribute vec2 a_position; // 接收像素坐标 (x, y)
uniform vec2 u_resolution; // 接收画布分辨率 (width, height)
void main() {
// 将像素坐标从 [0, resolution] 范围转换为 [0.0, 1.0]
vec2 zeroToOne = a_position / u_resolution;
// 转换为 [0.0, 2.0]
vec2 zeroToTwo = zeroToOne * 2.0;
// 转换为裁剪空间坐标 [-1.0, +1.0]
vec2 clipSpace = zeroToTwo - 1.0;
// 设置最终的顶点位置
gl_Position = vec4(clipSpace, 0.0, 1.0);
}片段着色器负责为每个被光栅化的像素(片段)设置颜色。
precision mediump float; // 声明浮点数精度
uniform vec4 u_color; // 尽管本例中未直接使用,但保留作为通用实践
void main() {
gl_FragColor = vec4(1,0,1,1); // 设置固定颜色为品红色 (RGBA)
}运行上述代码,当您将鼠标移动到WebGL画布上时,会在鼠标位置绘制出品红色的像素点。
注意事项:
本教程通过一个在WebGL画布上响应鼠标事件绘制单个像素的实例,详细阐述了WebGL中顶点属性的两种主要设置方式:gl.vertexAttribPointer(用于缓冲区数据)和gl.vertexAttrib2f(用于静态属性值)。我们强调了正确使用drawArrays调用以及在不同场景下选择合适的属性设置方法的重要性。通过理解这些核心概念,您将能够更有效地在WebGL中进行交互式图形编程。
# javascript
# java
# html
# js
# 浏览器
# ai
# win
# 区别
# overflow
# position属性
相关栏目:
【
Google疑问12 】
【
Facebook疑问10 】
【
网络优化76771 】
【
技术知识130152 】
【
IDC云计算60162 】
【
营销推广131313 】
【
AI优化88182 】
【
百度推广37138 】
【
网站推荐60173 】
【
精选阅读31334 】
相关推荐:
Win11怎么清理C盘下载文件夹_Win11清理下载文件夹技巧【教程】
C#如何使用XPathNavigator高效查询XML
如何在同包不同文件中正确引用 Go 结构体
c++ atoi和atof函数用法_c++字符数组转数字
Win11怎么关闭触摸键盘图标_Windows11任务栏系统托盘设置
Windows10如何删除恢复分区_Win10 Diskpart命令强制删除分区
php订单日志怎么记录发货_php记录订单发货操作日志指南【指南】
Python技术债务管理_长期维护解析【教程】
Win11开始菜单打不开_修复Windows 11点击开始图标无响应【教程】
如何在 IIS 上为 ASP.NET 6 应用排除特定目录并交由 PHP 处理
Win11怎么调整屏幕亮度_Windows 11调节显示器亮度护眼设置【步骤】
如何在Golang中捕获JSON序列化错误_Golangjson.Marshal错误处理示例
Mac上的iMovie如何剪辑视频?(新手入门教程)
PHP主流架构怎么集成Redis缓存_配置步骤【方法】
Win11怎么更改系统语言_Win11中文语言包下载与安装【指南】
php删除数据怎么清空表_truncate与delete区别及用法【汇总】
如何使用正则表达式批量替换重复的“-”模式为固定字符串
如何测试您的网站全球打开速度-网站海外测速工
Win11怎么开启游戏模式_Win11优化游戏帧数性能【教程】
Win11如何设置电源计划_Win11电源计划优化教程【攻略】
php怎么下载安装后无法解析php文件_服务器配置检查【解答】
Win11快速助手怎么用_Win11远程协助连接教程【工具】
Python函数缓存机制_lru_cache解析【指导】
Win11怎么开启移动热点_Windows11共享网络给手机设置教程
Win11怎么卸载Photos应用_Win11卸载Photos应用方法【教程】
Golang如何避免指针逃逸_Golang逃逸分析与堆栈优化策略
如何在 PHP 单元测试中正确模拟带方法的图像处理门面(Facade)
Win11怎么查看已连接wifi密码 Win11查已连wifi密码步骤【教程】
Win11怎么查看wifi信号强度_检测Windows 11无线网络质量方法【详解】
Win11怎么关闭通知中心_Windows11系统通知与专注助手设置
Windows笔记本无法进入睡眠模式怎么办?(电源疑难解答)
Linux怎么查找死循环进程_Linux系统负载分析与进程彻底结束【教程】
如何在Golang中实现RPC异步返回_Golang RPC异步处理与回调方法
php本地部署后数据库连接报错_1045accessdenied错误解决方法详解【汇总】
Win11怎么清理C盘虚拟内存_Win11清理虚拟内存设置【教程】
Win11怎么关闭搜索历史 Win11清除搜索框最近记录【隐私】
php能跑在stm32上吗_php在stm32微控制器上的移植方法【介绍】
Laravel 查询 JSON 列:高效筛选包含数组中任意值的记录
如何在Golang中使用闭包_封装变量与函数作用域
Win11怎样安装企业微信_Win11安装企业微信教程【步骤】
Windows10怎么用“讲述人”读屏辅助 Windows10轻松使用开启讲述人朗读屏幕文字帮助视障用户【教程】
Win11怎么更改任务栏颜色_Windows11个性化重音色设置
如何将竖排文本文件转换为横排字符串
如何使用Golang管理模块版本_Golanggo mod tidy与升级方法
Mac如何设置动态壁纸?(让桌面动起来)
Linux如何安装JDK11_Linux环境变量配置与Java开发环境搭建【教程】
用lighttpd能运行php吗_lighttpd配置php步骤【教程】
Win11怎么更改电脑名称_Windows 11修改计算机名操作指南【步骤】
Go 中的 := 运算符:类型推导机制与使用边界详解
c++ unordered_map怎么用 c++哈希表用法【教程】
2025-11-06
致胜网络推广营销网专注海外推广十年,是谷歌推广.Facebook广告全球合作伙伴,我们精英化的技术团队为企业提供谷歌海外推广+外贸网站建设+网站维护运营+Google SEO优化+社交营销为您提供一站式海外营销服务。