在WebGPU中创建纹理使用纹理很方便,只是JavaScript中只有Float32Array而没有原生只是的Float16Array类型,所以略微费点事。不过网上的大神多的是,摇摇小手就能获得解决方案。
废话多了容易挨胖揍,看代码。
js中float16单精度float数值转换:
- // thanks: https://esdiscuss.org/topic/float16array
- const toFloat16 = (function() {
- var floatView = new Float32Array(1);
- var int32View = new Int32Array(floatView.buffer);
- /* This method is faster than the OpenEXR implementation (very often
- * used, eg. in Ogre), with the additional benefit of rounding, inspired
- * by James Tursa?s half-precision code. */
- return function toHalf(val: number) {
- floatView[0] = val;
- var x = int32View[0];
-
- var bits = (x >> 16) & 0x8000; /* Get the sign */
- var m = (x >> 12) & 0x07ff; /* Keep one extra bit for rounding */
- var e = (x >> 23) & 0xff; /* Using int is faster here */
-
- /* If zero, or denormal, or exponent underflows too much for a denormal
- * half, return signed zero. */
- if (e < 103) {
- return bits;
- }
-
- /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */
- if (e > 142) {
- bits |= 0x7c00;
- /* If exponent was 0xff and one mantissa bit was set, it means NaN,
- * not Inf, so make sure we set one mantissa bit too. */
- bits |= (e == 255 ? 0 : 1) && x & 0x007fffff;
- return bits;
- }
-
- /* If exponent underflows but not too much, return a denormal */
- if (e < 113) {
- m |= 0x0800;
- /* Extra rounding may overflow and set mantissa to 0 and exponent
- * to 1, which is OK. */
- bits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1);
- return bits;
- }
-
- bits |= ((e - 112) << 10) | (m >> 1);
- /* Extra rounding. An overflow will set mantissa to 0 and increment
- * the exponent, which is OK. */
- bits += m & 1;
- return bits;
- };
- })();
基于上述数值转换,实现单精度浮点数纹理(rgba16float格式纹理):
- private createFloat16Texture(width: number, height: number): GPUTexture {
-
- let data = new Uint16Array(width * height * 4);
- let scale = 1.0;
- let k = 0;
- for (let i = 0; i < height; ++i) {
- for (let j = 0; j < width; ++j) {
- k = (width * i + j) * 4;
- data[k] = toFloat16(scale * (j/width));
- data[k+1] = toFloat16(scale * (0.5 + 0.5 * Math.sin(10.0 * (1.0 - j/width))));
- data[k+2] = toFloat16(scale * (1.0 - (i * j)/(width * height)));
- data[k+3] = toFloat16(scale * 1.0);
- }
- }
-
- const texture = device.createTexture({
- size: { width, height },
- format: "rgba16float",
- usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST
- });
- // 下面的8表示一个像素需要4个float16也就是8个字节的内存来读写
- device.queue.writeTexture({ texture }, data, {bytesPerRow: width * 8, rowsPerImage: height}, { width, height });
- return texture;
- }
上述代码呈现的纹理效果:

此引擎系统当前示例RGBA8纹理和Float16纹理运行效果:

此示例基于此渲染系统实现,当前示例TypeScript源码如下:
- export class DataTextureTest {
- private mRscene = new RendererScene();
-
- initialize(): void {
-
- this.initScene();
- }
- private applyRGBAFloat16Tex(): void {
- let rc = this.mRscene;
-
- let width = 256;
- let height = 256;
-
- let dataFs32 = new Float32Array(width * height * 4);
- let scale = 10.0;
- let k = 0;
- for (let i = 0; i < height; ++i) {
- for (let j = 0; j < width; ++j) {
- k = (width * i + j) * 4;
- dataFs32[k] = scale * (j / width);
- dataFs32[k + 1] = scale * (0.5 + 0.5 * Math.sin(10.0 * (1.0 - j / width)));
- dataFs32[k + 2] = scale * (1.0 - (i * j) / (width * height));
- dataFs32[k + 3] = scale * 1.0;
- }
- }
- const tex = {
- diffuse: { uuid: "tex0", dataTexture: { data: dataFs32, width, height }, format: "rgba16float", generateMipmaps: true }
- };
-
- let entity = new FixScreenPlaneEntity({ extent: [-0.8, -0.8, 0.8, 0.8], textures: [tex] });
- entity.color = [0.1, 0.1, 0.1, 0.1];
- rc.addEntity(entity);
- }
-
- private applyRGBA8Tex(): void {
- let rc = this.mRscene;
-
- let width = 256;
- let height = 256;
-
- let dataU8 = new Uint8Array(width * height * 4);
- let k = 0;
- for (let i = 0; i < height; ++i) {
- for (let j = 0; j < width; ++j) {
- k = (width * i + j) * 4;
- dataU8[k] = ((j / width) * 255) | 0;
- dataU8[k + 1] = ((0.5 + 0.5 * Math.sin(10.0 * (1.0 - j / width))) * 255) | 0;
- dataU8[k + 2] = ((1.0 - (i * j) / (width * height)) * 255) | 0;
- dataU8[k + 3] = 255;
- }
- }
-
- let tex = {
- diffuse: { uuid: "tex1", dataTexture: { data: dataU8, width, height }, format: "rgba8unorm", generateMipmaps: true }
- };
-
- let entity = new FixScreenPlaneEntity({ extent: [0.0, 0.0, 0.8, 0.8], textures: [tex] });
- rc.addEntity(entity);
- }
- private initScene(): void {
- this.applyRGBAFloat16Tex();
- this.applyRGBA8Tex();
- }
-
- run(): void {
- this.mRscene.run();
- }
- }
注: 这里的纹理应用基于系统的对应功能的封装实现。