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 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');

        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);

            // 固定值,画布长和宽的一半,均为200
            const 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_慕课网】

在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/368249.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【微服务】skywalking自定义链路追踪与日志采集

目录 一、前言 二、自定义链路追踪简介 2.1 自定义链路追踪应用场景 2.2 链路追踪几个关键概念 三、skywalking 自定义链路追踪实现 3.1 环境准备 3.2 集成过程 3.2.1 导入核心依赖 3.2.2 几个常用注解 3.2.3 方法集成 3.2.4 上报追踪信息 四、skywalking 自定义日志…

代码随想录 Leetcode491. 非递减子序列

题目&#xff1a; 代码(首刷看解析 2024年2月3日&#xff09;&#xff1a; class Solution { private:vector<vector<int>> res;vector<int> path; public:void backtracking(vector<int>& nums, int startIndex) {if (path.size() > 1) {res.…

HiveSQL题——collect_set()/collect_list()聚合函数

一、collect_set() /collect_list()介绍 collect_set()函数与collect_list()函数属于高级聚合函数&#xff08;行转列&#xff09;&#xff0c;将分组中的某列转换成一个数组返回&#xff0c;常与concat_ws()函数连用实现字段拼接效果。 collect_list&#xff1a;收集并形成lis…

Amazon Bedrock 知识库现可提供完全托管的 RAG 体验

Amazon Bedrock 知识库预览版已于9月问世。从今天开始&#xff0c;Amazon Bedrock 知识库正式全面发布。正式推出。 借助知识库&#xff0c;可安全地将 Amazon Bedrock 中的基础模型 (FM) 连接到贵公司的数据&#xff0c;供检索增强生成 (RAG) 使用。模型生成的响应更具相关性…

海外IP代理:解锁网络边界的实战利器

文章目录 引言&#xff1a;正文&#xff1a;一、Roxlabs全球IP代理服务概览特点&#xff1a;覆盖范围&#xff1a;住宅IP真实性&#xff1a;性价比&#xff1a;在网络数据采集中的重要性&#xff1a; 二、实战应用案例一&#xff1a;跨境电商竞品分析步骤介绍&#xff1a;代码示…

简单几步,借助Aapose.Cells将 Excel 工作表拆分为文件

近年来&#xff0c;Excel 文件已成为无数企业数据管理的支柱。然而&#xff0c;管理大型 Excel 文件可能是一项艰巨的任务&#xff0c;尤其是在高效共享和处理数据时。为了应对这一挑战&#xff0c;大型 Excel 工作簿被拆分为较小的工作簿以增强电子表格管理。Aspose提供了这样…

堪称灾难级攻击的 UDP FLOOD洪水攻击,应该如何防护?

DDOS又称为分布式拒绝服务&#xff0c;全称是Distributed Denial of Service。DDOS本是利用合理的请求造成资源过载&#xff0c;导致服务不可用&#xff0c;从而造成服务器拒绝正常流量服务。就如酒店里的房间是有固定的数量的&#xff0c;比如一个酒店有50个房间&#xff0c;当…

【Kafka】服务器Broker与Controller详解

这里写自定义目录标题 Broker概述Broker总体工作流程Broker重要参数 Controller为什么需要Controller具体作用数据服务Leader选举选举流程脑裂问题羊群效应触发leader选举 Broker 概述 Kafka服务实例&#xff0c;负责消息的持久化、中转等功能。一个独立的Kafka 服务器被就是…

AES算法:数据传输的安全保障

在当今数字化时代&#xff0c;数据安全成为了一个非常重要的问题。随着互联网的普及和信息技术的发展&#xff0c;我们需要一种可靠的加密算法来保护我们的敏感数据。Advanced Encryption Standard&#xff08;AES&#xff09;算法应运而生。本文将介绍AES算法的优缺点、解决了…

金和OA jc6 UploadFileBlock 任意文件上传漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

SpringBoot实战项目第一天

