代码见:https://github.com/onsummer/my-dev-notes/tree/master/webgpu-Notes/04-texture-triangle

原创,发布日 2021年4月5日,@秋意正寒。若代码失效请留言,或自行到官网根据最新 API 修改。

数据预览

NDC 坐标和 纹理坐标(原点、朝向、值域)基础不再补充。

0.1 VBO

const vbodata = new Float32Array([
-1.0, -1.0, 0.0, 1.0,
0.0, 1.0, 0.5, 0.0,
1.0, -1.0, 1.0, 1.0
])

0.2 贴图

一张 256 x 256 的 png 贴图,jpg 和 webp 没测试。

1 纹理与采样器

使用采样器在纹理贴图上,通过纹理坐标取顶点的颜色值,这是一个很常规的操作。

1.1 创建采样器、纹理

/* create sampler */
const sampler = device.createSampler({
minFilter: "linear",
magFilter: "linear"
}) /* --- create img source --- */
const img = document.createElement('img')
img.src = 'texture.png'
// 用 await 代表这个操作必须在 async 函数中,或者在 html 中提前做好 img 标签并加载纹理贴图
await img.decode()
const imageBitmap = await createImageBitmap(img) /* create texture */
const texture = device.createTexture({
size: [img.width, img.height], // 256, 256 -> 2d的纹理
format: "rgba8unorm",
usage: GPUTextureUsage.SAMPLED | GPUTextureUsage.COPY_DST
})

1.2 将纹理贴图数据 填入 纹理对象

我在文档中看到有两个方法,一个是 GPUQueue.prototype.writeToTexture(),另一个是下面这个 GPUQueue.prototype.copyImageBitmapToTexture()

device.queue.copyImageBitmapToTexture(
{ imageBitmap: imageBitmap },
{ texture: texture },
[img.width, img.height, 1]
)

至此,采样器、纹理对象都准备好了,纹理数据也写入纹理对象中了。

2 将纹理对象、采样器对象传进流水线(Pipeline)

要向渲染通道传递纹理和采样器,必须创建一个 “Pipeline布局” 对象。

这个布局对象要对纹理对象、采样器对象进行绑定。在 WebGPU 中,将诸如 uniform变量、采样器、纹理对象 等资源统一打组,这个组叫 GPUBindGroup

它们的关系图大概是这样的:

上代码:

/* 创建绑定组的布局对象 */
const bindGroupLayout = device.createBindGroupLayout({
entries: [
{
/* use for sampler */
binding: 0,
visibility: GPUShaderStage.FRAGMENT,
sampler: {
type: 'filtering',
},
},
{
/* use for texture view */
binding: 1,
visibility: GPUShaderStage.FRAGMENT,
texture: {
sampleType: 'float'
}
}
]
})
/* 创建 Pipeline 的布局对象 */
const pipelineLayout = device.createPipelineLayout({
bindGroupLayouts: [bindGroupLayout],
})
/* 创建 pipeline 时,传递 Pipeline 的布局对象 */
const pipeline = device.createRenderPipeline({
layout: pipelineLayout, // <- 传递布局对象
// ... 其他
})
/* 创建绑定组:GPUBindGroup,一组资源 */
const uniformBindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0), // <- 指定绑定组的布局对象
entries: [
{
binding: 0,
resource: sampler, // <- 传入采样器对象
},
{
binding: 1,
resource: texture.createView() // <- 传入纹理对象的视图
}
]
})

3 修改着色器

光将采样器、纹理对象传入流水线还是不够的,在顶点着色器阶段、片元着色器阶段,仍需要把颜色给弄到。

3.1 顶点着色器

[[builtin(position)]] var<out> out_position: vec4<f32>;
[[location(0)]] var<out> out_st: vec2<f32>; // <- 输出纹理坐标到下一阶段,即片元着色器 [[location(0)]] var<in> in_position_2d: vec2<f32>;
[[location(1)]] var<in> in_st: vec2<f32>; // <- 从 GPUBuffer 中获取的纹理坐标 [[stage(vertex)]]
// 注意这个主函数被改为 vertex_main,在创建流水线时,要改 entryPoint 属性为 'frag_main'
fn vertex_main() -> void {
out_position = vec4<f32>(in_position_2d, 0.0, 1.0);
out_uv = in_st;
return;
}

3.2 片元着色器

// 从绑定组i里取绑定的资源j的语法是 [[binding(j), group(i)]]
[[binding(0), group(0)]] var mySampler: sampler; // <- 采样器对象
[[binding(1), group(0)]] var myTexture: texture_2d<f32>; // <- 纹理对象 [[location(0)]] var<out> outColor: vec4<f32>; [[location(0)]] var<in> in_st: vec2<f32>; // 从顶点着色器里传递进来的纹理坐标 [[stage(fragment)]]
// 注意这个主函数被改为 frag_main,在创建流水线时,要改 entryPoint 属性为 'frag_main'
fn frag_main() -> void {
outColor = textureSample(myTexture, mySampler, in_st); // 使用 textureSample 内置函数获取对应纹理坐标的颜色
return;
}

4 渲染通道编码器传入资源

即把绑定组传入对象传入即可。

