WebGL系列教程 — 绘制点(版本2、版本3、版本4、版本5)

news2025/1/19 17:17:19

目录

0.WebGL简介:

绘制一个点

绘制一个点(版本2)

绘制一个点(版本3)

绘制一个点(版本4)

实现

绘制一个点(版本5)


0.WebGL简介:

WebGL是一种用于在Web浏览器中绘制3D图形的API,它允许开发者使用JavaScript和OpenGL ES 2.0来创建高性能的3D图形应用程序。WebGL是HTML5标准的一部分,可以在所有现代的桌面和移动浏览器中运行,包括Chrome、Firefox、Safari、Edge和Opera。

WebGL提供了一种在Web浏览器中创建交互式3D图形的方法。它是一个基于着色器的渲染引擎,可以直接在Web浏览器中渲染3D图形,而无需使用任何插件或附加组件。WebGL使用JavaScript和OpenGL ES 2.0 API来提供高性能的图形渲染,可以在各种设备上使用,包括PC、移动设备和智能电视等。

WebGL是建立在HTML5 Canvas元素上的。Canvas元素允许开发者使用JavaScript在浏览器中绘制2D图形。WebGL使用Canvas元素作为其3D渲染的目标,通过将3D渲染结果绘制到Canvas元素上来呈现3D场景。WebGL还提供了许多高级功能,如光照、阴影、纹理映射和反射等,以帮助开发者创建更加逼真的3D图形应用程序。

WebGL开发需要具备一定的数学和图形学知识,熟悉OpenGL ES 2.0的API和着色器编程。开发者需要编写自己的着色器程序来控制渲染流程和图形效果。WebGL的API非常强大,可以创建复杂的3D模型和场景,但同时也需要处理复杂的渲染管道和硬件兼容性问题。

总之,WebGL为Web开发者提供了一种在浏览器中创建高性能3D图形应用程序的方法。它使用JavaScript和OpenGL ES 2.0 API来提供强大的3D渲染能力,可以在各种设备上运行,包括PC、移动设备和智能电视等。开发者需要具备一定的数学和图形学知识,并熟悉OpenGL ES 2.0 API和着色器编程。

绘制一个点

我们初步认识了 webgl,本篇主要围绕绘制一个点的示例,逐步实现下面功能:

  1. 点的位置从 js 传入着色器
  2. 点的大小由 js 传入着色器
  3. 通过鼠标点击绘点
  4. 通过鼠标点击绘点,并改变点的颜色

绘制一个点(版本2)

需求

在上篇中我们在canvas中心绘制了一个点(效果如下),但这点的位置是直接写在顶点着色器中 gl_Position = vec4(0.0, 0.0, 0.0, 1.0);

需求:点的位置从 js 传入着色器

思路

将位置信息从 js 传入着色器有两种方式:attribute 变量、uniform 变量

  • attribute - 传输的是那些与顶点相关的数据
  • uniform - 传输的是那些对于所有顶点都相同的数据

 

 

我们这里使用 attribute 变量,因为每个点都有各自的坐标

Tip:GLSL 中有三种类型的“变量”或者说数据存储类型。每一种类型都有特定的目标和使用方法:: attributes、varyings(复杂,暂不介绍)和uniforms —— Data in WebGL

attribute

attribute 是一种着色器语言(GLSL ES)变量,用于向顶点着色器内传输数据,只有顶点着色器能使用它。

使用 attribute 有如下三步:

  1. 在顶点着色器中声明 attribute 变量
  2. 在顶点着色器中将声明的 attribute 变量赋值给 gl_Position 变量
  3. 向 attribute 变量传输数据

实现

完整代码如下:

// point02.js
// 必须要 ;
const VSHADER_SOURCE = `
// 声明 attribute 变量
attribute vec4 a_Position;
void main() {
  // 将声明的 attribute 变量赋值给 gl_Position 变量
  gl_Position = a_Position;
  gl_PointSize = 10.0;               
}
`

const FSHADER_SOURCE = `
  void main() {
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); 
  }
`

