WebGL+Three.js入门与实战——绘制水平移动的点、通过鼠标控制绘制(点击绘制、移动绘制、模拟画笔)

本文主要是介绍WebGL+Three.js入门与实战——绘制水平移动的点、通过鼠标控制绘制(点击绘制、移动绘制、模拟画笔),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

个人简介

👀个人主页: 前端杂货铺
🙋‍♂️学习方向: 主攻前端方向,正逐渐往全干发展
📃个人状态: 研发工程师,现效力于中国工业软件事业
🚀人生格言: 积跬步至千里,积小流成江海
🥇推荐学习:🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2/3项目实战 🥝Node.js🍒Three.js🍖数据结构与算法体系教程

🌕个人推广:每篇文章最下方都有加入方式,旨在交流学习&资源分享,快加入进来吧

文章目录

    • 前言
    • 一、绘制一个水平移动的点(attribute)
    • 二、通过鼠标控制绘制
      • 1、鼠标点击绘制点
      • 2、鼠标移动绘制点
      • 3、模拟画笔
    • 总结

前言

大家好,这里是前端杂货铺

上一篇文章,我们学习了如何给画布换颜色、如何绘制一个点并且了解了三维坐标系…

接下来,继续我们 WebGL 内容的学习!

鼠标绘制的三种效果


一、绘制一个水平移动的点(attribute)

我们在着色器源程序中声明 attribute 变量,js 可以给这个变量传值,再绘制出来,从而就可以实现动态修改点的位置。

以下是 声明 attribute 变量,需要注意的是,attribute 只能在顶点着色器中使用,不能在片元着色器中使用。

// attribute: 存储限定符; vec4: 类型; aPosition: 变量名;
attribute vec4 aPosition;

以下是 获取 attribute 变量,需要注意的是 获取 attribute 变量需要在 initShader 函数之后,因为会用到 program 这个程序对象。获取之后返回变量的存储地址

// program: 程序对象; 'aPosition': 指定想要获取存储地址的 attribute 变量的名称
const aPositon = gl.getAttribLocation(program, 'aPosition');

以下是 给 attribute 变量赋值,可以传递 1/2/3 个分量的值,没有传递的值为 0.0。

// location: 指定 attribute 变量的存储位置; v0, v1, v2, v3: 传入的分量值; 
gl.vertexAttrib1f(location, v0);
gl.vertexAttrib2f(location, v0, v1);
gl.vertexAttrib3f(location, v0, v1, v2);
gl.vertexAttrib4f(location, v0, v1, v2, v3);

绘制流程如下:

在这里插入图片描述

基于以上知识点,我们使用 attribute 变量绘制一个水平移动的点

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="./index.js"></script>
</head><body><canvas id="canvas" width="400" height="400" style="background: gray;">此浏览器不支持canvas</canvas><script>const ctx = document.getElementById('canvas');const gl = ctx.getContext('webgl');// 着色器// 创建着色器源码// 顶点着色器const VERTEX_SHADER_SOURCE = `// 存储限定符 类型 变量名 分号 (注: attribute 只传递顶点数据)attribute vec4 aPosition;void main() {gl_Position = aPosition; // vec4(0.0,0.0,0.0,1.0)gl_PointSize = 30.0;}`;// 片元着色器const FRAGMENT_SHADER_SOURCE = `void main() {// r g b agl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);}`const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE);// 获取 attribute 变量,返回变量的存储地址const aPosition = gl.getAttribLocation(program, 'aPosition');let x = 0;setInterval(() => {x += 0.1;if (x > 1.0) {x = 0;}// 给 attribute 变量赋值gl.vertexAttrib1f(aPosition, x);// 执行绘制gl.drawArrays(gl.POINTS, 0, 1);}, 200)</script>
</body></html>

attribute绘制一个水平移动的点


二、通过鼠标控制绘制

1、鼠标点击绘制点

接下来,我们通过鼠标来控制在画布上绘制点。

  • 屏幕坐标通过 event.clickXevent.clickY 来获取
  • 画布边距通过 event.target.getBoundingClientRect() 来获取,其 .left 和 .right 等同于 ctx.offsetWidth 和 ctx.offsetHeight
  • 画布的坐标通过 屏幕坐标 - 画布边距 获取
  • 转为 ndc 坐标:设画布的长和宽均为 400px,那么原点为 0,最左为 -200px,最右为 200px。想要转为原点为 0,最左为 -1,最右为 1,就可以均除以 200。最上和最下同理。

在这里插入图片描述

示例:我们点击画布的左上角的位置,查看控制台输出的坐标,如下

在这里插入图片描述

在这里插入图片描述

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="./index.js"></script><style>* {margin: 0;padding: 0;}canvas {margin: 50px auto 0;display: block;background: yellow;}</style>
</head><body><canvas id="canvas" width="400" height="400">此浏览器不支持canvas</canvas><script>const ctx = document.getElementById('canvas');const gl = ctx.getContext('webgl');// 着色器// 创建着色器源码// 顶点着色器const VERTEX_SHADER_SOURCE = `// 存储限定符 类型 变量名 分号 (注: attribute 只传递顶点数据)attribute vec4 aPosition;void main() {gl_Position = aPosition; // vec4(0.0,0.0,0.0,1.0)gl_PointSize = 10.0; // 点的大小}`;// 片元着色器const FRAGMENT_SHADER_SOURCE = `void main() {// r g b a 绘制颜色gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);}`const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE);// 获取 attribute 变量,返回变量的存储地址const aPosition = gl.getAttribLocation(program, 'aPosition');ctx.onclick = function (event) {// 坐标const x = event.clientX;const y = event.clientY;console.log("鼠标点击的屏幕坐标:", x, y);// 获取边距 (上边距和左边距) domPosition.left 等同于 ctx.offsetLeft 的值const domPosition = event.target.getBoundingClientRect();console.log("画布边距:", domPosition.left, domPosition.top);// 点击的点位于画布上方 、 左侧的距离 (domPosition.left: 568(基于我显示屏的长度), domPosition.top: 50)const domx = x - domPosition.left;const domy = y - domPosition.top;console.log("画布的坐标:", domx, domy);// 固定值,画布长和宽的一半,均为200const halfWidth = ctx.offsetWidth / 2;const halfHeight = ctx.offsetHeight / 2;console.log("画布长和宽的一半:", halfWidth, halfHeight);// 转为 ndc坐标 (-1, 1)const clickX = (domx - halfWidth) / halfWidth;const clickY = (halfHeight - domy) / halfHeight;console.log("转为ndc的坐标:", clickX, clickY);// 给 attribute 变量赋值gl.vertexAttrib2f(aPosition, clickX, clickY);// 执行绘制gl.drawArrays(gl.POINTS, 0, 1);}</script>
</body>
</html>

