Cesium 源码解析 float 与 rgba相互转化

1、float深度值转rgba纹素值        

        cesium中将float转换为rgba主要是为了解决显卡不支持float类型纹理的问题,例如将像素着色器中的深度提取出来,由于在opengl的规范当中将pixel shader中的数据提取出来的方法是保存成像素值,而低端的显卡硬件都支持将像素保存成rgba格式的[0~1]范围的值,这就需要将float转换成rgba的方式进行渲染,而后在将这个渲染出的结果中还原原来的float数据。

        cesium中glsl相关的代码是:

/**
 * Unpacks a vec4 depth value to a float in [0, 1) range.
 *
 * @name czm_unpackDepth
 * @glslFunction
 *
 * @param {vec4} packedDepth The packed depth.
 *
 * @returns {float} The floating-point depth in [0, 1) range.
 */
 float czm_unpackDepth(vec4 packedDepth)
 {
    // See Aras Pranckevičius' post Encoding Floats to RGBA
    // http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/
    return dot(packedDepth, vec4(1.0, 1.0 / 255.0, 1.0 / 65025.0, 1.0 / 16581375.0));
 }

/**
 * Packs a depth value into a vec3 that can be represented by unsigned bytes.
 *
 * @name czm_packDepth
 * @glslFunction
 *
 * @param {float} depth The floating-point depth.
 * @returns {vec3} The packed depth.
 */
vec4 czm_packDepth(float depth)
{
    // See Aras Pranckevičius' post Encoding Floats to RGBA
    // http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/
    vec4 enc = vec4(1.0, 255.0, 65025.0, 16581375.0) * depth;
    enc = fract(enc);
    enc -= enc.yzww * vec4(1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0, 0.0);
    return enc;
}

整个计算过程如下:

将float转换为rbga的过程vec4 czm_packDepth(float depth)如下:

1.1 假设depth为深度值,这个深度值是介于(0~1)开区间之间的,对于不在这个范围内的值可以将这个值除以远裁切面,既然在(0~1)之间,我们可以假设depth为下面的结果,至于为什么可以这样拆解,主要是depth的范围(0~1),而rgba中每个分量也在[0~1]之间,同时因为数据压缩了,必定会出现精度损失,但是在这个可以认为这个精度可以接受,所以g/255.0、b/255.0/255.0会很小,可以拆解成下面的结果。

        depth = (r + g / 255.0 + b/255.0^{2} + a/255.0^{3})

1.2 对于代码的第一步,对应的计算过程enc,假设一个中间量key,

        key = (1^{}.0,255,255.0^{2}, 255.0^{3})

        enc = depth \cdot key = (depth, depth * 255.0, depth * 255.0^{2}, depth * 255.0^{3})

        enc = \begin{bmatrix} r+g/255.0+b/255.0^{2}+a/255.0^{3}\\ r*255.0+g+b/255.0+a/255.0^{2}\\ r*255.0^{2}+g*255.0+b+a/255.0\\ r*255.0^{3}+g*255.0^{2}+b*255.0+a\\ \end{bmatrix}

1.3 对于代码的第二步,将enc向量中的每个成员取小数, 因为

         enc = \begin{bmatrix} r+g/255.0+b/255.0^{2}+a/255.0^{3}\\ g+b/255.0+a/255.0^{2}\\b+a/255.0\\ a\\ \end{bmatrix}

1.4  假设一个中间变量m为

        m = (1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0, 0.0)

        n = enc.yzww * m = \begin{bmatrix} g/255.0 + b/255.0^{2} + a/255.0^{3}\\ b/255.0 + a/255.0^{2}\\ a/255.0\\ a/255.0 \end{bmatrix}

       result = enc - n = (r,g,b,a-a/255.0)\approx (r,g,b,a)

所以结果就是原来depth假设的各个分量

1.5 如何将result还原为原来的depth,

        depth = result\cdot (1.0, 1.0/255.0, 1.0/255.0^{2}, 1.0/255.0^{2})

                   = (r + g / 255.0 + b/255.0^{2} + a/255.0^{3})

总结:

        对于float转rgba的原理,可以参考位权的方式,例如对于1234这个十进制数,按照位权展开是

                1234 = 1*10^3 + 2*10^2 + 3*10^1 + 4

如果将1234这个整数存储在rgba中,假如每个分量存储空间是4位,那么r=1,g=2, b=3, a=4;

        而对于真实情况向rgba,每个分量的存储空间是8位[0-255],将将原来的数据按照255的位权展开(即255进制),就可以提取出每个位权对应的数据。

              1234 = 4*255 + 214

所以        r=0,g=0, b=4, c=255

        对于存储shader中的depth方法而言,可以存储经过透视除法之后的深度信息,因为此时的depth范围为[0~1],是一个小数,如何将小数转换成byte类型的rgba数据呢,还是使用位权的方式,计算机相关的书籍都会将如何存储将10进制小数转换为二进制小数的方式,或者将二进制小数转换为10进制小数。

        (11.75)d转化为(1011.11)b,同理将[0~1]之间的的小数转换成rgba就可以按照这种方法,整数部分除以255,小数部分乘以255*255*255取r,g,b,a.的方式。

2、数据压缩

        cesium在处理地形数据时,会将地形数据进行编码压缩后传递给gpu,其中对uv坐标的压缩代码如下:

/**
 * Pack texture coordinates into a single float. The texture coordinates will only preserve 12 bits of precision.
 *
 * @param {Cartesian2} textureCoordinates The texture coordinates to compress.  Both coordinates must be in the range 0.0-1.0.
 * @returns {number} The packed texture coordinates.
 *
 */
AttributeCompression.compressTextureCoordinates = function (
  textureCoordinates
) {
  //>>includeStart('debug', pragmas.debug);
  Check.defined("textureCoordinates", textureCoordinates);
  //>>includeEnd('debug');

  // Move x and y to the range 0-4095;
  // 4096就进位了,所以乘以4095,
  const x = (textureCoordinates.x * 4095.0) | 0;
  const y = (textureCoordinates.y * 4095.0) | 0;
  // x最大为4095,在乘以4096不会进位
  return 4096.0 * x + y;
};

/**
 * Decompresses texture coordinates that were packed into a single float.
 *
 * @param {number} compressed The compressed texture coordinates.
 * @param {Cartesian2} result The decompressed texture coordinates.
 * @returns {Cartesian2} The modified result parameter.
 *
 */
AttributeCompression.decompressTextureCoordinates = function (
  compressed,
  result
) {
  //>>includeStart('debug', pragmas.debug);
  Check.defined("compressed", compressed);
  Check.defined("result", result);
  //>>includeEnd('debug');

  const temp = compressed / 4096.0;
  const xZeroTo4095 = Math.floor(temp);
  result.x = xZeroTo4095 / 4095.0;
  result.y = (compressed - xZeroTo4095 * 4096) / 4095;
  return result;
};

a、uv坐标压缩过程

        将uv两个分量压缩成一个数值,因为uv的范围都是[0~1],所以扩展成整数并占用12位的[0~4095]范围,注意位权是4096,所以最大只能是4095,超过4095就进位了。

        x分量占24位的前12位,y分量占24位的后12个位,注意大小端的情况。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值