passEncoder.setBindGroup(0, uniformBindGroup)

踩坑点

不要设置流水线中 primitive 属性的 cullMode 属性值为 "back",否则会背面剔除。

const pipeline = device.createRenderPipeline({
// ...
primitive: {
topology: 'triangle-list',
cullMode: 'back', // <- 如果设为 back 三角形就不见了
}
// ...
})

WebGPU[4] 纹理三角形的更多相关文章

  1. WebGL 与 WebGPU比对[6] - 纹理

    目录 1. WebGL 中的纹理 1.1. 创建二维纹理与设置采样参数 1.2. 纹理数据写入与拷贝 1.3. 着色器中的纹理 1.4. 纹理对象 vs 渲染缓冲对象 1.5. 立方体六面纹理 1.6 ...

  2. dx中纹理相关的接口备注

    1.内存中的纹理保存到文件 HRESULT D3DXSaveTextureToFile( __in LPCTSTR pDestFile, __in D3DXIMAGE_FILEFORMAT DestF ...

  3. DirectX 基础学习系列5 纹理映射

    1 纹理坐标 类似BMP图像坐标系,左上为原点 纹理坐标为了规范化,范围限定在[0,1]之间,使用纹理的时候,需要修改顶点结构 struct ColorVetex { float x, y,z; fl ...

  4. 有关mipmaps

    Mipmaps的作用是什么,仅仅是为了使屏幕三角形和纹理三角形的差异变小?有没有以空间换时间的理念? Mipmaps在生成一系列小的纹理样本时, 是如何从原始纹理采样的?即如何生成这些小的纹理样本.

  5. DirectX 总结和DirectX 9.0 学习笔记

    转自:http://www.cnblogs.com/graphics/archive/2009/11/25/1583682.html DirectX 总结 DDS DirectXDraw Surfac ...

  6. CCRenderBuffer初始化中的render state参数

    绘制纹理三角形的渲染状态(render state)已经被CCSprite基类设置过了,所以你可以简单的将self.renderState传递过去就可以了. 渲染状态是混合模式(blend mode) ...

  7. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十九章:法线贴图

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十九章:法线贴图 学习目标 理解为什么需要法线贴图: 学习法线贴图如 ...

  8. WebGPU图形编程(3):构建三角形图元<学习引自徐博士教程>

    一.首先修改你的index.html文件 请注意主要在html页面修改添加的是需要加选择项:"triangle-list"和"triangle-strip",如 ...

  9. WebGPU学习(二): 学习“绘制一个三角形”示例

    大家好,本文学习Chrome->webgl-samplers->helloTriangle示例. 准备Sample代码 克隆webgl-samplers Github Repo到本地. ( ...

随机推荐

  1. Bastion Host (BH)

    Bastion Host (BH) 堡垒机 堡垒主机是专门设计和构造成承受攻击网络上的专用计算机. 该计算机通常承载单个应用程序,例如代理服务器,并且所有其他服务都将被删除或限制以减少对计算机的威胁. ...

  2. Chrome blocked third-party cookies

    Chrome blocked third-party cookies Chrome Incognito Chrome 无痕模式 https://support.google.com/chrome/an ...

  3. Learning CSS with Chrome DevTools

    Learning CSS with Chrome DevTools CSS 复合属性展开 border background box-shadow flex-flow flex HTML5 custo ...

  4. vscode & ignore .idea

    vscode & ignore .idea settings.json .vscode & ignore .idea // 将设置放入此文件中以覆盖默认设置 { "files ...

  5. rem & 16px & 62.5%

    rem & 16px & 62.5% 浏览器的默认字体大小都是16px 兼容性:IE9+,Firefox.Chrome.Safari.Opera 的主流版本都支持了rem 对不支持的浏 ...

  6. 3. Vue语法--计算属性

    一. 计算属性 1. 什么是计算属性? 通常, 我们是在模板中, 通过插值语法显示data的内容, 但有时候我们可能需要在{{}}里添加一些计算, 然后在展示出来数据. 这时我们可以使用到计算属性 先 ...

  7. 一文读懂网管协议 - SNMP,NETCONF,RESTCONF

    本文篇幅较长,主要涉及以下内容: 介绍传统 CLI 配置网络设备存在的挑战,网管协议出现的背景 SNMP 原理,交互过程,以及 trade-off NETCONF 架构,交互过程 RESTCONF 架 ...

  8. c#winform主题实现的一个方法

    winform的主题实现没有bs里面那么舒服,下面做了一个简单实现,记录一下. 1.一个接口,需要做主题的控件.窗体都要实现这个接口 /// <summary> /// 使用主题的控件.窗 ...

  9. SpringBoot接收map类型的参数

    如果某个controller的某个接口的参数特别多,也可以使用map的方式来接收参数,接收之后使用get方法获取即可. 1)get请求方式,定义map接收方式 @RequestParam(requir ...

  10. Java基本概念:内部类

    一.简介 描述: 很多时候我们创建类的对象的时候并不需要使用很多次,每次只使用一次,这个时候我们就可以使用内部类了. 内部类不是在一个java源文件中编写两个平行的类,而是在一个类的内部再定义另外一个 ...