webgl 系列 —— 绘制一个点(版本2、版本3、版本4、版本5)
绘制一个点
我们初步认识了 webgl,本篇主要围绕绘制一个点的示例,逐步实现下面功能:
- 点的位置从 js 传入着色器
- 点的大小由 js 传入着色器
- 通过鼠标点击绘点
- 通过鼠标点击绘点,并改变点的颜色
绘制一个点(版本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 有如下三步:
- 在顶点着色器中声明 attribute 变量
- 在顶点着色器中将声明的 attribute 变量赋值给 gl_Position 变量
- 向 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 变量
attribute vec4 a_Position;
void main() {
// 将声明的 attribute 变量赋值给 gl_Position 变量
gl_Position = a_Position;
}
// 获取 attribute 变量的存储位置。如果找不到该属性则返回 -1。
// gl.program - 在 initShaders() 函数中创建了这个程序对象。现在只需知道它是一个参数
const a_Position = gl.getAttribLocation(gl.program, 'a_Position')
// 为顶点 attibute 变量赋值
gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0);
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 类似):
- 在顶点着色器中声明 uniform 变量
- 在顶点着色器中将声明的 uniform 变量赋值给 gl_FragColor 变量
- 向 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);
})
})
}
核心代码:
const FSHADER_SOURCE = `
// 片元着色器必须加上精度描述。否则浏览器报错:No precision specified for (float)
precision mediump float;
// 声明变量
uniform vec4 u_FragColor;
void main() {
gl_FragColor = u_FragColor;
}
`
function main() {
// 获取 uniform 变量的存储位置。如果找不到该属性则返回 -1。
const u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor')
if (u_FragColor < 0) {
console.log('找不到 u_FragColor 的存储位置');
return;
}
$(canvas).click(event => {
...
points.forEach(item => {
...
// 给 unifrom 变量赋值
gl.uniform4f(u_FragColor, ...item.rgb, 1.0);
gl.drawArrays(gl.POINTS, 0, 1);
})
})
}
注:片元着色器必须加上精度描述(例如:precision mediump float;)。否则浏览器报错:No precision specified for (float)
getUniformLocation
getUniformLocation 与 getAttribLocation 类似,只是这里返回 uniform 变量的位置
uniform4f
有了 uniform 变量的存储地址,就可以使用 uniform4f(和 vertexAttrib3f 很类似) 向变量中写入数据。
webgl 系列 —— 绘制一个点(版本2、版本3、版本4、版本5)的更多相关文章
- Atitit.播放系统规划新版本 and 最近版本回顾 v3 pbf.doc 1 版本11 (ing)41.1 规划h5本地缓存系列 41.2 Android版本app41.3 双类别系统,
Atitit.播放系统规划新版本 and 最近版本回顾 v3 pbf.doc 1 版本11 (ing)4 1.1 规划h5本地缓存系列 4 1.2 Android版本app4 1.3 双类别系统, ...
- WebGL编程指南案例解析之绘制一个点
<!DOCTYPE html> <html> <head> <title>webgl</title> <style type=&quo ...
- Java容器--2021面试题系列教程(附答案解析)--大白话解读--JavaPub版本
Java容器--2021面试题系列教程(附答案解析)--大白话解读--JavaPub版本 前言 序言 再高大上的框架,也需要扎实的基础才能玩转,高频面试问题更是基础中的高频实战要点. 适合阅读人群 J ...
- js,onblur后下一个控件获取焦点判断、html当前活跃控件、jquery版本查看、jquery查看浏览器版本、setTimeout&setInterval
需求: input控件在失去焦点后直接做验证,验证通不过的话,显示相应错误.但是如果失去焦点后点击的下个控件是比较特殊的控件(比如,退出系统),那么不执行验证操作,直接退出系统(防止在系统退出前,还显 ...
- Opentk教程系列-1绘制一个三角形
本系列教程翻译自Neo Kabuto's Blog.已经取得作者授权. 本文原文地址http://neokabuto.blogspot.com/2013/02/opentk-tutorial-1-op ...
- C#的Installer生成的msi的安装文件,安装新版本时提示:已经安装了该产品的另一个版本。无法继续安装此版本
之前折腾了个C#的项目: WLW (Windows Live Writer) Plugin–InsertSkydriveFiles 然后又弄了个对应的Installer: [已解决]给一个C#的Dll ...
- WebGL简易教程(三):绘制一个三角形(缓冲区对象)
目录 1. 概述 2. 示例:绘制三角形 1) HelloTriangle.html 2) HelloTriangle.js 3) 缓冲区对象 (1) 创建缓冲区对象(gl.createBuffer( ...
- WebGL简易教程(七):绘制一个矩形体
目录 1. 概述 2. 示例 2.1. 顶点索引绘制 2.2. MVP矩阵设置 2.2.1. 模型矩阵 2.2.2. 投影矩阵 2.2.3. 视图矩阵 2.2.4. MVP矩阵 3. 结果 4. 参考 ...
- [Modern OpenGL系列(三)]用OpenGL绘制一个三角形
本文已同步发表在CSDN:http://blog.csdn.net/wenxin2011/article/details/51347008 在上一篇文章中已经介绍了OpenGL窗口的创建.本文接着说如 ...
- OpenGl 绘制一个立方体
OpenGl 绘制一个立方体 为了绘制六个正方形,我们为每个正方形指定四个顶点,最终我们需要指定6*4=24个顶点.但是我们知道,一个立方体其实总共只有八个顶点,要指定24次,就意味着每个顶点其实重复 ...
随机推荐
- codeforce E - Binary Inversions题解
题目: 给你一个01串,现在你可以(或者不用)选取其中一个元素进行一次反转操作0-1,1-0:从而使得串中的逆序对个数最多. 题目链接:codeforce origin problem 思路: 1. ...
- AcWing340通信道路/ USACO2008 Telephone Line S
AcWing题目 洛谷题目 解题思路 首先可以得到一个很容易得到的贪心策略,将一条路径上最贵的(边权最大)的\(K\)条边删去,那么我们剩下的路径中最贵(边权最大)的路就是原本这条路径上帝\(K + ...
- AWVS漏洞扫描器的使用
前言 AWVS是一款强大的web漏洞扫描工具,扫描速度快,可针对特定的漏洞进行扫描测试,用于在按全人员对指定企业进行安全扫描以及测试人员对web应用检测漏洞. AWVS使用以及功能介绍 这里介绍的是使 ...
- vivo 游戏中心低代码平台的提效秘诀
作者:vivo 互联网服务器团队- Chen Wenyang 本文根据陈文洋老师在"2022 vivo开发者大会"现场演讲内容整理而成.公众号回复[2022 VDC]获取互联网技术 ...
- 巧用视觉障眼法,还原 3D 文字特效
最近群里有这样一个有意思的问题,大家在讨论,使用 CSS 3D 能否实现如下所示的效果: 这里的核心难点在于,如何利用 CSS 实现一个立体的数字?CSS 能做到吗? 不是特别好实现,但是,如果仅仅只 ...
- Git操作不规范,战友提刀来相见!
年终奖都没了,还要扣我绩效,门都没有,哈哈. 这波骚Git操作我也是第一次用,担心闪了腰,所以不仅做了备份,也做了笔记,分享给大家. 文末留言,聊聊你的年终奖. 问题描述 小A和我在同时开发一个功能模 ...
- python之路46 django request对象 form表单 pycharm连接数据库 ORM简介
静态文件配置 1.编写一个用户登录页面 2.静态文件 不怎么经常变化的文件 主要针对html文件所使用的到的各种资源 css文件.js文件.img文件.第三方框架文件 django针对静态文件资源需要 ...
- C++ 一种交换两个数的思路
在 Lua 或者 Python 中可以使用多值赋值语句来交换两个数.例如:a, b = b, a.在 C++ 中有没有类似的操作? 先解析一下多值赋值的原理,a, b = b, a 等价于 t1, t ...
- Hugging News #0106
每一周,我们的同事都会向社区的成员们发布一些关于 Hugging Face 相关的更新,包括我们的产品和平台更新.社区活动.学习资源和内容更新.开源库和模型更新等,我们将其称之为「Hugging Ne ...
- vue3实现一个抽奖小项目
前言 在公司年会期间我做了个抽奖小项目,我把它分享出来,有用得着的可以看下. 浏览链接:http://xisite.top/original/luck-draw/index.html 项目链接:htt ...