一起学 WebGL:三角形加上渐变色

开发 前端
本节讲了Varying 的能力:将顶点着色器中的变量传递给片元着色器。并演示了使用两个缓冲区对象,位置数据和颜色数据,以及将它们组合成一个缓冲区对象的实现。

大家好,我是前端西瓜哥。之前教大家绘制一个红色的三角形,这次我们来画个有渐变的三角形。

原来的写法,颜色是在片元着色器中写死的,这次我们来像传顶点数据一样,声明一个颜色数据传递过去。

颜色需要在片元着色器中赋值给内部变量 gl_FragColor,但 attribute 动态类型却不能在片元着色器中使用。

这时候就要用到一个新的类型 varying 了。(意思为:“变化的“)

varying 用于从顶点着色器中将变量传递到片元着色器中

两个缓冲区对象的写法

着色器代码:

const vertexShaderSrc = `
attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
void main() {
 gl_Position = a_Position;
 v_Color = a_Color;
}
`;

const fragmentShaderSrc = `
precision mediump float;
varying vec4 v_Color;
void main() {
  gl_FragColor = v_Color;
}
`;

这里我们需要在两种着色器中同时声明 varing 变量,后面的类型也必须是相同的 vec4,变量名也要一致,只能说是完全相同了。

顶点着色器中需要通过 v_Color = a_Color; 赋值。然后在片元着色器中,再将同步过来的 v_Color 赋值给 gl_FragColor。

然后是新增的颜色数组的声明,以及对应缓存区的创建。

/**** 颜色数据 ****/
// prettier-ignore
const colors = new Float32Array([
  1, 0, 0, // 红色
  0, 1, 0, // 绿色
  0, 0, 1, // 蓝色
])
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
const a_Color = gl.getAttribLocation(gl.program, "a_Color");
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(a_Color);

贴一下完整代码:

/** @type {HTMLCanvasElement} */
const canvas = document.querySelector("canvas");
const gl = canvas.getContext("webgl");

const vertexShaderSrc = `
attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
void main() {
 gl_Position = a_Position;
 v_Color = a_Color;
}
`;

const fragmentShaderSrc = `
precision mediump float;
varying vec4 v_Color;
void main() {
  gl_FragColor = v_Color;
}
`;

/**** 渲染器生成处理 ****/
// 创建顶点渲染器
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSrc);
gl.compileShader(vertexShader);
// 创建片元渲染器
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSrc);
gl.compileShader(fragmentShader);
// 程序对象
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
gl.program = program;

// 顶点数据
// prettier-ignore
const vertices = new Float32Array([
  0, 0.5,  // 第一个点
  -0.5, -0.5,  // 第二个点
  0.5, -0.5,  // 第三个点
]);

// 创建缓存对象
const vertexBuffer = gl.createBuffer();
// 绑定缓存对象到上下文
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 向缓存区写入数据
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

// 获取 a_Position 变量地址
const a_Position = gl.getAttribLocation(gl.program, "a_Position");
// 将缓冲区对象分配给 a_Position 变量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);

// 允许访问缓存区
gl.enableVertexAttribArray(a_Position);

/**** 颜色数据 ****/
// prettier-ignore
const colors = new Float32Array([
  1, 0, 0, // 红色
  0, 1, 0, // 绿色
  0, 0, 1, // 蓝色
])
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
const a_Color = gl.getAttribLocation(gl.program, "a_Color");
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(a_Color);

/*** 绘制 ***/
// 清空画布,并指定颜色
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// 绘制三角形
gl.drawArrays(gl.TRIANGLES, 0, 3);

demo 地址:

https://codesandbox.io/s/uqbjsu?file=/index.js。

渲染结果:

图片

我们其实只是给三个顶点设置了红、绿、蓝三个颜色,然后 WebGL 会基于它们计算出中间的过内插颜色,将它们填充满三个点围成区域的像素点。

单缓冲区的实现

前面的实现用了两个缓冲区对象分别保存位置信息和颜色信息。

但实际上可以将它们组合在一起,让数据更紧凑放在一个缓冲区里。

浮点数数组为:

// prettier-ignore
const verticesColors = new Float32Array([
  0, 0.5, 1, 0, 0,  // 点 1 的位置和颜色信息
  -0.5, -0.5, 0, 1, 0,  // 点 2
  0.5, -0.5, 0, 0, 1,  // 点 3
]);

