顶点着色器向片元着色器传参

392 阅读4分钟

课件:github.com/buglas/webg…

知识点:

  • 多attribute 变量的建立
  • 在着色器中定义接口
  • 顶点着色器向片元着色器传递数据

1-基本概念

有的时候,我们需要把基于顶点定义的attribute变量,从顶点着色器传递到片元着色器中,其功能就像WebGL 里的varying 插值。

image-20220702133145307

接下来,我们会以一个彩色三角形为例,说一下如何把基于顶点定义的颜色,从顶点着色器传递到片元着色器中。

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还在频繁更新中。

当前我们是把顶点点位和顶点颜色都合在一起建立的缓冲区,下节课我会说一下如何把顶点点位和顶点颜色分开。

参考链接:space.bilibili.com/1006136755