function main() {
  const canvas = document.getElementById('webgl');

  const gl = canvas.getContext("webgl");

  // 初始化着色器
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('初始化着色器失败');
    return;
  }

  // 获取 attribute 变量的存储位置。如果找不到该属性则返回 -1。
  const a_Position = gl.getAttribLocation(gl.program, 'a_Position')
  if (a_Position < 0) {
    console.log('找不到 a_Position 的存储位置');
    return;
  }
  // 为顶点 attibute 变量赋值
  gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0);


  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.clear(gl.COLOR_BUFFER_BIT); 

  gl.drawArrays(gl.POINTS, 0, 1);
}

Tip:习惯:所有 attribute 的变量以 a_ 开头,所有 uniform 的变量以 u_ 开头。

getAttribLocation

WebGLRenderingContext.getAttribLocation(program, name) 方法返回了给定 WebGLProgram对象 中某属性的下标指向位置。

vertexAttrib3f

vertexAttrib3f 是一系列同族方法中的一个,为顶点 attibute 变量赋值。

一系列方法指:

void gl.vertexAttrib1f(index, v0);
void gl.vertexAttrib2f(index, v0, v1);
// 将数据(v0, v1, v2) 传给 index 指定的 attribute 变量
void gl.vertexAttrib3f(index, v0, v1, v2);
void gl.vertexAttrib4f(index, v0, v1, v2, v3);

// 矢量版本 v(vector)
void gl.vertexAttrib1fv(index, value);
void gl.vertexAttrib2fv(index, value);
void gl.vertexAttrib3fv(index, value);
void gl.vertexAttrib4fv(index, value);
  • gl.vertexAttrib1f 传1个分量。第二第三分量设置为 0.0,第四个分量设置为1.0
  • gl.vertexAttrib3f 传3个分量。第四个分量设置为 1.0,以此类推。

矢量版本以 v 结尾,接受类型化数组。就像这样:

const floatArray = new Float32Array([10.0, 5.0, 2.0]);
gl.vertexAttrib3fv(a_foobar, floatArray);

绘制一个点(版本3)

需求

需求:点的大小由 js 传入着色器 —— 在版本2的基础上实现

实现

和版本2的实现类似:

const VSHADER_SOURCE = `
 attribute vec4 a_Position;
+attribute float a_PointSize;
 void main() {
   // 将声明的 attribute 变量赋值给 gl_Position 变量
   gl_Position = a_Position;
-  gl_PointSize = 10.0;
+  gl_PointSize = a_PointSize;
 }
 `

function main() {
   gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0);

+  const a_PointSize = gl.getAttribLocation(gl.program, 'a_PointSize')
+  if (a_PointSize < 0) {
+    console.log('找不到 a_PointSize 的存储位置');
+    return;
+  }
+  // 为顶点 attibute 变量赋值
+  gl.vertexAttrib1f(a_PointSize, 10.0);

   gl.clearColor(0.0, 0.0, 0.0, 1.0);
   gl.clear(gl.COLOR_BUFFER_BIT);

绘制一个点(版本4)

需求

需求:通过鼠标点击绘点

效果如下:

实现

完整代码如下:

 

const VSHADER_SOURCE = `
// 声明 attribute 变量
attribute vec4 a_Position;
attribute float a_PointSize;
void main() {
  // 将声明的 attribute 变量赋值给 gl_Position 变量
  gl_Position = a_Position;
  gl_PointSize = a_PointSize;               
}
`

const FSHADER_SOURCE = `
  void main() {
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); 
  }
`

