2

为了使用WebGL在chrome中手动打包RGBA浮动组件纹理并将其绑定到GPU,请使用OES_texture_float(OES_纹理_浮动)扩展名,像素组件数据必须存储在Float32Array中。

例如,对于一个简单的3像素纹理,每个纹理有4个浮点分量,首先声明一个普通的JS数组:

var像素=[1.01,1.02,1.03,1.04,2.01,2.02,2.03,2.04,3.01,3.02,3.03];

然后,为了将普通JS数组转换为可以馈送到GPU的强类型浮点数组,我们只需使用Float32Array构造函数,该构造函数可以将普通的JS数组作为输入:

像素=新的Float32Array(像素);

现在,我们已经将纹理表示为一个强类型的浮点数组,我们可以使用已经建立的WebGL上下文(该上下文正在工作并且超出了此问题的范围),使用texImage2D将其提供给GPU:

gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,3,1,0,gl.RGBA、gl.FLOAT,像素);

进行适当的呈现调用表明,这些浮点正在传入和传出(通过将输出浮点编码为片段颜色)GPU,没有出现错误(尽管由于转换导致精度略有下降)。

问题

从普通JS数组转换为Float32Array实际上是一项非常昂贵的操作,而且在Float32Array中操作已经转换的浮点要快得多——根据大多数浮动的JS规范,似乎支持这种操作:https://developer.mozilla.org/en-US/docs/Web/API/Float32Array网站

建立后,可以使用对象的方法或使用标准数组索引语法(即使用括号表示法)引用数组中的元素。

出现问题的原因是:

使用预设值的普通JS数组创建Float32Array

我们使用[]符号更改Float32Array中的一个或多个值,即:

像素[0]=420.4;像素[1]=420.4;

我们使用texImage2D将Float32Array传递给GPU,并且使用上面提到的相同方法表明,对Float32Array的初始设置值以某种方式传递给了GPU,而没有将这两个值更改为420.4

世界跆拳道联盟?

我的最佳猜测是,由于强类型数组(通常)在内部表示为缓冲区和视图,所以我正在更新视图,而缓冲区没有反映更改。将Float32Array记录到浏览器控制台显示,在这种情况下,两个更改的数字似乎确实发生了更改。然而,由于ArrayBuffer的内容无法通过Chrome控制台读取,就我的技能而言,这是一个调试死胡同。

使用NodeJS REPL尝试相同的创建、转换、更新和检查方法(不涉及GPU),可以发现缓冲区值在计算像素[0]=420.4;并且在读取缓冲区时不会以“懒惰”的方式进行更新。

Chrome可能会延迟更新底层缓冲区,但将数据复制到GPU不会触发getter,而是从内存中复制原始数据。

临时解决方案

在发现并解决潜在问题之前(即使适用),似乎Float32Arrays在WebGL纹理上下文中创建后本质上是不可变的(无法更改)。似乎还有一个.set()方法附加到类型化数组,但这:

pixels.set(新的Float32Array([420.4]),索引);

似乎需要很多外部装箱/转换来绕过一个惰性缓冲区,尤其是一个声称允许[]访问的缓冲区。

0

1答案1

重置为默认值
8

类型化数组(包括Float32Arrays)只是压缩数组(想想C/C++)。更新它们是即时的。如果你想在GPU上看到数据,你必须用texImage2D再次上传数据,否则,就没有魔法,没有疯狂的缓冲,非常直接。如果你知道C/C++,它在功能上等价于

void*arrayBuffer=malloc(sizeOfBuffer);float*viewAsFloat=(float*)arrayBuffer;

类型化数组不是JS数组的视图。使用本机JS数组初始化类型化数组只是初始化类型化阵列的一种方便方法。一旦创建,TypedArray就是一个新数组。

不过,您可以将多个ArrayBuffer视图放入同一个Array Buffer中。

例子

var b=新的ArrayBuffer(16);//制作一个16字节的ArrayBuffervar bytes=新Uint8Array(b);//在ArrayBuffer中创建Uint8Array视图var longs=新Uint32Array(b);//在ArrayBuffer中创建Uint32Array视图var floats=新的Float32Array(b);//在ArrayBuffer中创建Float32Array视图//打印视图的内容console.log(字节);console.log(longs);console.log(浮点);//使用其中一个视图更改字节字节[1]=255;//再次打印内容console.log(字节);console.log(longs);console.log(浮点);