然后是和前一种写法有一些不同的地方:

// 每个数组元素的字节数
const SIZE = verticesColors.BYTES_PER_ELEMENT;

// 获取 a_Position 变量地址
const a_Position = gl.getAttribLocation(gl.program, "a_Position");
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, SIZE * 5, 0);
gl.enableVertexAttribArray(a_Position);

const a_Color = gl.getAttribLocation(gl.program, "a_Color");
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, SIZE * 5, SIZE * 2);
gl.enableVertexAttribArray(a_Color);

主要是 gl.vertexAttribPointer 方法的最后两个参数 stride 和 offset。

我们看下面这个:

gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, SIZE * 5, SIZE * 2);

stride 为 SIZE * 5(单位为字节,所以要乘以一个数组元素的字节大小),表示 5 个数组元素为一趟,然后 offset 为 SIZE  * 2,表示从第 2 个元素,取 3 个元素作为这一趟的数据内容。

完整代码实现:

/** @type {HTMLCanvasElement} */
const canvas = document.querySelector("canvas");
const gl = canvas.getContext("webgl");

const vertexShaderSrc = `
attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
void main() {
 gl_Position = a_Position;
 v_Color = a_Color;
}
`;

const fragmentShaderSrc = `
precision mediump float;
varying vec4 v_Color;
void main() {
  gl_FragColor = v_Color;
}
`;

/**** 渲染器生成处理 ****/
// 创建顶点渲染器
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSrc);
gl.compileShader(vertexShader);
// 创建片元渲染器
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSrc);
gl.compileShader(fragmentShader);
// 程序对象
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
gl.program = program;

// 顶点数据
// prettier-ignore
const verticesColors = new Float32Array([
  0, 0.5, 1, 0, 0,  // 点 1 的位置和颜色信息
  -0.5, -0.5, 0, 1, 0,  // 点 2
  0.5, -0.5, 0, 0, 1,  // 点 3
]);
// 每个数组元素的字节数
const SIZE = verticesColors.BYTES_PER_ELEMENT;

// 创建缓存对象
const vertexColorBuffer = gl.createBuffer();
// 绑定缓存对象到上下文
gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);
// 向缓存区写入数据
gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);

// 获取 a_Position 变量地址
const a_Position = gl.getAttribLocation(gl.program, "a_Position");
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, SIZE * 5, 0);
gl.enableVertexAttribArray(a_Position);

const a_Color = gl.getAttribLocation(gl.program, "a_Color");
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, SIZE * 5, SIZE * 2);
gl.enableVertexAttribArray(a_Color);

/*** 绘制 ***/
// 清空画布,并指定颜色
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// 绘制三角形
gl.drawArrays(gl.TRIANGLES, 0, 3);

demo 地址:

https://codesandbox.io/s/bvkv20?file=/index.js。

结尾

本节讲了 varying 的能力:将顶点着色器中的变量传递给片元着色器。并演示了使用两个缓冲区对象,位置数据和颜色数据,以及将它们组合成一个缓冲区对象的实现。

责任编辑:姜华 来源: 前端西瓜哥
相关推荐

2023-04-17 09:01:01

WebGL绘制三角形

2023-11-01 07:51:15

WebGPU3D 图形

2016-10-20 13:36:28

WebRTC浏览器服务器

2021-10-19 10:09:21

三角形个数数组

2022-03-16 14:27:49

CSS三角形前端

2021-08-29 18:32:18

CSS

2020-12-09 08:34:24

css生成器设计师

2024-02-20 18:30:53

CSS属性边框

2023-04-26 07:42:16

WebGL图元的类型

2024-09-05 11:20:54

2021-07-16 05:59:27

CSS 技巧带圆角的三角形

2022-09-14 15:17:26

ArkUI鸿蒙

2018-03-02 15:54:37

三角形主机比特币

2021-04-15 06:02:50

CSS 三角形技巧

2023-05-04 08:48:42

WebGL复合矩阵

2023-06-26 15:14:19

WebGL纹理对象学习

2023-04-12 07:46:24

JavaScriptWebGL

2023-03-29 07:31:09

WebGL坐标系

2013-09-26 13:43:13

iOS开发OpenGL ES教程图元

2023-04-13 07:45:15

WebGL片元着色器
点赞
收藏

51CTO技术栈公众号