绘制一个点

我们初步认识了 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 变量
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 类似):

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

核心代码:

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

getUniformLocationgetAttribLocation 类似,只是这里返回 uniform 变量的位置

uniform4f

有了 uniform 变量的存储地址,就可以使用 uniform4f(和 vertexAttrib3f 很类似) 向变量中写入数据。

webgl 系列 —— 绘制一个点(版本2、版本3、版本4、版本5)的更多相关文章

  1. 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 双类别系统, ...

  2. WebGL编程指南案例解析之绘制一个点

    <!DOCTYPE html> <html> <head> <title>webgl</title> <style type=&quo ...

  3. Java容器--2021面试题系列教程(附答案解析)--大白话解读--JavaPub版本

    Java容器--2021面试题系列教程(附答案解析)--大白话解读--JavaPub版本 前言 序言 再高大上的框架,也需要扎实的基础才能玩转,高频面试问题更是基础中的高频实战要点. 适合阅读人群 J ...

  4. js,onblur后下一个控件获取焦点判断、html当前活跃控件、jquery版本查看、jquery查看浏览器版本、setTimeout&setInterval

    需求: input控件在失去焦点后直接做验证,验证通不过的话,显示相应错误.但是如果失去焦点后点击的下个控件是比较特殊的控件(比如,退出系统),那么不执行验证操作,直接退出系统(防止在系统退出前,还显 ...

  5. Opentk教程系列-1绘制一个三角形

    本系列教程翻译自Neo Kabuto's Blog.已经取得作者授权. 本文原文地址http://neokabuto.blogspot.com/2013/02/opentk-tutorial-1-op ...

  6. C#的Installer生成的msi的安装文件,安装新版本时提示:已经安装了该产品的另一个版本。无法继续安装此版本

    之前折腾了个C#的项目: WLW (Windows Live Writer) Plugin–InsertSkydriveFiles 然后又弄了个对应的Installer: [已解决]给一个C#的Dll ...

  7. WebGL简易教程(三):绘制一个三角形(缓冲区对象)

    目录 1. 概述 2. 示例:绘制三角形 1) HelloTriangle.html 2) HelloTriangle.js 3) 缓冲区对象 (1) 创建缓冲区对象(gl.createBuffer( ...

  8. WebGL简易教程(七):绘制一个矩形体

    目录 1. 概述 2. 示例 2.1. 顶点索引绘制 2.2. MVP矩阵设置 2.2.1. 模型矩阵 2.2.2. 投影矩阵 2.2.3. 视图矩阵 2.2.4. MVP矩阵 3. 结果 4. 参考 ...

  9. [Modern OpenGL系列(三)]用OpenGL绘制一个三角形

    本文已同步发表在CSDN:http://blog.csdn.net/wenxin2011/article/details/51347008 在上一篇文章中已经介绍了OpenGL窗口的创建.本文接着说如 ...

  10. OpenGl 绘制一个立方体

    OpenGl 绘制一个立方体 为了绘制六个正方形,我们为每个正方形指定四个顶点,最终我们需要指定6*4=24个顶点.但是我们知道,一个立方体其实总共只有八个顶点,要指定24次,就意味着每个顶点其实重复 ...

随机推荐

  1. Velero 系列文章(一):基础

    概述 Velero 是一个开源工具,可以安全地备份和还原,执行灾难恢复以及迁移 Kubernetes 集群资源和持久卷. 灾难恢复 Velero 可以在基础架构丢失,数据损坏和/或服务中断的情况下,减 ...

  2. PPT排版技巧

  3. 【转载】WebBrowser控件的常用方法、属性和事件

    1. 属性 属性 说明 Application 如果该对象有效,则返回掌管WebBrowser控件的应用程序实现的自动化对象(IDispatch).如果在宿主对象中自动化对象无效,这个程序将返回Web ...

  4. vivo 服务端监控体系建设实践

    作者:vivo 互联网服务器团队- Chen Ningning 本文根据"2022 vivo开发者大会"现场演讲内容整理而成. 经过几年的平台建设,vivo监控平台产品矩阵日趋完善 ...

  5. [深度学习] imgaug边界框增强笔记

    imgaug边界框增强笔记主要是讲述基于imgaug库对目标检测图像的边界框进行图像增强.本文需要掌握imgaug库的基本使用,imgaug库的基本使用见[深度学习] imgaug库使用笔记. 文章目 ...

  6. Redis-01 常用命令

    创建和获取 key 命令 说明 例子 set 创建一个名为 key 值为 value 键值对 set views 10 get 获取名为 key 的值,存在返回值,不存在返回 nil get view ...

  7. angular2-qrcode 引用报错 error NG8001: 'qr-code' is not a known element:

    error NG8001: 'qr-code' is not a known element: 解决方案 假如你的组件模块叫做a-demo.module,你的组件叫做print.component.t ...

  8. 【Azure Cache for Redis】Python Djange-Redis连接Azure Redis服务遇上(104, 'Connection reset by peer')

    问题描述 使用Python连接Azure Redis服务,因为在代码中使用的是Djange-redis组件,所以通过如下的配置连接到Azure Redis服务: CACHES = { "de ...

  9. drf-day5——反序列化类校验部分源码分析、断言、drf请求、drf响应、视图组件及两个视图基类、基于GenericAPIView+5个视图扩展类

    目录 一.反序列化类校验部分源码解析(了解) 二.断言 三.drf之请求 3.1 Request能够解析的前端传入的编码格式 3.2 Request类有哪些属性和方法(学过) 常用参数 Respons ...

  10. 11月30日内容总结——前端简介、http协议概念、html协议概念及基础知识和部分标签的讲解

    目录 一.前端与后端的概念 什么是前端开发? 什么是后端? 学习前端的目的 前端三剑客 二.前端前戏 三.HTTP协议 1.四大特性 2.报文格式 3.响应状态码 四.HTML概览 1.HTML简介 ...