环境搭建 后端部分需要准备&#xff1a; sql数据库 创建SpringBoot工程&#xff0c;引入对应的依赖(web\mybatis\mysql驱动) 配置文件application.yml中引入mybatis的配置信息 创建包结构&#xff0c;并准备实体类 完成今日开发后项目部分内容如下图示 用户注册于登录部分…

【MongoDB】跨库跨表查询(python版)

MongoDB跨表跨库查询 1.数据准备&#xff1a;2.跨集合查询3.跨库查询应该怎么做&#xff1f; 讲一个简单的例子&#xff0c;python连接mongodb做跨表跨库查询的正确姿势 1.数据准备&#xff1a; use order_db; db.createCollection("orders"); db.orders.insertMan…

机器学习 | 如何利用集成学习提高机器学习的性能?

目录 初识集成学习 Bagging与随机森林 Otto Group Product(实操) Boosting集成原理 初识集成学习 集成学习&#xff08;Ensemble Learning&#xff09;是一种通过组合多个基本模型来提高预测准确性和泛化能力的机器学习方法。它通过将多个模型的预测结果进行整合或投票来做…

增加Vscode引用路径

增加Vscode引用路径 增加Vscode引用路径问题说明解决思路1在Vscode中进行配置缺点 解决思路2 增加Vscode引用路径 问题说明 在嵌入式开发中需要经常用到库函数(SPL), Vscode需要配置引用路径才能对函数名或变量进行跳转 解决思路1 与Keil5 MDK类似, 在配置C/C的json文件中添…

计算机网络_1.6.2 计算机网络体系结构分层的必要性

1.6.2 计算机网络体系结构分层的必要性 一、五层原理体系结构每层各自主要解决什么问题1、物理层2、数据链路层3、网络层4、运输层5、应用层 二、总结三、练习 笔记来源&#xff1a; B站 《深入浅出计算机网络》课程 本节主要介绍实现计算机网络需要解决哪些问题&#xff1f;以…

【Crypto | CTF】BUUCTF 大帝的密码武器1

天命&#xff1a;这题真的是来刷经验的&#xff0c;有点吐血 首先这题是贼简单&#xff0c;但我居然跪到了&#xff0c;所以特此写这一篇来惩戒自己心太大 拿到文件&#xff0c;文件写着zip&#xff0c;改成zip后缀名即可&#xff0c;也不算啥难的 打开里面的两份文件&#x…

NLP入门系列—Attention 机制

NLP入门系列—Attention 机制 Attention 正在被越来越广泛的得到应用。尤其是 [BERT]火爆了之后。 Attention 到底有什么特别之处&#xff1f;他的原理和本质是什么&#xff1f;Attention都有哪些类型&#xff1f;本文将详细讲解Attention的方方面面。 Attention 的本质是什…

LabVIEW传感器通用实验平台

LabVIEW传感器通用实验平台 介绍了基于LabVIEW的传感器实验平台的开发。该平台利用LabVIEW图形化编程语言和多参量数据采集卡&#xff0c;提供了一个交互性好、可扩充性强、使用灵活方便的传感器技术实验环境。 系统由硬件和软件两部分组成。硬件部分主要包括多通道数据采集卡…

MySQL 安装配置 windows

一、下载 去官网MySQL :: MySQL Downloads 下载社区版 然后根据自己的系统选择 直接下载。 二、安装 点击安装程序 这边看样子缺少东西。 去这边下载 Latest supported Visual C Redistributable downloads | Microsoft Learn 然后再一次安装mysql 三、配置 安装完成后&a…

Vue3_基础使用_2

这节主要介绍&#xff1a;标签和组件的ref属性&#xff0c;父子组件间的传递值&#xff0c;ts的接口定义&#xff0c;vue3的生命周期 1.标签的ref属性。 1.1ref属性就是给标签打标识用的&#xff0c;相当于html的id&#xff0c;但是在vue3中用id可能会乱&#xff0c;下面是ref…