cesium中的对数深度

1,375 阅读2分钟

前言

cesium中的深度一般都有两种解决方式,一种是对数深度(默认),另一种是多视锥体,当然还有其他解决方法,比如补偿深度缓存等,由于公司之后计划做cesium与babylon.js的深度融合,因此需要研究一下cesium部分源码

首先总结一下cesium与three.js深度融合的几个要点

  1. 共用上下文。
  2. 相机同步。
  3. 深度保持一致

现在主要记录一下cesium关于深度的一些问题

对数深度

1.写入

1.1.定义 oneOverLog2FarDepthFromNearPlusOne

/**
 * Synchronizes the frustum's state with the uniform state.  This is called
 * by the {@link Scene} when rendering to ensure that automatic GLSL uniforms
 * are set to the right value.
 *
 * @param {Object} frustum The frustum to synchronize with.
 */
UniformState.prototype.updateFrustum = function (frustum) {
  setProjection(this, frustum.projectionMatrix);
  if (defined(frustum.infiniteProjectionMatrix)) {
    setInfiniteProjection(this, frustum.infiniteProjectionMatrix);
  }
  this._currentFrustum.x = frustum.near;
  this._currentFrustum.y = frustum.far;

  this._farDepthFromNearPlusOne = frustum.far - frustum.near + 1.0;
  this._log2FarDepthFromNearPlusOne = CesiumMath.log2(
    this._farDepthFromNearPlusOne
  );
  this._oneOverLog2FarDepthFromNearPlusOne =
    1.0 / this._log2FarDepthFromNearPlusOne;

  if (defined(frustum._offCenterFrustum)) {
    frustum = frustum._offCenterFrustum;
  }

  this._frustumPlanes.x = frustum.top;
  this._frustumPlanes.y = frustum.bottom;
  this._frustumPlanes.z = frustum.left;
  this._frustumPlanes.w = frustum.right;
};

1.2 传值到shader中


  /**
   * Gets 1.0 divided by {@link AutomaticUniforms#czm_log2FarDepthFromNearPlusOne}.
   */
  czm_oneOverLog2FarDepthFromNearPlusOne: new AutomaticUniform({
    size: 1,
    datatype: WebGLConstants.FLOAT,
    getValue: function (uniformState) {
      return uniformState.oneOverLog2FarDepthFromNearPlusOne;
    },
  }),
  
    /**
   * The far plane's distance from the near plane, plus 1.0.
   *
   * @memberof UniformState.prototype
   * @type {Number}
   */
  farDepthFromNearPlusOne: {
    get: function () {
      return this._farDepthFromNearPlusOne;
    },
  },

1.3 写入深度

/**
* Writes the fragment depth to the logarithmic depth buffer.
* <p>
* Use this when the vertex shader does not call {@link czm_vertexlogDepth}, for example, when
* ray-casting geometry using a full screen quad.
* </p>
* @name czm_writeLogDepth
* @glslFunction
*
* @param {float} depth The depth coordinate, where 1.0 is on the near plane and
*                      depth increases in eye-space units from there
*
* @example
* czm_writeLogDepth((czm_projection * v_positionEyeCoordinates).w + 1.0);
*/
void czm_writeLogDepth(float depth)
{
#if defined(GL_EXT_frag_depth) && defined(LOG_DEPTH)
   // Discard the vertex if it's not between the near and far planes.
   // We allow a bit of epsilon on the near plane comparison because a 1.0
   // from the vertex shader (indicating the vertex should be _on_ the near
   // plane) will not necessarily come here as exactly 1.0.
   if (depth <= 0.9999999 || depth > czm_farDepthFromNearPlusOne) {
       discard;
   }

#ifdef POLYGON_OFFSET
   // Polygon offset: m * factor + r * units
   float factor = u_polygonOffset[0];
   float units = u_polygonOffset[1];

   // If we can't compute derivatives, just leave out the factor I guess?
#ifdef GL_OES_standard_derivatives
   // m = sqrt(dZdX^2 + dZdY^2);
   float x = dFdx(depth);
   float y = dFdy(depth);
   float m = sqrt(x * x + y * y);

   // Apply the factor before computing the log depth.
   depth += m * factor;
#endif

#endif

   gl_FragDepthEXT = log2(depth) * czm_oneOverLog2FarDepthFromNearPlusOne;

#ifdef POLYGON_OFFSET
   // Apply the units after the log depth.
   gl_FragDepthEXT += czm_epsilon7 * units;
#endif

#endif
}

1.4 读取深度

float czm_readDepth(sampler2D depthTexture, vec2 texCoords)
{
    return czm_reverseLogDepth(texture2D(depthTexture, texCoords).r);
}

float czm_reverseLogDepth(float logZ)
{
#ifdef LOG_DEPTH
    float near = czm_currentFrustum.x;
    float far = czm_currentFrustum.y;
    float log2Depth = logZ * czm_log2FarDepthFromNearPlusOne;
    float depthFromNear = pow(2.0, log2Depth) - 1.0;
    return far * (1.0 - near / (depthFromNear + near)) / (far - near);
#endif
    return logZ;
}

1.5 打包深度和解包深度

/**
 * 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;
}



 * 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));
 }

应用

uniform sampler2D u_depthTexture;

varying vec2 v_textureCoordinates;

void main()
{
    float z_window = czm_unpackDepth(texture2D(u_depthTexture, v_textureCoordinates));
    z_window = czm_reverseLogDepth(z_window);
    float n_range = czm_depthRange.near;
    float f_range = czm_depthRange.far;
    float z_ndc = (2.0 * z_window - n_range - f_range) / (f_range - n_range);
    float scale = pow(z_ndc * 0.5 + 0.5, 8.0);
    gl_FragColor = vec4(mix(vec3(0.0), vec3(1.0), scale), 1.0);
}