function main() {
  const canvas = document.getElementById('webgl');

  const gl = canvas.getContext("webgl");

  // 初始化着色器
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('初始化着色器失败');
    return;
  }

  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.clear(gl.COLOR_BUFFER_BIT);

  const a_PointSize = gl.getAttribLocation(gl.program, 'a_PointSize')
  if (a_PointSize < 0) {
    console.log('找不到 a_PointSize 的存储位置');
    return;
  }
  // 为顶点 attibute 变量赋值
  gl.vertexAttrib1f(a_PointSize, 10.0);

  // 获取 attribute 变量的存储位置。如果找不到该属性则返回 -1。
  const a_Position = gl.getAttribLocation(gl.program, 'a_Position')
  if (a_Position < 0) {
    console.log('找不到 a_Position 的存储位置');
    return;
  }

  // 存储所有点
  const points = [];

  // 注册点击事件
  $(canvas).click(event => {
    // 将点击的坐标转为 webgl 坐标系统。
    const rect = event.target.getBoundingClientRect();
    const x = ((event.clientX - rect.left) - canvas.width / 2) / (canvas.width / 2);
    const y = (canvas.height / 2 - (event.clientY - rect.top)) / (canvas.height / 2);
    console.log(x, y)
    // 将点保存
    points.push({ x, y })
    // 注:使用预设值来清空缓冲。如果注释这行,颜色缓冲区会被 webgl 重置为默认的透明色(0.0, 0.0, 0.0, 0.0) —— 必须
    gl.clear(gl.COLOR_BUFFER_BIT);
    // 绘点
    points.forEach(item => {
      // 为顶点 attibute 变量赋值
      // 注:即使 x, y 是整数程序也没问题
      gl.vertexAttrib3f(a_Position, item.x, item.y, 0.0);
      gl.drawArrays(gl.POINTS, 0, 1);
    })
  })
}

核心思路如下:

  • 给 canvas 注册点击事件(这里引入jQuery)
  • 点击时将 (x, y) 转为 webgl 坐标(怎么转换?百度搜索,这不是重点),并将该点存入一个变量 points 中
  • 循环 points 不停绘制点

:循环前得通过 gl.clear 清空绘图区,否则canvas背景会被重置为透明色。

绘制一个点(版本5)

需求

需求:通过鼠标点击绘点,并改变点的颜色 —— 在版本4的基础上实现

效果如下:

思路:颜色从硬编码改成从 js 传入。使用 uniform 变量。

uniform

前面我们用 attribute 向顶点着色器传输顶点的位置,只有顶点着色器才能使用 attribute。

对于片元着色器,需要使用 uniform 变量。

使用 uniform 有如下三步(和 attribute 类似):

  1. 在顶点着色器中声明 uniform 变量
  2. 在顶点着色器中将声明的 uniform 变量赋值给 gl_FragColor 变量
  3. 向 uniform 变量传输数据

实现

完整代码:

const VSHADER_SOURCE = `
attribute vec4 a_Position;
attribute float a_PointSize;
void main() {
  gl_Position = a_Position;
  gl_PointSize = a_PointSize;               
}
`

const FSHADER_SOURCE = `
// 片元着色器必须加上精度描述。否则浏览器报错:No precision specified for (float)
precision mediump float;
// 声明变量
uniform vec4 u_FragColor;
void main() {
  gl_FragColor = u_FragColor;
}
`

function main() {
  const canvas = document.getElementById('webgl');

  const gl = canvas.getContext("webgl");

  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('初始化着色器失败');
    return;
  }

  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.clear(gl.COLOR_BUFFER_BIT);

  const a_PointSize = gl.getAttribLocation(gl.program, 'a_PointSize')
  if (a_PointSize < 0) {
    console.log('找不到 a_PointSize 的存储位置');
    return;
  }
  gl.vertexAttrib1f(a_PointSize, 10.0);

  const a_Position = gl.getAttribLocation(gl.program, 'a_Position')
  if (a_Position < 0) {
    console.log('找不到 a_Position 的存储位置');
    return;
  }

  // 获取 uniform 变量的存储位置。如果找不到该属性则返回 -1。
  const u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor')
  if (u_FragColor < 0) {
    console.log('找不到 u_FragColor 的存储位置');
    return;
  }

  const points = [];

  // [0, 1)
  const getColor = () => Number.parseFloat(Math.random())
  
  $(canvas).click(event => {
    const rect = event.target.getBoundingClientRect();
    const x = ((event.clientX - rect.left) - canvas.width / 2) / (canvas.width / 2);
    const y = (canvas.height / 2 - (event.clientY - rect.top)) / (canvas.height / 2);
    
    // 将点的随机颜色
    points.push({ x, y, rgb: [getColor(), getColor(), getColor()] })
    gl.clear(gl.COLOR_BUFFER_BIT);
    points.forEach(item => {
      gl.vertexAttrib3f(a_Position, item.x, item.y, 0.0);
      // 给 unifrom 变量赋值
      gl.uniform4f(u_FragColor, ...item.rgb, 1.0);
      gl.drawArrays(gl.POINTS, 0, 1);
    })
  })
}

 

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

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

