javascript 如何在GLSL / WebGL中将一个32位整数打包成4,8位整数?

bqjvbblv  于 2023-03-28  发布在  Java
关注(0)|答案(3)|浏览(156)

我正在寻找并行化一些复杂的数学,WebGL看起来是完美的方法来做到这一点。问题是,你只能从纹理中读取8位整数。我希望从纹理中获得32位数字。我的想法是使用4个颜色通道来获得每像素32位,而不是4乘以8位。
我的问题是,GLSL没有“%”运算符或任何位运算符!
TLDR:如何使用glsl中的运算符将32位数字转换为4个8位数字。
关于该技术的一些额外信息(使用位运算符):
How to store a 64 bit integer in two 32 bit integers and convert back again

fdx2calv

fdx2calv1#

您可以通过乘以/除以2的幂来进行位移位。
正如在评论中指出的,我最初发布的方法是工作,但不正确的,here's one by Aras Pranckevičius,请注意,在后本身的源代码包含一个错别字,是HLSL,这是一个GLSL端口与错别字更正:

const vec4 bitEnc = vec4(1.,255.,65025.,16581375.);
const vec4 bitDec = 1./bitEnc;
vec4 EncodeFloatRGBA (float v) {
    vec4 enc = bitEnc * v;
    enc = fract(enc);
    enc -= enc.yzww * vec2(1./255., 0.).xxxy;
    return enc;
}
float DecodeFloatRGBA (vec4 v) {
    return dot(v, bitDec);
}
zxlwwiss

zxlwwiss2#

一般来说,如果你想把一个浮点数的有效数字打包成字节,你必须连续地提取有效数字的8位包并将其存储在一个字节中。

对预定义范围内的浮点数进行编码

为了将浮点值打包到4 * 8位缓冲区中,必须首先指定源值的范围。
如果你定义了一个值域[ minValmaxVal ],它必须Map到值域[0.0,1.0]:

float mapVal = clamp((value-minVal)/(maxVal-minVal), 0.0, 1.0);

函数Encode将范围[0.0,1.0]内的浮点值打包为vec4

vec4 Encode( in float value )
{
    value *= (256.0*256.0*256.0 - 1.0) / (256.0*256.0*256.0);
    vec4 encode = fract( value * vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) );
    return vec4( encode.xyz - encode.yzw / 256.0, encode.w ) + 1.0/512.0;
}

函数Decodevec4中提取[0.0,1.0]范围内的浮点值:

float Decode( in vec4 pack )
{
    float value = dot( pack, 1.0 / vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) );
    return value * (256.0*256.0*256.0) / (256.0*256.0*256.0 - 1.0);
}

以下函数将浮点值打包并提取到[ minValmaxVal ]范围内:

vec4 EncodeRange( in float value, flaot minVal, maxVal )
{
    value = clamp( (value-minVal) / (maxVal-minVal), 0.0, 1.0 );
    value *= (256.0*256.0*256.0 - 1.0) / (256.0*256.0*256.0);
    vec4 encode = fract( value * vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) );
    return vec4( encode.xyz - encode.yzw / 256.0, encode.w ) + 1.0/512.0;
}

float DecodeRange( in vec4 pack, flaot minVal, maxVal )
{
    value = dot( pack, 1.0 / vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) );
    value *= (256.0*256.0*256.0) / (256.0*256.0*256.0 - 1.0);
    return mix( minVal, maxVal, value );
}

用指数编码浮点数

另一种可能性是将有效数字编码为RGB值的3 * 8位,并将指数编码为Alpha通道的8位:

vec4 EncodeExp( in float value )
{
    int exponent  = int( log2( abs( value ) ) + 1.0 );
    value        /= exp2( float( exponent ) );
    value         = (value + 1.0) * (256.0*256.0*256.0 - 1.0) / (2.0*256.0*256.0*256.0);
    vec4 encode   = fract( value * vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) );
    return vec4( encode.xyz - encode.yzw / 256.0 + 1.0/512.0, (float(exponent) + 127.5) / 256.0 );
}

float DecodeExp( in vec4 pack )
{
    int exponent = int( pack.w * 256.0 - 127.0 );
    float value  = dot( pack.xyz, 1.0 / vec3(1.0, 256.0, 256.0*256.0) );
    value        = value * (2.0*256.0*256.0*256.0) / (256.0*256.0*256.0 - 1.0) - 1.0;
    return value * exp2( float(exponent) );
}

请注意,由于标准的32位IEEE 754数只有24个有效位,因此将该数编码为3个字节完全足够。
另请参见How do I convert between float and vec4,vec3,vec2?

mklgxw1f

mklgxw1f3#

每个人在如何在WebGl中处理这样的事情上都是绝对正确的,但我想分享一个获取值的技巧。
假设你想对两个16位的值进行比较:

// Generate a list of random 16bit integers
let data16bit = new Uint16Array(1000000);
for(let i=0; i < data16bit.length; i+=2){
    data16bit[i]   = Math.random()*(2**16);
    data16bit[i+1] = Math.random()*(2**16);
}
// Read them one byte at a time, for writing to 
// WebGL
let texture = new Uint8Array(data16bit.buffer);

现在,当您在片段着色器中获取值时,您可以拾取数字进行操作:

vec4 here = texture2D(u_image, v_texCoord);
// Read the "red" byte and the "green" byte together (as a single thing) 
// as well as the "blue" byte and the "alpha" byte together as a single
// thing
vec2 a = here.rg;
vec2 b = here.ba;
// now compare the things
if(a == b){
    here.a = 1;
}
else{
    here.a = 0;
}
// return the boolean value
gl_FragColor = here;

这一点只是提醒你,你可以把同一块JavaScript内存当作不同的大小:Uint16ArrayUint8Array(而不是尝试进行位移位并将其分解)。

更新

为了响应更多细节的请求,该代码非常接近于直接从this code and explanation剪切/粘贴。
这个的确切用法可以在GitLab上的相应示例中找到(同一文件的两个部分)

相关问题