当前位置:首页 »“秋了秋”个人博客 » 前端编程 » WebGPU教程(说人话)批量绘制几何图形(二)

WebGPU教程(说人话)批量绘制几何图形(二)

作者:秋了秋 发表时间:2024年03月13日

    上一篇文章《WebGPU教程(说人话)绘制任意形状的2D图形(一)》介绍了绘制任意形状的2D图形,我们总结下,想要绘制任意形状的图形,我们只需要把这个图形的所有顶点数据放到一个数组里面再发给GPU,然后使用WGSL代码对顶点数据处理,配置渲染管线怎么处理数据,最后通过调用draw方法,把这些顶点全部绘制出来。

那么是不是绘制多个图形就要多次走这一套流程呢?很显然不是。GPU绘制是多线程的,如果这样做就失去了使用GPU的意义了,会带来很大的性能消耗。正确的做法应该是把数据都写在顶点数组里面,通过一次draw出来。

    比如绘制一个五边形和一个三角形顶点数据是:

const  vertexData = new Float32Array([
    -0.2,0.5, // 五边形第一个顶点
    -0.6,0.4, // 五边形第二个顶点
    -0.3,0.2, // 五边形第三个顶点
    -0.5, -0.3, // 五边形第四个顶点
    -0.1,0, // 五边形第五个顶点
    -0.2,0.5, // 闭合,回到起点
    // 三角形
    0.3, 0.2, // 三角形第一个顶点
    0.6,0.1, // 三角形第二个顶点
    0.1,-0.3, // 三角形第三个顶点
    0.3, 0.2, // 闭合,回到起点
]);

一共十个顶点,顶点处理函数为:

const shaderCode = `
    struct VertexOut {
        @builtin(position) pos: vec4f,
        @location(0) color: vec4f
    }
    @vertex
    fn v_main(@location(0) pos:vec2f) -> VertexOut {
        var out: VertexOut;
        out.pos = vec4f(pos, 0, 1);
        out.color = vec4f(1, 0, 0, 1);
        return out;
    }
    @fragment
    fn f_main(@location(0) c:vec4f) -> @location(0) vec4f {
        return c;
    }
 `;

渲染管线的配置:

const pipeLineConfig ={
    ...
    vertex: {
        ...
        buffers: [{
            ...
            arrayStride: 8,
            attributes: [{
                shaderLocation: 0,
                offset: 0,
                format: 'float32x2'
            }]
        }]
    },
    primitive: {
        topology: 'line-strip' // 连续线条绘制
    }
}

因为给的顶点数据只有二维坐标,没有颜色信息,所以我们也不需要做颜色解析了,直接使用固定颜色vec4f(1, 0, 0, 1)红色,attributes数组也就只有一项了,arrayStride步进改为8了。

最后通过draw 10次把图形绘制出来。

pass.draw(10);

webgpu绘制多个图形.jpg

    看看现在出来的图形会很诡异,中间多了一条线,是五边形的结束点和三角形的起始点连了一条线,这是我们所不期望的,问题出在哪里?这是我们的topology设置成了line-strip。


    topology有以下几个值: triangle-list, triangle-strip, line-list, line-strip, point-list。这几个值分别对应的模式简单明了的阐述为:


    triangle-list:三角形列表,三个点一组三个点一组去渲染,是有填充颜色的,填充颜色是顶点颜色。 例: 给定顶点ABCDEF,GPU会绘制两个三角形ABCDEF

    triangle-strip:连续的三角形,也是三个点一组三个点一组去渲染,跟triangle-list不同的是接下来绘制三角形的起点是上一次绘制的终点,如果是首次绘制就是数组里面的第一个坐标点 例: 给定顶点ABCDEF,GPU会绘制两个三角形ABCCDEF没有找到成立三角形的其它点被舍弃了。

    line-list:线段列表,两个点一组两个点一组去渲染,没有填充颜色。例: 给定顶点ABCDEF,GPU会绘制三条线段ABCDEF

    line-strip:连续线段列表,也是两个点一组两个点一组去渲染线段,跟line-list不同的是接下来绘制线段的起点是上一次绘制的终点,如果是首次绘制就是数组里面的第一个坐标点。例: 给定顶点ABCDEF,GPU会绘制五条线段ABBCCDDEEF

     point-list: 就单纯把顶点绘制出来,不进行连线等任何操作。


    所以针对以上bug我们只需要设置topology: 'line-list' 就行,当你设置了之后发现还是不行,现在发现缺失了一些线条,仔细看我们的顶点数据,结合line-list的说明才恍然大悟,要想通过此模式绘制连续的线条,顶点里面必须增加一些重复的顶点来实现:

