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;
}
整个计算过程如下:
1 将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会很小,可以拆解成下面的结果。
1.2 对于代码的第一步,对应的计算过程enc,假设一个中间量key,
1.3 对于代码的第二步,将enc向量中的每个成员取小数, 因为
1.4 假设一个中间变量m为
所以结果就是原来depth假设的各个分量
1.5 如何将result还原为原来的depth,
总结:
对于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个位,注意大小端的情况。