DepthPlane.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. import BoundingSphere from "../Core/BoundingSphere.js";
  2. import Cartesian3 from "../Core/Cartesian3.js";
  3. import ComponentDatatype from "../Core/ComponentDatatype.js";
  4. import defined from "../Core/defined.js";
  5. import FeatureDetection from "../Core/FeatureDetection.js";
  6. import Geometry from "../Core/Geometry.js";
  7. import GeometryAttribute from "../Core/GeometryAttribute.js";
  8. import OrthographicFrustum from "../Core/OrthographicFrustum.js";
  9. import PrimitiveType from "../Core/PrimitiveType.js";
  10. import BufferUsage from "../Renderer/BufferUsage.js";
  11. import DrawCommand from "../Renderer/DrawCommand.js";
  12. import Pass from "../Renderer/Pass.js";
  13. import RenderState from "../Renderer/RenderState.js";
  14. import ShaderProgram from "../Renderer/ShaderProgram.js";
  15. import ShaderSource from "../Renderer/ShaderSource.js";
  16. import VertexArray from "../Renderer/VertexArray.js";
  17. import DepthPlaneFS from "../Shaders/DepthPlaneFS.js";
  18. import DepthPlaneVS from "../Shaders/DepthPlaneVS.js";
  19. import SceneMode from "./SceneMode.js";
  20. import defaultValue from "../Core/defaultValue.js";
  21. import Ellipsoid from "../Core/Ellipsoid.js";
  22. /**
  23. * @private
  24. */
  25. function DepthPlane(depthPlaneEllipsoidOffset) {
  26. this._rs = undefined;
  27. this._sp = undefined;
  28. this._va = undefined;
  29. this._command = undefined;
  30. this._mode = undefined;
  31. this._useLogDepth = false;
  32. this._ellipsoidOffset = defaultValue(depthPlaneEllipsoidOffset, 0);
  33. }
  34. const depthQuadScratch = FeatureDetection.supportsTypedArrays()
  35. ? new Float32Array(12)
  36. : [];
  37. const scratchCartesian1 = new Cartesian3();
  38. const scratchCartesian2 = new Cartesian3();
  39. const scratchCartesian3 = new Cartesian3();
  40. const scratchCartesian4 = new Cartesian3();
  41. const scratchCartesian5 = new Cartesian3();
  42. function computeDepthQuad(ellipsoid, frameState) {
  43. const radii = ellipsoid.radii;
  44. const camera = frameState.camera;
  45. let center, eastOffset, northOffset;
  46. if (camera.frustum instanceof OrthographicFrustum) {
  47. center = Cartesian3.ZERO;
  48. eastOffset = camera.rightWC;
  49. northOffset = camera.upWC;
  50. } else {
  51. const p = camera.positionWC;
  52. // Find the corresponding position in the scaled space of the ellipsoid.
  53. const q = Cartesian3.multiplyComponents(
  54. ellipsoid.oneOverRadii,
  55. p,
  56. scratchCartesian1
  57. );
  58. const qUnit = Cartesian3.normalize(q, scratchCartesian2);
  59. // Determine the east and north directions at q.
  60. const eUnit = Cartesian3.normalize(
  61. Cartesian3.cross(Cartesian3.UNIT_Z, q, scratchCartesian3),
  62. scratchCartesian3
  63. );
  64. const nUnit = Cartesian3.normalize(
  65. Cartesian3.cross(qUnit, eUnit, scratchCartesian4),
  66. scratchCartesian4
  67. );
  68. const qMagnitude = Cartesian3.magnitude(q);
  69. // Determine the radius of the 'limb' of the ellipsoid.
  70. const wMagnitude = Math.sqrt(qMagnitude * qMagnitude - 1.0);
  71. // Compute the center and offsets.
  72. center = Cartesian3.multiplyByScalar(
  73. qUnit,
  74. 1.0 / qMagnitude,
  75. scratchCartesian1
  76. );
  77. const scalar = wMagnitude / qMagnitude;
  78. eastOffset = Cartesian3.multiplyByScalar(eUnit, scalar, scratchCartesian2);
  79. northOffset = Cartesian3.multiplyByScalar(nUnit, scalar, scratchCartesian3);
  80. }
  81. // A conservative measure for the longitudes would be to use the min/max longitudes of the bounding frustum.
  82. const upperLeft = Cartesian3.add(center, northOffset, scratchCartesian5);
  83. Cartesian3.subtract(upperLeft, eastOffset, upperLeft);
  84. Cartesian3.multiplyComponents(radii, upperLeft, upperLeft);
  85. Cartesian3.pack(upperLeft, depthQuadScratch, 0);
  86. const lowerLeft = Cartesian3.subtract(center, northOffset, scratchCartesian5);
  87. Cartesian3.subtract(lowerLeft, eastOffset, lowerLeft);
  88. Cartesian3.multiplyComponents(radii, lowerLeft, lowerLeft);
  89. Cartesian3.pack(lowerLeft, depthQuadScratch, 3);
  90. const upperRight = Cartesian3.add(center, northOffset, scratchCartesian5);
  91. Cartesian3.add(upperRight, eastOffset, upperRight);
  92. Cartesian3.multiplyComponents(radii, upperRight, upperRight);
  93. Cartesian3.pack(upperRight, depthQuadScratch, 6);
  94. const lowerRight = Cartesian3.subtract(
  95. center,
  96. northOffset,
  97. scratchCartesian5
  98. );
  99. Cartesian3.add(lowerRight, eastOffset, lowerRight);
  100. Cartesian3.multiplyComponents(radii, lowerRight, lowerRight);
  101. Cartesian3.pack(lowerRight, depthQuadScratch, 9);
  102. return depthQuadScratch;
  103. }
  104. DepthPlane.prototype.update = function (frameState) {
  105. this._mode = frameState.mode;
  106. if (frameState.mode !== SceneMode.SCENE3D) {
  107. return;
  108. }
  109. const context = frameState.context;
  110. // Allow offsetting the ellipsoid radius to address rendering artifacts below ellipsoid zero elevation.
  111. const radii = frameState.mapProjection.ellipsoid.radii;
  112. const ellipsoid = new Ellipsoid(
  113. radii.x + this._ellipsoidOffset,
  114. radii.y + this._ellipsoidOffset,
  115. radii.z + this._ellipsoidOffset
  116. );
  117. const useLogDepth = frameState.useLogDepth;
  118. if (!defined(this._command)) {
  119. this._rs = RenderState.fromCache({
  120. // Write depth, not color
  121. cull: {
  122. enabled: true,
  123. },
  124. depthTest: {
  125. enabled: true,
  126. },
  127. colorMask: {
  128. red: false,
  129. green: false,
  130. blue: false,
  131. alpha: false,
  132. },
  133. });
  134. this._command = new DrawCommand({
  135. renderState: this._rs,
  136. boundingVolume: new BoundingSphere(
  137. Cartesian3.ZERO,
  138. ellipsoid.maximumRadius
  139. ),
  140. pass: Pass.OPAQUE,
  141. owner: this,
  142. });
  143. }
  144. if (!defined(this._sp) || this._useLogDepth !== useLogDepth) {
  145. this._useLogDepth = useLogDepth;
  146. const vs = new ShaderSource({
  147. sources: [DepthPlaneVS],
  148. });
  149. const fs = new ShaderSource({
  150. sources: [DepthPlaneFS],
  151. });
  152. if (useLogDepth) {
  153. fs.defines.push("LOG_DEPTH");
  154. vs.defines.push("LOG_DEPTH");
  155. }
  156. this._sp = ShaderProgram.replaceCache({
  157. shaderProgram: this._sp,
  158. context: context,
  159. vertexShaderSource: vs,
  160. fragmentShaderSource: fs,
  161. attributeLocations: {
  162. position: 0,
  163. },
  164. });
  165. this._command.shaderProgram = this._sp;
  166. }
  167. // update depth plane
  168. const depthQuad = computeDepthQuad(ellipsoid, frameState);
  169. // depth plane
  170. if (!defined(this._va)) {
  171. const geometry = new Geometry({
  172. attributes: {
  173. position: new GeometryAttribute({
  174. componentDatatype: ComponentDatatype.FLOAT,
  175. componentsPerAttribute: 3,
  176. values: depthQuad,
  177. }),
  178. },
  179. indices: [0, 1, 2, 2, 1, 3],
  180. primitiveType: PrimitiveType.TRIANGLES,
  181. });
  182. this._va = VertexArray.fromGeometry({
  183. context: context,
  184. geometry: geometry,
  185. attributeLocations: {
  186. position: 0,
  187. },
  188. bufferUsage: BufferUsage.DYNAMIC_DRAW,
  189. });
  190. this._command.vertexArray = this._va;
  191. } else {
  192. this._va.getAttribute(0).vertexBuffer.copyFromArrayView(depthQuad);
  193. }
  194. };
  195. DepthPlane.prototype.execute = function (context, passState) {
  196. if (this._mode === SceneMode.SCENE3D) {
  197. this._command.execute(context, passState);
  198. }
  199. };
  200. DepthPlane.prototype.isDestroyed = function () {
  201. return false;
  202. };
  203. DepthPlane.prototype.destroy = function () {
  204. this._sp = this._sp && this._sp.destroy();
  205. this._va = this._va && this._va.destroy();
  206. };
  207. export default DepthPlane;