相关文章

Linux学习第六节-Facl访问控制列表

1.概念 FACL&#xff08;Filesystemctl Access Control List &#xff09; 文件系统访问控制列表&#xff1a;利用文件扩展属性保存额外的访问控制权限&#xff0c;单独为每一个用户量身定制一个权限。 命令格式&#xff1a; setfacl [选项] 归属关系:用户名:权限 目录 常用选…

【数据结构起航】:衡量算法的好坏--时间空间复杂度

时间复杂度和空间复杂度 文章目录时间复杂度和空间复杂度1.算法效率1.1算法复杂度1.2复杂度在OJ里的应用2.时间复杂度2.1时间复杂度的概率2.2大O渐进表示法推导大O阶方法&#xff1a;2.3时间复杂度的举例计算3.空间复杂度3.1空间复杂度的举例计算4.复杂度各量级对比1.算法效率 …

ECharts数据可视化库--导入EChars库

目录 一.ECharts基本认识 二.EChars库导入 1.下载echart.js文件 2.在IDEA引入eharts.js 三.数据可视化绘制 最快乐的学习就是从繁入简&#xff0c;虽然此文章比较简短&#xff0c;但花最少的时间学最多的技能是真的香&#xff0c;点个关注吧&#xff01;这个是数据可视化的…

Kubernetes三 Kubernetes之Pod控制器与service详解

KubernetesPod详解 一 Pod控制器详解 1.1 Pod控制器介绍 Pod是kubernetes的最小管理单元&#xff0c;在kubernetes中&#xff0c;按照pod的创建方式可以将其分为两类&#xff1a; 自主式pod&#xff08;不推荐&#xff09;&#xff1a;kubernetes直接创建出来的Pod&#xf…

MDK Keil查看map文件及编译占用内存信息、函数入口地址

一、在哪里打开map文件查看&#xff08;1&#xff09;第一种&#xff0c;在keil软件下&#xff0c;双击你的工程名&#xff0c;map就会弹出&#xff08;2&#xff09;第二种&#xff0c;点击魔术棒&#xff0c;查看 Lis文件输出的位置入口我的在output...文件下&#xff0c;那我…

努力构建15分钟听力圈,腾讯天籁行动助力听障老人更快融入数字社会

3月3日&#xff0c;腾讯与北京听力协会联合举办线上技术研讨会&#xff0c;以“AI助听技术发展与应用创新”为主题&#xff0c;汇聚国内从事AI助听、辅听相关理论研究及前沿技术落地的专家学者&#xff0c;共同探讨当前人工智能在听力健康领域的研究热点和实践成果。会上&#…

得物供应链复杂业务实时数仓建设之路

1 背景 得物供应链业务是纷繁复杂的&#xff0c;我们既有 JIT 的现货模式中间夹着这大量的仓库作业环节&#xff0c;又有到仓的寄售&#xff0c;品牌业务&#xff0c;有非常复杂的逆向链路。在这么复杂的业务背后&#xff0c;我们需要精细化关注人货场车的效率和成本&#xff…

Malware Dev 03 - 隐匿之 Command Line Spoofing 原理解析

写在最前 如果你是信息安全爱好者&#xff0c;如果你想考一些证书来提升自己的能力&#xff0c;那么欢迎大家来我的 Discord 频道 Northern Bay。邀请链接在这里&#xff1a; https://discord.gg/9XvvuFq9Wb我拥有 OSCP&#xff0c;OSEP&#xff0c;OSWE&#xff0c;OSED&…

浅分析BIG-建筑展示系统

一、主页&#xff08;主要界面&#xff09;重点疑点&#xff08;需要解决&#xff09;1.云平台实时同步。是否可以电脑与hololens2同步或链接&#xff1f;并可以传输信息提醒&#xff1f;一级界面&#xff08;启动界面&#xff09;1.交互式启动激活效果&#xff08;触发按钮旋转…

TCP协议三次握手的原因是什么?为什么不用两次握手和4次握手?