const  vertexData = new Float32Array([
    -0.2,0.5,
    -0.6,0.4,
    -0.6,0.4, // 新增
    -0.3,0.2,
    -0.3,0.2, // 新增
    -0.5, -0.3,
    -0.5,- 0.3, // 新增
    -0.1,0,
    -0.1,0, // 新增
    -0.2,0.5,
    // 三角形
    0.3, 0.2,
    0.6,0.1,
    0.6,0.1, // 新增
    0.1,-0.3,
    0.1,-0.3, // 新增
    0.3, 0.2,
]);

绘制16个顶点

pass.draw(16);

现在两个图形终于分开了,两个三个四个更多独立图形,按照此逻辑即可批量进行绘制。

webgpu绘制多个分开独立的图形.jpg


    方法二:


    如果是绘制多个形状相同的图形呢,只是位置或者颜色有差异,WebGPU给我们提供了绘制实例的方法,解放了定义过多顶点数据问题和定义重复顶点的问题。只需要定义一个图形的所有顶点,draw的第二个参数就是绘制实例值的个数。比如:

const  vertexData = new Float32Array([
    -0.2,0.5,
    -0.6,0.4,
    -0.3,0.2,
    -0.5, -0.3,
    -0.1,0,
    -0.2,0.5
]);

我们还是可以使用topology: 'line-strip'这样可以少定义一些重复顶点提升性能。然后通过

pass.draw(6, 2)绘制两个实例,第二个参数就是实例的个数。


    现在我们看到画布还是只有一个图形,其实是有两个的,它们两个重叠在一起了所以看不出来,如果让实例之间按照规则错开排列,需要使用WGSL语言借助内部变量instance_index来实现:

const shaderCode = `
    struct VertexOut {
        @builtin(position) pos:vec4f,
        @location(0) color:vec4f
    }
    @vertex
    fn v_main(@location(0) pos:vec2f, @builtin(instance_index) index: u32) -> VertexOut {
        var out: VertexOut;
        let i = f32(index);
        let vecI = vec2f(i / 4, 0);
        let myPos = pos + vecI;
        out.pos = vec4f(myPos, 0, 1);
        out.color = vec4f(1,  i / 1.4,  i / 1.4,  1);
        return out;
    }

    @fragment
    fn f_main(@location(0) c:vec4f) -> @location(0) vec4f {
        return c;
    }
 `;

    WGSL代码里面的运算跟JS一样,但是要注意的是类型一定要相同才能运算,比如实例类型是无符号32位整型,需要使用f32()函数转成32位有符号浮点数。同时运算都是向量的运算,需要使用vec2f()函数转成二维向量,再通过向量与向量的加法分别对xy做偏移,let vecI = vec2f(i / 4, 0);
let myPos = pos + vecI;表示的就是x移动i/4,y移动0。而i是instance_index演变而来,这是个变量,当绘制第一个实例的时候它是0,绘制第二个实例的时候它是1。除了位置变了颜色也改变了out.color = vec4f(1,  i / 1.4,  i / 1.4,  1);最终渲染的结果就是

webgpu绘制多个实例.jpg

多绘制几个,只需要改变实例个数即可,比如绘制4个:

pass.draw(6, 4);

webgpu绘制多个实例(4个).jpg

你学废了吗?

1
文章作者: “秋了秋”个人博客,本站鼓励原创。
转载请注明本文地址:http://netblog.cn/blog/508.html
目录: 前端编程标签: WebGPU,几何图形绘制 699次阅读

请求播放音乐,请点击播放

登 录
点击获取验证码
还没账号?点击这里