- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
WebGL 使用 TypedArray 进行数据传递,这点 WebGPU 也是一样的。
下面的代码是 WebGL 1.0 常规的 VertexBuffer 创建、赋值、配置过程。
const positions = [
0, 0,
0, 0.5,
0.7, 0,
]
/*
创建着色器程序 program...
*/
// 获取 vertex attribute 在着色器中的位置
const positionAttributeLocation = gl.getAttribLocation(program, "a_position")
//#region 创建 WebGLBuffer 并绑定,随即写入数据
const positionBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW)
//#endregion
//#region 启用顶点着色器中对应的 attribute,再次绑定数据,并告知 WebGL 如何读取 VertexBuffer
gl.enableVertexAttribArray(positionAttributeLocation)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
gl.vertexAttribPointer(positionAttributeLocation, size, type, normalize, stride, offset)
//#endregion
WebGL 通过 gl 变量的 createBuffer
、bindBuffer
、bufferData
方法来创建缓冲、绑定当前要用什么缓冲及缓冲的用途、向缓冲传递 CPU 端的 TypedArray 数据并指明绘制模式,通过 gl 变量的 enableVertexAttribArray
、vertexAttribPointer
方法来启用着色器中 attribute 的坑位、告诉着色器如何从 VertexBuffer 中获取顶点数据。
一个非常简单的顶点着色器:
precision mediump float;
attribute vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0.0, 0.0);
}
如果用高版本的语法(譬如 WebGL 2.0 中用更高版本的 glsl 语法),你可以这样写:
#version 300 es
precision mediump float;
layout(location = 0) in vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0.0, 0.0);
}
const verticesData = [
// 坐标 xy // 颜色 RGBA
-0.5, 0.0, 1.0, 0.0, 0.0, 1.0, // ← 顶点 1
0.0, 0.5, 0.0, 1.0, 0.0, 1.0, // ← 顶点 2
0.5, 0.0, 0.0, 0.0, 1.0, 1.0 // ← 顶点 3
])
const verticesBuffer = device.createBuffer({
size: vbodata.byteLength,
usage: GPUBufferUsage.VERTEX,
mappedAtCreation: true // 创建时立刻映射,让 CPU 端能读写数据
})
// 让 GPUBuffer 映射出一块 CPU 端的内存,即 ArrayBuffer,此时这个 Float32Array 仍是空的
const verticesBufferArray = new Float32Array(verticesBuffer.getMappedRange())
// 将数据传入这个 Float32Array
verticesBufferArray.set(verticesData)
// 令 GPUBuffer 解除映射,此时 verticesBufferArray 那块内存才能被 GPU 访问
verticesBuffer.unmap()
WebGPU 创建 VertexBuffer 是调取设备对象的 createBuffer
方法,返回一个 GPUBuffer
对象,它所需要的是指定 GPUBuffer 的类型以及缓冲的大小。如何写入这块缓冲呢?那还要提到“映射”这个概念。
映射简单的说就是让 CPU/GPU 单边访问。此处创建 GPUBuffer 的参数中有一个 mappedAtCreation
表示创建时就映射。
关于 WebGPU 中 Buffer 的映射、解映射,我有一篇专门的文章介绍,这里不展开过多了。
上面代码中 verticesBuffer.getMappedRange()
返回的是一个 ArrayBuffer,随后才进行 set 操作来填充数据。数据填充完毕后,还需要 unmap
来解映射,以供后续 GPU 能访问。
顶点着色阶段是 渲染管线(GPURenderPipeline) 的一个组成部分,管线需要知道顶点缓冲的数据规格,由着色器模块告知。
创建渲染管线需要 着色器模块对象(GPUShaderModule
),顶点着色器模块的创建参数就有一个 buffers
属性,是一个数组,用于描述顶点着色器中访问到的顶点数据规格:
const vsShaderModule = device.createShaderModule({
// ...
buffers: [
{
// 2 个 float32 代表 xy 坐标
shaderLocation: 0,
offset: 0,
format: 'float32x2'
}, {
// 4 个 float32 代表 rgba 色值
shaderLocation: 1,
offset: 2 * verticesData.BYTES_PER_ELEMENT,
format: 'float32x4'
}
]
})
详细资料可查阅官方 API 文档中关于设备对象的 createShaderModule 方法的要求。
使用 渲染通道编码器(GPURenderPassEncoder
) 来编码单个渲染通道的全流程,其中有一步要设置该通道的顶点缓冲。这个比较简单:
// ...
renderPassEncoder.setVertexBuffer(0, verticesBuffer)
// ...
struct PositionColorInput {
@location(0) in_position_2d: vec2<f32>;
@location(1) in_color_rgba: vec4<f32>;
};
struct PositionColorOutput {
@builtin(position) coords_output: vec4<f32>;
@location(0) color_output: vec4<f32>;
};
@stage(vertex)
fn main(input: PositionColorInput)
-> PositionColorOutput {
var output: PositionColorOutput;
output.color_output = input.in_color_rgba;
output.coords_output = vec4<f32>(input.in_position_2d, 0.0, 1.0);
return output;
}
WGSL 着色器代码可以自定义顶点着色器的入口函数名称、传入参数的结构,也可以自定义向下一阶段输出(即返回值)的结构。
可以看到,为了接收来自 WebGPU API 传递进来的顶点属性,即自定义结构中的 PositionColorInput
结构体中的 xy 坐标 in_position_2d
,以及颜色值 in_color_rgba
,需要有一个“特性”,叫做 location
,它括号里的值与着色器模块对象中的 shaderLocation 必须对应上。
而对于输出,代码中则对应了结构体 PositionColorOutput
,其中向下一阶段(即片段着色阶段)输出用到了内置特性(builtin),叫做 position
,以及自定义的一个 vec4:color_output,它是片段着色器中光栅化后的颜色,这两个输出,类似 glsl 中的 varying(或者out)作用。
创建 GPUBuffer 的时候,如果没有 mappedAtCreation: true
,那么内存、显存都没有被申请。
经过代码测试,当执行映射请求且成功映射后,内存就会占用掉对应的 GPUBuffer 的 size,此时完成了 ArrayBuffer 的创建,是要占空间的。
那么什么时候显存会被申请呢?猜测是 device.queue.commit()
时,指令缓冲携带着各种通道、各种 Buffer 一并传递给 GPU,执行指令缓冲,希望有高手测试我的猜测。
至于销毁,我使用 destory
方法测试 CPU 的内存情况,发现两分钟内并未回收,这一点待测试 ArrayBuffer 的回收情况。
gl.vertexAttribPointer()
方法的作用类似于 device.createShaderModule()
中 buffers
的作用,告诉着色器顶点缓冲单个顶点的数据规格。
gl.createBuffer()
和 device.createBuffer()
是类似的,都是创建一个 CPU 端内存中的 Buffer 对象,但实际并没有传入数据。
数据传递则不大一致了,WebGL 同一时刻只能指定一个 VertexBuffer,所以 gl.bindBuffer()
、gl.bufferData()
一系列函数调用下来都沿着逻辑走;而 WebGPU 则需要经过映射和解映射。
在 WebGPU 中最重要的是,在 renderPassEncoder 记录发出 draw 指令之前,要调用 renderPassEncoder.setVertexBuffer()
方法显式指定用哪一个 VertexBuffer。
着色器代码请读者自行比对研究,只是语法上的差异。
VAO 我也写过一篇《WebGPU 中消失的 VAO》,这里就不详细展开了,有兴趣的读者请移步我的博客列表找找。
WebGPU 中已经不需要 VAO 了,源于 WebGPU 的机制与 WebGL 不同,VAO 本身是 OpenGL 体系提出的概念,它能节约 WebGL 切换顶点相关状态时的负担,也就是帮你缓存下来一个 VBO 的设定状态,而无需再 gl.bindBuffer()
、gl.bufferData()
、gl.vertexAttribPointer()
等再来一遍。
WebGPU 的装配式思想天然就与 VAO 是一致的。VAO 的职能转交给 GPURenderPipeline
完成,其创建参数 GPURenderPipelineDescriptor.vertex.buffers
属性是 GPUVertexBufferLayout[]
类型的,这每一个 GPUVertexBufferLayout
对象就有一部分 VAO 的职能。
一些网站说你应该通过以下方式初始化 webgl: var gl = c.getContext("webgl") || c.getContext("experimental-webgl"); if (!
我一直在寻找有关 WebGL 的信息以及可以分配用于渲染的最大纹理数/内存量。这显然是特定于硬件/设备的,因此我正在寻找一种智能处理纹理的方法。 我目前有 512x512 RGBA8 格式的纹理。一个
我想知道是否可以利用WebGL进行任何异步调用? 我研究了Spec v1和Spec v2,他们什么都没提及。在V2中,有一种WebGL查询机制,我认为这不是我想要的。 在网络上进行搜索并没有确定的定义
我正在参与一个 webgl 项目。 当我调用 gl.DrawElements 时,会显示错误“范围超出缓冲区范围”。 我肯定确保我传递了正确的缓冲区长度或偏移量。但是,仍然显示错误。 我认为有几个原因
我知道 WebGL 中有 8 个纹理的限制。 我的问题是,8 是全局限制还是每个着色器/程序明智的? 如果它是每个着色器/程序明智的限制,这是否意味着,一旦我将纹理加载到一个着色器的制服上,我就可以开
我一直在使用 Haxe + Away3D 编写一个小型行星生成器,并将其部署到 HTML5/WebGL。但是在渲染云时我遇到了一个奇怪的问题。我有行星网格,然后云网格在相同位置稍大一些。 我正在使用柏
关闭。这个问题是opinion-based 。目前不接受答案。 想要改进这个问题吗?更新问题,以便 editing this post 可以用事实和引文来回答它。 . 已关闭 8 年前。 Improv
在 OpenGL 中,深度缓冲区值是根据场景的近和远裁剪平面计算的。 (引用:Getting the true z value from the depth buffer) 这在 WebGL 中是如何
简单的问题,但我无法在任何地方的规范中找到答案。我可能在某处遗漏了明显的答案。 我可以在 WebGL 片段着色器中同时使用多少个纹理?如果它是可变的,那么假设 PC 使用的合理数字是多少? (对移动不
我有一个渲染场景的帧缓冲区,现在我想将它渲染到“全屏”四边形。如何设置我的相机以及我应该在我的顶点着色器中放置什么以便将帧缓冲区的纹理渲染到整个屏幕。 我试过像这样创建一个全屏四边形 var gl =
我正在阅读 here 的教程。 var gl; function initGL() { // Get A WebGL context var canvas = document.getEle
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引起辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the he
我正在学习 WebGL,我能感觉到我的速度很慢,因为我很难调试我的代码。是否有任何扩展或工具可以帮助我知道缓冲区、属性指针、矩阵等的值。 我在谷歌上搜索并了解了 chrome 扩展程序 spector
我可以在某处找到任何文档来记录 WebGL 调用所需的先决条件吗? 我已经对 WebGL 基础有了相当深入的了解,但现在我正在创建自己的“框架”,并且我正在更深入地了解。 例如, enableVert
关闭。这个问题是opinion-based .它目前不接受答案。 想改善这个问题吗?更新问题,以便可以通过 editing this post 用事实和引文回答问题. 7年前关闭。 Improve t
我有兴趣在 webgl 中执行一些密集计算,所以它在 GPU 上运行。 大多数文档都讨论了如何渲染图形。 我正在完成非常简单的任务:对于给定的图像,将其转换为灰度,并找到局部最大值的坐标(比其邻居更亮
我目前在 WebGL 中使用这个片段着色器来对照片纹理应用高光/阴影调整。 着色器本身是直接从优秀的 GPUImage 中拉出来的适用于 iOS 的库。 uniform sampler2D input
我是 webgl 的新手。我正在尝试设置时间统一,因此我可以随着时间的推移更改片段着色器的输出。我认为这实现起来相当简单,但我正在努力。我知道这两种方法可能涉及: https://developer.
我正在尝试使用两个 Canvas 并排绘制相同的 WebGL 场景。是否可以?到目前为止,我还没有走运。 思路如下: 我加载几何 我设置了两个gl上下文,每幅 Canvas 一个 我调用 drawEl
我正在学习 WebGL 并尝试显示一个球体。没有纹理,只有每个顶点着色,但我在 Opera 和 Chrome 中收到以下错误消息:“[.WebGLRenderingContext]GL 错误:GL_I
我是一名优秀的程序员,十分优秀!