Cesium 和 three.js 对数深度缓冲原理简析
对数深度缓冲原理
为啥需要对数深度?
深度缓冲通常使用 浮点数 来存储深度值,但浮点数的精度有限,尤其是在处理远距离物体时,深度值之间的差异可能非常小,导致精度不足,出现 深度冲突 (Z-Fighting) 问题。
为了解决这个问题,可以使用 对数深度 (Logarithmic Depth) 技术。对数深度将线性深度值转换为对数空间,从而提高远距离物体的深度精度。
1、普通深度
float z_clip = gl_Position.z;// gl_Position.w = -z_view, 这是应用对数的关键点
float w_clip = gl_Position.w;// [-1, 1]
float z_ndc = z_clip/w_clip;// [0, 1]
float depth = z_ndc * 0.5 + 0.5;gl_FragDepth = gl_FragCoord.z = depth;
2、对数深度原理
普通对数变换到0~1之间:
普通 z n d c z_{ndc} zndc图像:
y n o r a m l = ( f + n ) f − n + 2 f n ( f − n ) z y_{noraml} =\ \frac{\left(f+n\right)}{f-n}+\frac{2fn}{\left(f-n\right)z} ynoraml= f−n(f+n)+(f−n)z2fn
使用直接对 z v i e w z_{view} zview取对数:
y l o g T h r e e = log 2 ( − z + 1 ) ( 1 log 2 ( f + 1 ) ) y_{logThree}=\log_{2}\left(-z+1\right)\left(\frac{1}{\log_{2}\left(f+1\right)}\right) ylogThree=log2(−z+1)(log2(f+1)1)
$near = 0.1, far = 5 $时, 的图像:
3、Cesium 实现对数深度
y l o g C e s i u m = log 2 ( − z − n + 1 ) ( 1 log 2 ( f − n + 1 ) ) y_{logCesium} =\log_{2}\left(-z-n+1\right)\left(\frac{1}{\log_{2}\left(f-n+1\right)}\right) ylogCesium=log2(−z−n+1)(log2(f−n+1)1)
const czm_oneOverLog2FarDepthFromNearPlusOne = 1.0 / CesiumMath.log2(frustum.far - frustum.near + 1.0);
void czm_writeLogDepth() {float depth = (gl_Position.w - czm_currentFrustum.x) + 1.0;gl_FragDepth = log2(depth) * czm_oneOverLog2FarDepthFromNearPlusOne;
}
4、three.js 实现对数深度
const logDepthBufFC = 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 );
gl_FragDepth = log2( 1.0 + gl_Position.w ) * logDepthBufFC * 0.5;
y l o g T h r e e = log 2 ( − z + 1 ) ( 1 log 2 ( f + 1 ) ) y_{logThree}=\log_{2}\left(-z+1\right)\left(\frac{1}{\log_{2}\left(f+1\right)}\right) ylogThree=log2(−z+1)(log2(f+1)1)
处理一下 log 2 ( f + 1 ) \log_{2}(f+1) log2(f+1), 应用对数的换底公式:
log a b = log c b log c a \log_ab = \frac{\log_cb}{\log_ca} logab=logcalogcb
log 2 ( f + 1 ) = log e ( f + 1 ) log e ( 2 ) \log_{2}(f+1) = \frac{\log_e(f+1)}{\log_e(2)} log2(f+1)=loge(2)loge(f+1)