文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-11-30 14:53

关注

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

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

颜色需要在片元着色器中赋值给内部变量 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);

贴一下完整代码:


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 个元素作为这一趟的数据内容。

完整代码实现:


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

来源:前端西瓜哥内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