|
绘制一个点
我们初步认识了 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 很类似) 向变量中写入数据。
来源:https://www.cnblogs.com/pengjiali/p/17168064.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|