WebGPU06-Begin-The Pipeline,来画个可爱的三角形?


Begin | WebGPU

3.The Pipeline-2

译者注:本节内容紧随上节,是对上节内容的具体应用。

LearnWgpu项目代码库

  • 怎样使用这些着色器?

    在这一部分,我们终于要开始着手于标题内容:the pipeline。

    首先我们需要修改State来包含以下代码:

    // main.rs
    struct State {
        surface: wgpu::Surface,
        device: wgpu::Device,
        queue: wgpu::Queue,
        config: wgpu::SurfaceConfiguration,
        size: winit::dpi::PhysicalSize,
        // NEW!
        render_pipeline: wgpu::RenderPipeline,
    }
    

    现在让我们将目光移向 方法部分,开始完善pipeline

    由于render_pipeline需要这些,我们要将下列代码写入我们之前创建的着色器:

    let shader = device.create_shader_module(&wgpu::ShaderModuleDescriptor {
        label: Some("Shader"),
        source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()),
    });
    

    此处不译,但需查看

    You can also use include_wgsl! macro as a small shortcut to create the ShaderModuleDescriptor.

    let shader = device.create_shader_module(&include_wgsl!("shader.wgsl"));
    

    还有一件事,我们需要创建一个名为PipelineLayout的方法。

    在我们介绍Buffers后,我们将对此作更多介绍:

    let render_pipeline_layout =
        device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
            label: Some("Render Pipeline Layout"),
            bind_group_layouts: &[],
            push_constant_ranges: &[],
        });
    

    终于,我们有了我们需要用来创建render_pipeline的一切:

    1.render_pipeline-1

    let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
        label: Some("Render Pipeline"),
        layout: Some(&render_pipeline_layout),
        vertex: wgpu::VertexState {
            module: &shader,
            entry_point: "vs_main", // 1.
            buffers: &[], // 2.
        },
        fragment: Some(wgpu::FragmentState { // 3.
            module: &shader,
            entry_point: "fs_main",
            targets: &[wgpu::ColorTargetState { // 4.
                format: config.format,
                blend: Some(wgpu::BlendState::REPLACE),
                write_mask: wgpu::ColorWrites::ALL,
            }],
        }),
        // continued ...
    

    代码讲解

    在这有几件事需要注意:

    1. 这里在着色器中你能够指定的函数应当是entry_point

      这些函数是我们用[[stage(vertex)]][[stage(fragment)]]标记过的。

    2. buffers区 告诉wgpu我们想传递各种类型的顶点给顶点着色器。由于我们要在 顶点着色器 中指定其本身的 顶点,所以我们将 顶点着色器 设置为空。在下个指导中,我们会像其中添加一些代码。

    3. fragment是技术性可选的,所以你得将其包入Some()中。如果我们想将颜色数据存入surface中,我们就需要它。

    4. targets域告诉wgpu它应该设置什么样的颜色输出。通常,我们仅需其中一个用于surface。我们使用surface的格式,以便轻松复制到它中,并且我们指定 混合(blending) 应该只用新数据替换旧像素数据。

      我们同时也告诉wgpu写入所有颜色:red,blue,green,还有alpha。在我们讨论材质时,我们对color_state会作更深入的了解。

    2.render_pipeline-2

        primitive: wgpu::PrimitiveState {
            topology: wgpu::PrimitiveTopology::TriangleList, // 1.
            strip_index_format: None,
            front_face: wgpu::FrontFace::Ccw, // 2.
            cull_mode: Some(wgpu::Face::Back),
            // Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE
            polygon_mode: wgpu::PolygonMode::Fill,
            // Requires Features::DEPTH_CLIP_CONTROL
            unclipped_depth: false,
            // Requires Features::CONSERVATIVE_RASTERIZATION
            conservative: false,
        },
        // continued ...
    

    代码讲解

    primitive域 描述了怎样解释我们的顶点当将它们转换为三角形时:

    1. 使用PrimitiveTopology::TriangleList意味着每三个顶点将与一个三角形建立联系。

    2. front_facecull_mode域 告诉wgpu如何决定一个给定的三角形是否面向前方。

      FrontFce::Ccw意味着如果 顶点 由 逆时针方向(counter-clockwise direction)安排三角形面向前方。

      按照CullMode::Back的指定,不被视为面前前方的三角形将会被剔除(cull:not included in the render)。

      在我们介绍Buffer时,我们再对 剔除(cull)作更多介绍。

    3.render_pipeline-3

        depth_stencil: None, // 1.
        multisample: wgpu::MultisampleState {
            count: 1, // 2.
            mask: !0, // 3.
            alpha_to_coverage_enabled: false, // 4.
        },
        multiview: None, // 5.
    });
    

    代码讲解

    此方法的剩余部分十分简单:

    1. 目前我们没有使用 深度/模板(depth/stencil) 缓冲区,所以我们先将depth_stencil设置为None我们待会儿再来修改它。
    2. count决定了 管道 将使用多少个 采样(sample)。多重采样是一个复杂的话题,所以我们不在此讨论。
    3. mask指定了那些 采样 应该被使用。在此处我们全部使用。
    4. alpha_to_coverage_enabled与 抗锯齿 有关。此处并不包括 抗锯齿,所以我们目前在此设置为false
    5. multiview(多视图)指示 渲染附件(render attachment)可以具有多少个 数组层(array layer)。我们不会渲染至 数组纹理(array texture),因此我们可以将其设置为None

    现在我们所需要做的仅是将render_pipeline添加至State中,然后我们就可以使用它了!

    Self {
        surface,
        device,
        queue,
        config,
        size,
        // NEW!
        render_pipeline,
    }
    
  • 使用一个 管道:Using a pipeline

    如果你现在运行你的程序,程序启动会花费更多时间,但最后它仍会显示我们在上一节中得到的蓝色屏幕。这是因为当我们创建了render_pipeline时,我们需要修改render()中的代码来实际使用它。

    // render()
    
    // ...
    {
        // 1.
        let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
            label: Some("Render Pass"),
            color_attachments: &[
                // This is what [[location(0)]] in the fragment shader targets
                wgpu::RenderPassColorAttachment {
                    view: &view,
                    resolve_target: None,
                    ops: wgpu::Operations {
                        load: wgpu::LoadOp::Clear(
                            wgpu::Color {
                                r: 0.1,
                                g: 0.2,
                                b: 0.3,
                                a: 1.0,
                            }
                        ),
                        store: true,
                    }
                }
            ],
            depth_stencil_attachment: None,
        });
    
        // NEW!
        render_pass.set_pipeline(&self.render_pipeline); // 2.
        render_pass.draw(0..3, 0..1); // 3.
    }
    // ...
    

    我们并未做太多的修改,但还是让我们具体讨论一下我们修改了什么:

    1. 我们重命名_render_passrender_pass并使它可变。
    2. 我们使用刚刚创建的 管道 在render_pass上来设置 管道。(?)
    3. 我们告知wgpu用3个 顶点 和1个 采样 来绘制一些东西。这就是[[builein(vertex_index)]]的来源。

    当所有工作都完成时,运行代码,你将得到一个可爱(大嘘)的棕色三角形。

    三角形

  • Challenge:挑战

    创建第二个管道,使用三角形的位置数据创建颜色,然后将其发送到片段着色器。 当你按空格键时,让应用程序在这些之间进行交换。

    提示:你需要修改 VertexOutput

该博客由本人个人翻译自Learn Wgpu,因此可能有部分文本不易理解或出现错误,如有发现还望告知,本人必定及时更改。