将所有代码复制并粘贴到JavaScript控制台中。你应该看到这样的东西

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 0, 0, 0] [0, 0, 0, 0] [0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [65280, 0, 0, 0] [9.1447676375112406e-41,0,0,0]

注意:使用多个视图不同类型的在同一阵列上的缓冲区不兼容跨平台。换句话说,在大端平台和小端平台上会得到不同的结果。目前还没有支持TypedArrays的浏览器的流行大端平台,所以您可以忽略这个问题,尽管您的页面可能会在未来的平台上中断。如果要以独立于平台的方式读/写数据,应使用数据视图否则,在同一缓冲区上使用多个视图的主要目的是上传打包的顶点数据,例如用uint32 RGBA颜色打包的浮点位置。在这种情况下,它将跨平台工作,因为您没有使用视图读/写相同的数据。

如前所述,JS本机数组和TypedArrays没有关系,除非您可以使用JS本机数组初始化TypedArray

var jsArray=[1,2,3,4];var floats=新的Float32Array(jsArray);//这是一个新数组,而不是视图。console.log(jsArray);console.log(浮点);jsArray[1]=567;//仅影响JS数组console.log(jsArray);console.log(浮点);浮点数[2]=89;//仅影响浮点数组console.log(jsArray);console.log(浮点);

粘贴到控制台中

[1, 2, 3, 4] [1, 2, 3, 4] [1, 567, 3, 4] [1, 2, 3, 4] [1, 567, 3, 4] [1, 2, 89, 4]

注意,您可以从任何类型的数组中获取底层ArrayBuffer。

var buffer=floats.buffer;

并创建新视图

var longs=新Uint8Array(缓冲区);console.log(longs);//打印[0,0,128,63,0,0,0,64,0,0178,66,0,0,128,64]

您还可以创建覆盖缓冲区一部分的视图。

var偏移=8;//偏移量以字节为单位var长度=2;//长度以类型为单位//查看最后2个浮点的缓冲区var f2=新的Float32Array(缓冲区、偏移量、长度);控制台日志(f2);//打印[89,4]

至于纹理和类型化数组,这里有一个使用Float32Array更新浮点纹理的片段。

main();函数main(){var canvas=文档.getElementById(“canvas”);var gl=canvas.getContext(“webgl”);如果(!gl){警报(“无WebGL”);回报;}var f=gl.getExtension(“OES_texture_float”);如果(!f){警报(“无OES_texture_float”);回报;}var程序=twgl.createProgramFromScripts(gl,[“2d-vertex-shader”,“2d-fragment-shader”]);gl.useProgram(程序);var positionLocation=gl.getAttribLocation(程序,“a_position”);var resolutionLocation=gl.getUniformLocation(程序,“u_resolution”);gl.uniform2f(分辨率位置、画布宽度、画布高度);var缓冲区=gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER,缓冲区);gl.bufferData(gl.ARRAY_BUFFER,新的Float32Array([-1, -1, 1, -1, -1, 2,-1、1、1,-1、1、1]),gl.STATIC_DRAW);gl.enableVertexAttribArray(位置位置);gl.vertexAttribPointer(位置位置,2,gl.FLOAT,false,0,0);var-tex=gl.createTexture();gl.bindTexture(gl.TEXTURE_2D,tex);var宽度=64;var高度=64;var像素=新的Float32Array(宽度*高度*4);对于(var y=0;y<高度;++y){for(var x=0;x<宽度;++x){var偏移=(y*宽度+x)*4;像素[偏移量+0]=(x*256/宽度)*1000;像素[偏移量+1]=(y*256/高度)*1000;像素[偏移量+2]=(x*y/(宽度*高度))*1000;像素[偏移+3]=256000;}}gl.tex图像2D(gl.TEXTURE_2D,0,gl.RGBA,宽度,高度,0,gl.RGBA,像素);gl.tex参数(gl.TEXTURE_2D,gl.TESTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D、gl.TEXTURE_WRAP_T、gl.CCLAMP_TO_EDGE);gl.tex参数(gl.TEXTURE_2D,gl.TESTURE_MIN_FILTER,gl.NEAREST);gl.tex参数(gl.TEXTURE_2D,gl.TESTURE_MAG_FILTER,gl.NEAREST);函数randInt(范围){return Math.floor(Math.random()*range);}函数render(){//更新随机像素var x=randInt(宽度);var y=randInt(高度);var偏移=(y*宽度+x)*4;像素[偏移量+0]=randInt(256000);像素[偏移量+1]=randInt(256000);像素[偏移量+2]=randInt(256000);gl.tex图像2D(gl.TEXTURE_2D,0,gl.RGBA,宽度,高度,0,gl.RGBA,像素);gl.drawArrays(gl.TRIANGLES,0,6);requestAnimationFrame(渲染);}渲染();}
<script src=“https://twgljs.org/dist/2.x/twgl.min.js“></script><script id=“2d-vertex-shader”type=“x-shader/x-vertex”>属性vec2 a_position;空main(){gl_Position=vec4(a_Position,0,1);}</script><script id=“2d-fragment-shader”type=“x-shader/x-fragment”>精密介质浮子;均匀vec2 u_resolution;均匀采样器2D u_tex;空main(){vec2 texCoord=gl_FragCoord.xy/u_resolution;vec4 floatColor=纹理2D(u_tex,texCoord);gl_FragColor=floatColor/256000.0;}</script><canvas id=“canvas”width=“400”height=“300”></cavas>

所有这些都表明你所看到的问题可能与其他问题有关?至于调试,如上所述,ArrayBuffer非常简单,没有延迟缓冲或任何东西。因此,如果您想查看ArrayBuffer,请为其创建一个视图,这样调试器就可以知道您想要显示什么。

  • 回答得很漂亮,非常感谢!在重构成几个独立的测试之后,它似乎不是类型化数组有问题,而是我可能在某处有一个错误的顶点或纹理坐标-导致p0.r的变化反映在p345808附近。r 评论 2014年1月3日13:45
  • @gman公司:..没有支持TypeArrays的浏览器的流行小端平台。。。你是想在这里说大端吗? 评论 2016年12月21日4:26
  • 啊!哈哈,是的😅 评论 2016年12月21日4:34

你的答案

单击“发布您的答案”,表示您同意我们的服务条款并确认您已阅读我们的隐私政策.

不是你想要的答案吗?浏览标记的其他问题问你自己的问题.