「这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战」。
前言
接上文Hello WebGPU —— 旋转立方体 - 掘金 (juejin.cn),我们介绍了在WebGPU中操作三维物体的方法,学习到了除了在创建Buffer时可以填充数据以外,还可以利用device.queue.writeBuffer 这个API进行数据的写入。今天,让我们继续来操作这个立方体,今天要学习的内容是给立方体贴图~
给立方体贴图
给立方体贴图我们分为以下几个步骤:
- “衣裳打样” —— 创建纹理
- ”衣裳印花“ —— 将原图像的纹理复制到纹理对象中
- ”制造说明书“ —— 创建sampler
- ”发送快递“ —— 将纹理、sampler传入GPU
- "根据说明书穿衣" —— shader渲染
衣裳打样
现在让我们来给光秃秃的立方体穿上好看的衣裳。首先我们需要“制造”这件衣裳:
cubeTexture = device.createTexture({
dimension: '2d',
size: [imageBitmap.width, imageBitmap.height],
format: 'rgba8unorm',
usage:
GPUTextureUsage.TEXTURE_BINDING |
GPUTextureUsage.COPY_DST |
GPUTextureUsage.RENDER_ATTACHMENT,
});
其中参数的意义如下:
- dimension: 纹理可以是1d、2d、3d的,这里我们使用2d纹理就好。
- size: 与dimension所对应,表示纹理的大小
- format: 'rgba8unorm',这里对format的格式说明一下:
- r, g, b, a: 表示red, green, blue, alpha
- unorm: 表示unsigned normalized,即表示是无符号的,归一化为 0~1范围的值。除此之外,还有以下的格式:
- snorm: signed normalized
- uint: unsigned int
- sint: signed int
- float: floating point
- usage: 这里可以认为是固定搭配,至少需要这三种usage。
衣裳印花
现在衣服“打样”完毕,但是现在的衣裳上面什么花纹也没有,我们需要一张图像,并且将上面的图案COPY过来
device.queue.copyExternalImageToTexture(
{ source: imageBitmap },
{ texture: cubeTexture },
[imageBitmap.width, imageBitmap.height]
);
copyExternalImageToTexture 该函数接受3个参数:
- source:
GPUImageCopyExternalImage,其是一个对象,需要具有以下属性:- 必须具有source 属性:只能是
ImageBitmap|HTMLCanvasElement|OffscreenCanvas对象。注意这里不能接受HTMLImageElement了,但是在WebGL中这是可以的,在WebGPU中,往其中传入的纹理必须是经过解码的纹理,而WebGL是可以帮我进行解码的。 这里也是一个提高WebGL中传入纹理性能的优化点。 - origin (可选):纹理数据是通过拷贝的方式传入GPU中,那么我们需要提供从哪里开始进行拷贝的点。
- 必须具有source 属性:只能是
- destination:
GPUImageCopyTextureTagged,其中需要具有以下属性:- 必须具有texture属性,为
GPUTexture - origin(可选):表示拿到拷贝的数据后,应该粘贴到纹理的哪个位置。
- 必须具有texture属性,为
- copySize: 表示复制的纹理区域大小。
制造说明书
现在衣服制造完毕,并且也把快递发送给了GPU,但是这件衣服稍微有一点点的花里胡哨,可能GPU拿到这件新衣不知道应该怎样去穿,所以现在我们需要给GPU送去一封说明书。
const sampler = device.createSampler({
magFilter: 'linear',
minFilter: 'linear',
});
说明书就制造好了,这是干嘛的呢,我们对比WebGL中又是怎样做的呢?
this.texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.texture);
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, srcFormat, srcType, img);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
在WebGL中,在制造完衣服时,就应该将说明书随产品一并发送给GPU了。而在WebGPU中,说明书却是单独发送的,这有一个好处,这提高了说明书的复用性,如果另一件相似的衣服我们传给GPU,那么它可以继续复用之前的说明书。
发送快递
现在,来到了我们熟悉的环节,发送快递,与Hello WebGPU —— 旋转立方体 - 掘金 (juejin.cn)这篇文章中类似,我们需要创建 BindGroup 来往GPU中发送数据。
const uniformBindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{
binding: 0,
resource: {
buffer: uniformBuffer,
},
},
{
binding: 1,
resource: sampler,
},
{
binding: 2,
resource: cubeTexture.createView(),
},
],
});
先将这些数据一并打包,等待最后的发货流程~!(提交drawCall请求)
根据说明书穿衣
好了,在我们发货完成后,现在来到GPU这边,GPU收到货之后,Shader开始运作了。我们看看它是如何阅读说明书并穿衣的。
[[group(0), binding(1)]] var mySampler: sampler;
[[group(0), binding(2)]] var myTexture: texture_2d<f32>;
[[stage(fragment)]]
fn main([[location(0)]] fragUV: vec2<f32>,
[[location(1)]] fragPosition: vec4<f32>) -> [[location(0)]] vec4<f32> {
return textureSample(myTexture, mySampler, fragUV) * fragPosition;
}
可以看到,我们声明了一个 sampler 类型的变量 mySampler,WGSL的标准中这样解释到:
Sampler. Mediates access to a sampled texture.
简单的来说,就是sampler是用于读取纹理的信息的,根据sampler的配置不同,读取纹理的方式也不同。
textureSample 是WGSL语言中内置的一个函数,用于根据提供的 纹理、sampler、纹理坐标对图像进行采样。类似于WebGL中 texture2D 函数,这里只是多了一个sampler对象。
最后,我们看看我们的立方体穿上衣裳后美美的样子吧!
总结
今天我们学习了如何给立方体贴图的操作,简单的来说分为以下几步:
- “衣裳打样” —— 创建纹理
- ”衣裳印花“ —— 将原图像的纹理复制到纹理对象中
- ”制造说明书“ —— 创建sampler
- ”发送快递“ —— 将纹理、sampler传入GPU
- "根据说明书穿衣" —— shader渲染
其余的操作步骤与我们之前学习的内容类似,今天的内容不算太复杂。各位有兴趣可以下来自行实现一番。好了,如果你觉得本文对你有用,别忘了点个赞,你的鼓励就是作者更新的动力~!