今天复习了TCP协议的三次握手&#xff0c;对上一篇C网络编程有了更深的理解。当时考研的时候计网学过&#xff0c;这里再总结一下分享。网图都是截图来的&#xff0c;侵删。TCP协议属于传输层协议&#xff0c;上面的应用层协议包括HTTP、FTP之类&#xff0c;应用层协议是最接近…

Prometheus 监控云Mysql和自建Mysql(多实例)

本文您将了解到 Prometheus如何配置才能监控云Mysql(包括阿里云、腾讯云、华为云)和自建Mysql。 Prometheus 提供了很多种Exporter&#xff0c;用于监控第三方系统指标&#xff0c;如果没有提供也可以根据Exporter规范自定义Exporter。 本文将通过MySQL server exporter 来监控…

通达信波段主图指标公式,源码简洁原理却不简单

通达信波段主图指标公式的核心语句也就4句&#xff0c;后面的语句都是为了画图的。公式看起来比较简单&#xff0c;原理也比较巧妙&#xff0c;但是理解起来有些困难。 直接上源码&#xff1a; HH:HHV(H,5); LL:LLV(L,5); TH:BARSLAST(H>REF(HH,1)); TL:BARSLAST(L<REF(…

K8s(v1.25.1) 高可用集群(3 Master + 5 Node) Ansible 剧本部署(CRI使用docker,cri-docker)

写在前面 分享一个 k8s 高可用集群部署的 Ansible 剧本以及涉及到的一些工具的安装博文内容涉及&#xff1a;从零开始 一个 k8s 高可用 集群部署 Ansible剧本编写&#xff0c;编写后搭建 k8s 高可用 集群一些集群常用的 监控&#xff0c;备份工具安装&#xff0c;包括&#xff…

边缘计算:万字长文详解高通SNPE inception_v3推理实战

本文主要讲解利用高通SNPE进行神经网络推理&#xff0c;主要参考&#xff1a; 上手SNPE&#xff0d;推理inception_v3 - 知乎 文中是容器做的&#xff0c;在conda环境下做一样的&#xff0c;没问题&#xff0c;已跑通。 在anaconda环境中使用conda命令安装cuda、cudnn、tens…

数据结构与算法系列之单链表

&#x1f497; &#x1f497; 博客:小怡同学 &#x1f497; &#x1f497; 个人简介:编程小萌新 &#x1f497; &#x1f497; 如果博客对大家有用的话&#xff0c;请点赞关注再收藏 &#x1f31e; 这里写目录标题test.hSList.h注意事项一级指针与二级指针的使用assert的使用空…

内大892复试真题19年

内大892复试真题19年 1. 统计低于平均分的人数2. 输出数组中最大值3. 一元二次方程求根4. 字符串数组平移(反转法)5. 矩阵乘法(分治+strassen思想)1. 统计低于平均分的人数 问题 代码 #include <iostream>using namespace std;// 函数声明 double avgFunc

0098 Mysql01

1.登录Mysql mysql -uroot -p密码 2.Mysql常用命令 退出:exit 查看mysql有哪些数据库&#xff1a;show databases;(以分号结尾) 选择使用某个数据库&#xff1a;use sys; (表示正在使用一个名叫sys得数据库) 创建数据库&#xff1a;create database bjpowernode; 查看某个数…

FFmpeg入门 - 格式转换

1、音频分⽚(plane)与打包(packed)解码出来的AVFrame,它的data字段放的是视频像素数据或者音频的PCM裸流数据,linesize字段放的是对齐后的画面行长度或者音频的分片长度:/*** For video, size in bytes of each picture line.* For audio, size in bytes of each plane.** For …

Python3 入门教程||Python3 条件控制||Python3 循环

Python3 条件控制 if语句 Python条件语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块。 Python 中 if 语句的一般形式如下所示&#xff1a; if condition_1:statement_block_1 流程图如下所示&#xff1a; 这种if语句只有在符合条件的时候才会执行代…

华大Flash檫写导致重启异常问题

一、华大Flash写入注意事项由Flash操作说明我们可以看出重要一点&#xff0c;就是檫写Flash函数地址需要定义在32K之前&#xff08;即0x8000之前&#xff09;&#xff0c;否则将写入失败。二、先上的错误的源代码这个代码是我应用中导致硬件卡死重启的&#xff0c;其实也不算错…