2023-4-18 地平线剔除

156 阅读1分钟

原理

对于地球来讲,除了进行视锥体剔除之外,还要进行遮挡体剔除,地球就是一个巨大的遮挡体,地球背面的物体应该被剔除,即图中灰色阴影部分不该显示。

image.png

如图所示C为视点,T为要剔除的点(假设我们已经选到了一个合适的点,该点不显示则整个物体必然不会显示),H为视点与地球的切点,C为球心 要实现地平线剔除,需要T在灰色阴影体中,需要满足两个条件

  • VQ>VP\color{orange}|\vec{VQ}|>|\vec{VP}|
  • α>β\color{orange}\angle\alpha>\angle\beta

实现

1.VQ>VP\color{orange}|\vec{VQ}|>|\vec{VP}|的推导

首先明确已知点为 V,T,C

VQ=VTcos(β)=VTVT^VC^=VTVC^|\vec{VQ}|=|\vec{VT}|*cos(\beta)\\ =|\vec{VT}|*\hat{VT}\cdot\hat{VC} =\vec{VT}\cdot\hat{VC}

又因为:

sinα=HCVC=PCHC \sin{\alpha}=\frac{|\vec{HC}|}{|\vec{VC}|}=\frac{|\vec{PC}|}{|\vec{HC}|}

则为单位球时:

PC=HC2VC=1VCVP=VCPC=VC1VCVTVC^>VC1VCVTVC>VC21|\vec{PC}|=\frac{|\vec{HC}|^2}{|\vec{VC}|}=\frac{1}{|\vec{VC}|}\\ 而 |\vec{VP}|=|\vec{VC}|-|\vec{PC}|=|\vec{VC}|-\frac{1}{|\vec{VC}|}\\ \vec{VT}\cdot\hat{VC}>|\vec{VC}|-\frac{1}{|\vec{VC}|}\\ \vec{VT}\cdot\vec{VC}>|\vec{VC}|^2-1\\

2.α>β\color{orange}\angle\alpha>\angle\beta的推导

cosα=VHVCcosβ=VTVCVTVC\\cos{\alpha}=\frac{|\vec{VH}|}{|\vec{VC}|}\\ \cos{\beta}=\frac{\vec{VT}\cdot{\vec{VC}}}{|\vec{VT}|*|\vec{VC}|}

若要使得 α>β\angle\alpha>\angle\beta,则

cosα<cosβVHVC<VTVCVTVCVH<VTVCVT\cos{\alpha}<\cos{\beta}\\ \frac{|\vec{VH}|}{|\vec{VC}|}<\frac{\vec{VT}\cdot{\vec{VC}}}{|\vec{VT}|*|\vec{VC}|}\\ |\vec{VH}|<\frac{\vec{VT}\cdot{\vec{VC}}}{|\vec{VT}|}\\

又由于VT\color{orange}\vec{VT}VC\color{orange}\vec{VC}的夹角小于90度,则VTVC>0\color{orange}\vec{VT}\cdot{\vec{VC}}>0,两边同时平方,符号不变,则为:

VH2<(VTVC)2VT2|\vec{VH}|^2<\frac{(\vec{VT}\cdot{\vec{VC}})^2}{|\vec{VT}|^2}

而当为单位球时,VH2=VC21|\vec{VH}|^2=|\vec{VC}|^2-1

VC21<(VTVC)2VT2|\vec{VC}|^2-1<\frac{(\vec{VT}\cdot{\vec{VC}})^2}{|\vec{VT}|^2}

代码

在cesium中实现的关键代码如下:

  // Ellipsoid radii - WGS84 shown here
  var rX = 6378137.0;
  var rY = 6378137.0;
  var rZ = 6356752.3142451793;

  // Vector CV
  var cvX = cameraPosition.x / rX;
  var cvY = cameraPosition.y / rY;
  var cvZ = cameraPosition.z / rZ;

  var vhMagnitudeSquared = cvX * cvX + cvY * cvY + cvZ * cvZ - 1.0;
// Target position, transformed to scaled space
    var tX = position.x / rX;
    var tY = position.y / rY;
    var tZ = position.z / rZ;

    // Vector VT
    var vtX = tX - cvX;
    var vtY = tY - cvY;
    var vtZ = tZ - cvZ;
    var vtMagnitudeSquared = vtX * vtX + vtY * vtY + vtZ * vtZ;

    // VT dot VC is the inverse of VT dot CV
    var vtDotVc = -(vtX * cvX + vtY * cvY + vtZ * cvZ);

    var isOccluded = vtDotVc > vhMagnitudeSquared &&
      vtDotVc * vtDotVc / vtMagnitudeSquared > vhMagnitudeSquared;