知识点:
- 多attribute 变量的建立
- 在着色器中定义接口
- 顶点着色器向片元着色器传递数据
1-基本概念
有的时候,我们需要把基于顶点定义的attribute变量,从顶点着色器传递到片元着色器中,其功能就像WebGL 里的varying 插值。
接下来,我们会以一个彩色三角形为例,说一下如何把基于顶点定义的颜色,从顶点着色器传递到片元着色器中。
2-在Pipeline中建立多个attribute 变量
在上一节矩阵变换图形的代码基础上进行修改。
1.先删除与颜色color相关的代码,这个color是为所有顶点统一定义的uniform变量,后面我们会为每个顶点定义一个color。
// 顶点颜色
// const color = new Float32Array([1, 1, 0, 1])
……
// 颜色缓冲区
/* const colorBuffer = device.createBuffer({
size: color.byteLength, //4 * 4,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
}) */
// 写入数据
// device.queue.writeBuffer(colorBuffer, 0, color)
……
const uniformGroup = device.createBindGroup({
// 布局
layout: pipeline.getBindGroupLayout(0),
// 添加buffer
entries: [
// 图形颜色
/* {
// 位置
binding: 0,
// 资源
resource: {
buffer: colorBuffer,
},
}, */
// 模型矩阵
{
// 位置
binding: 1,
// 资源
resource: {
buffer: modelBuffer,
},
},
],
})
2.把顶点颜色和顶点点位合到一起。
const vertex = new Float32Array([
// 顶点0
0, 0.5, 0, 1,1,0,
// 顶点1
-0.5, -0.5, 0,1,0,1,
// 顶点2
0.5, -0.5, 0, 0,0,1
])
在上面的每个顶点里面,前三个元素对应顶点的(x,y,z)位置,后三个元素对应顶点的(r,g,b)颜色。
3.在Pipeline的描述对象里再添加一个与顶点颜色相对应的attribute 描述对象。
const descriptor: GPURenderPipelineDescriptor = {
// 顶点着色器
vertex: {
// 着色程序
module: device.createShaderModule({
code: positionVert,
}),
// 主函数
entryPoint: "main",
//缓冲数据,1个渲染管道可最多传入8个缓冲数据
buffers: [
{
// 顶点长度,以字节为单位
arrayStride: 6 * 4,
attributes: [
//顶点位置
{
// 变量索引
shaderLocation: 0,
// 偏移
offset: 0,
// 参数格式
format: "float32x3",
},
//顶点颜色
{
// 变量索引
shaderLocation: 1,
// 偏移
offset: 3*4,
// 参数格式
format: "float32x3",
},
],
},
],
},
……
}
3-建立顶点着色器
- /src/shaders/vertToFrag.vert.wgsl
@group(0) @binding(1) var<uniform> modelMatrix:mat4x4<f32>;
struct VertexOutput {
@builtin(position) Position : vec4<f32>,
@location(0) color: vec4<f32>
};
@vertex
fn main(
@location(0) position : vec4<f32>,
@location(1) color : vec4<f32>
) ->VertexOutput{
var output : VertexOutput;
output.Position =modelMatrix*position;
output.color = color;
return output;
}
1.获取js中定义的attribute 变量
fn main(
@location(0) position : vec4<f32>,
@location(1) color : vec4<f32>
)……
上面的@location(0)、@location(1) 对应Pipeline的描述对象里的shaderLocation
const descriptor: GPURenderPipelineDescriptor = {
// 顶点着色器
vertex: {
……
buffers: [
{
……
attributes: [
//顶点位置
{
// 变量索引
shaderLocation: 0,
……
},
//顶点颜色
{
// 变量索引
shaderLocation: 1,
……
},
],
},
],
},
……
}
2.建立VertexOutput 接口,类似于ts里的interface 接口。
struct VertexOutput {
@builtin(position) Position : vec4<f32>,
@location(0) color: vec4<f32>
};
- @builtin(position) Position : vec4 是必须要有的,因为我们之后会将VertexOutput 作为顶点着色器主函数的返回对象的类型,在这个对象里必须要有Position。
- @location(0) color: vec4 是我在当前接口中定义的局部变量,location(0)中的0是可以自定义的数字,这和main()中的location 变量是不冲突的,因为二者的作用域不同。
3.将VertexOutput 接口作为main函数中的返回对象的类型
fn main(
@location(0) position : vec4<f32>,
@location(1) color : vec4<f32>
) ->VertexOutput{
……
}
4.在main函数中声明VertexOutput 接口类型的对象,对其赋值并返回。
@vertex
fn main(
@location(0) position : vec4<f32>,
@location(1) color : vec4<f32>
) ->VertexOutput{
var output : VertexOutput;
output.Position =modelMatrix*position;
output.color = color;
return output;
}
- output.Position 就相当于WebGL里的gl_Position,这是必须要有的。
- output.color 便是我们想要在片元着色器里拿到的颜色。
4-在片元着色器里获取顶点着色器传递过来的颜色
- /src/shaders/vertToFrag.frag.wgsl
@fragment
fn main(
@location(0) color: vec4<f32>
) -> @location(0) vec4<f32> {
return color;
}
- main(@location(0) color: vec4) 中的@location(0)对应的就是VertexOutput中的@location(0)
- fn main(……) -> @location(0) vec4 这里的@location(0) vec4 表示片元着色器要返回的片元颜色的数据类型。@location(0)中必须写0,这个0和main(@location(0)……)中的0不冲突,因为作用域不同。
总结
现在我们已经说完了把attribute 变量从顶点着色器传递到片元着色器里的方法。
这个过程有点绕,我们可以先知道这个原理,因为WebGPU的 API还在频繁更新中。
当前我们是把顶点点位和顶点颜色都合在一起建立的缓冲区,下节课我会说一下如何把顶点点位和顶点颜色分开。