通过鼠标点击绘制点


2、鼠标移动绘制点

改为 ctx.onmousemove 实现鼠标移动:

绘制点跟随鼠标


3、模拟画笔

改为 ctx.onmousemove 的基础上,在 ctx.onmousemove 之前定义一个存储点的数组 points

const points = [];

把以下内容替换掉,从而实现画笔效果:

    // 给 attribute 变量赋值// gl.vertexAttrib2f(aPosition, clickX, clickY);// 执行绘制// gl.drawArrays(gl.POINTS, 0, 1);for (let i = 0; i < points.length; i++) {gl.vertexAttrib2f(aPosition, points[i].clickX, points[i].clickY);gl.drawArrays(gl.POINTS, 0, 1);}

画笔


总结

本文,我们首先实现了绘制水平移动的点,之后通过坐标的转移,认识了 ndc 坐标的求解,从而实现了鼠标控制绘制,(包括点击绘制、移动绘制和模拟画布)的效果。

更多 WebGL 和 Three.js 内容正在更新中…

好啦,本篇文章到这里就要和大家说再见啦,祝你这篇文章阅读愉快,你下篇文章的阅读愉快留着我下篇文章再祝!


参考资料:

  1. 百度百科 · WebGL
  2. WebGL + Three.js入门与实战【作者:yancy_慕课网】

在这里插入图片描述


这篇关于WebGL+Three.js入门与实战——绘制水平移动的点、通过鼠标控制绘制(点击绘制、移动绘制、模拟画笔)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/675345

相关文章

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis

MyBatis 动态 SQL 优化之标签的实战与技巧(常见用法)

《MyBatis动态SQL优化之标签的实战与技巧(常见用法)》本文通过详细的示例和实际应用场景,介绍了如何有效利用这些标签来优化MyBatis配置,提升开发效率,确保SQL的高效执行和安全性,感... 目录动态SQL详解一、动态SQL的核心概念1.1 什么是动态SQL?1.2 动态SQL的优点1.3 动态S

Pandas使用SQLite3实战

《Pandas使用SQLite3实战》本文主要介绍了Pandas使用SQLite3实战,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学... 目录1 环境准备2 从 SQLite3VlfrWQzgt 读取数据到 DataFrame基础用法:读

Python异步编程中asyncio.gather的并发控制详解

《Python异步编程中asyncio.gather的并发控制详解》在Python异步编程生态中,asyncio.gather是并发任务调度的核心工具,本文将通过实际场景和代码示例,展示如何结合信号量... 目录一、asyncio.gather的原始行为解析二、信号量控制法:给并发装上"节流阀"三、进阶控制

Python实战之屏幕录制功能的实现

《Python实战之屏幕录制功能的实现》屏幕录制,即屏幕捕获,是指将计算机屏幕上的活动记录下来,生成视频文件,本文主要为大家介绍了如何使用Python实现这一功能,希望对大家有所帮助... 目录屏幕录制原理图像捕获音频捕获编码压缩输出保存完整的屏幕录制工具高级功能实时预览增加水印多平台支持屏幕录制原理屏幕

使用DrissionPage控制360浏览器的完美解决方案

《使用DrissionPage控制360浏览器的完美解决方案》在网页自动化领域,经常遇到需要保持登录状态、保留Cookie等场景,今天要分享的方案可以完美解决这个问题:使用DrissionPage直接... 目录完整代码引言为什么要使用已有用户数据?核心代码实现1. 导入必要模块2. 关键配置(重点!)3.

最新Spring Security实战教程之Spring Security安全框架指南

《最新SpringSecurity实战教程之SpringSecurity安全框架指南》SpringSecurity是Spring生态系统中的核心组件,提供认证、授权和防护机制,以保护应用免受各种安... 目录前言什么是Spring Security?同类框架对比Spring Security典型应用场景传统

最新Spring Security实战教程之表单登录定制到处理逻辑的深度改造(最新推荐)

《最新SpringSecurity实战教程之表单登录定制到处理逻辑的深度改造(最新推荐)》本章节介绍了如何通过SpringSecurity实现从配置自定义登录页面、表单登录处理逻辑的配置,并简单模拟... 目录前言改造准备开始登录页改造自定义用户名密码登陆成功失败跳转问题自定义登出前后端分离适配方案结语前言

SpringSecurity 认证、注销、权限控制功能(注销、记住密码、自定义登入页)

《SpringSecurity认证、注销、权限控制功能(注销、记住密码、自定义登入页)》SpringSecurity是一个强大的Java框架,用于保护应用程序的安全性,它提供了一套全面的安全解决方案... 目录简介认识Spring Security“认证”(Authentication)“授权” (Auth