PointCloudEyeDomeLighting.js 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. import Cartesian2 from "../Core/Cartesian2.js";
  2. import Color from "../Core/Color.js";
  3. import defined from "../Core/defined.js";
  4. import destroyObject from "../Core/destroyObject.js";
  5. import PixelFormat from "../Core/PixelFormat.js";
  6. import PrimitiveType from "../Core/PrimitiveType.js";
  7. import ClearCommand from "../Renderer/ClearCommand.js";
  8. import DrawCommand from "../Renderer/DrawCommand.js";
  9. import Framebuffer from "../Renderer/Framebuffer.js";
  10. import Pass from "../Renderer/Pass.js";
  11. import PixelDatatype from "../Renderer/PixelDatatype.js";
  12. import RenderState from "../Renderer/RenderState.js";
  13. import Sampler from "../Renderer/Sampler.js";
  14. import ShaderSource from "../Renderer/ShaderSource.js";
  15. import Texture from "../Renderer/Texture.js";
  16. import BlendingState from "../Scene/BlendingState.js";
  17. import StencilConstants from "../Scene/StencilConstants.js";
  18. import PointCloudEyeDomeLightingShader from "../Shaders/PostProcessStages/PointCloudEyeDomeLighting.js";
  19. /**
  20. * Eye dome lighting. Does not support points with per-point translucency, but does allow translucent styling against the globe.
  21. * Requires support for EXT_frag_depth and WEBGL_draw_buffers extensions in WebGL 1.0.
  22. *
  23. * @private
  24. */
  25. function PointCloudEyeDomeLighting() {
  26. this._framebuffer = undefined;
  27. this._colorGBuffer = undefined; // color gbuffer
  28. this._depthGBuffer = undefined; // depth gbuffer
  29. this._depthTexture = undefined; // needed to write depth so camera based on depth works
  30. this._drawCommand = undefined;
  31. this._clearCommand = undefined;
  32. this._strength = 1.0;
  33. this._radius = 1.0;
  34. }
  35. function destroyFramebuffer(processor) {
  36. var framebuffer = processor._framebuffer;
  37. if (!defined(framebuffer)) {
  38. return;
  39. }
  40. processor._colorGBuffer.destroy();
  41. processor._depthGBuffer.destroy();
  42. processor._depthTexture.destroy();
  43. framebuffer.destroy();
  44. processor._framebuffer = undefined;
  45. processor._colorGBuffer = undefined;
  46. processor._depthGBuffer = undefined;
  47. processor._depthTexture = undefined;
  48. processor._drawCommand = undefined;
  49. processor._clearCommand = undefined;
  50. }
  51. function createFramebuffer(processor, context) {
  52. var screenWidth = context.drawingBufferWidth;
  53. var screenHeight = context.drawingBufferHeight;
  54. var colorGBuffer = new Texture({
  55. context: context,
  56. width: screenWidth,
  57. height: screenHeight,
  58. pixelFormat: PixelFormat.RGBA,
  59. pixelDatatype: PixelDatatype.UNSIGNED_BYTE,
  60. sampler: Sampler.NEAREST,
  61. });
  62. var depthGBuffer = new Texture({
  63. context: context,
  64. width: screenWidth,
  65. height: screenHeight,
  66. pixelFormat: PixelFormat.RGBA,
  67. pixelDatatype: PixelDatatype.UNSIGNED_BYTE,
  68. sampler: Sampler.NEAREST,
  69. });
  70. var depthTexture = new Texture({
  71. context: context,
  72. width: screenWidth,
  73. height: screenHeight,
  74. pixelFormat: PixelFormat.DEPTH_COMPONENT,
  75. pixelDatatype: PixelDatatype.UNSIGNED_INT,
  76. sampler: Sampler.NEAREST,
  77. });
  78. processor._framebuffer = new Framebuffer({
  79. context: context,
  80. colorTextures: [colorGBuffer, depthGBuffer],
  81. depthTexture: depthTexture,
  82. destroyAttachments: false,
  83. });
  84. processor._colorGBuffer = colorGBuffer;
  85. processor._depthGBuffer = depthGBuffer;
  86. processor._depthTexture = depthTexture;
  87. }
  88. var distanceAndEdlStrengthScratch = new Cartesian2();
  89. function createCommands(processor, context) {
  90. var blendFS = new ShaderSource({
  91. defines: ["LOG_DEPTH_WRITE"],
  92. sources: [PointCloudEyeDomeLightingShader],
  93. });
  94. var blendUniformMap = {
  95. u_pointCloud_colorGBuffer: function () {
  96. return processor._colorGBuffer;
  97. },
  98. u_pointCloud_depthGBuffer: function () {
  99. return processor._depthGBuffer;
  100. },
  101. u_distanceAndEdlStrength: function () {
  102. distanceAndEdlStrengthScratch.x = processor._radius;
  103. distanceAndEdlStrengthScratch.y = processor._strength;
  104. return distanceAndEdlStrengthScratch;
  105. },
  106. };
  107. var blendRenderState = RenderState.fromCache({
  108. blending: BlendingState.ALPHA_BLEND,
  109. depthMask: true,
  110. depthTest: {
  111. enabled: true,
  112. },
  113. stencilTest: StencilConstants.setCesium3DTileBit(),
  114. stencilMask: StencilConstants.CESIUM_3D_TILE_MASK,
  115. });
  116. processor._drawCommand = context.createViewportQuadCommand(blendFS, {
  117. uniformMap: blendUniformMap,
  118. renderState: blendRenderState,
  119. pass: Pass.CESIUM_3D_TILE,
  120. owner: processor,
  121. });
  122. processor._clearCommand = new ClearCommand({
  123. framebuffer: processor._framebuffer,
  124. color: new Color(0.0, 0.0, 0.0, 0.0),
  125. depth: 1.0,
  126. renderState: RenderState.fromCache(),
  127. pass: Pass.CESIUM_3D_TILE,
  128. owner: processor,
  129. });
  130. }
  131. function createResources(processor, context) {
  132. var screenWidth = context.drawingBufferWidth;
  133. var screenHeight = context.drawingBufferHeight;
  134. var colorGBuffer = processor._colorGBuffer;
  135. var nowDirty = false;
  136. var resized =
  137. defined(colorGBuffer) &&
  138. (colorGBuffer.width !== screenWidth ||
  139. colorGBuffer.height !== screenHeight);
  140. if (!defined(colorGBuffer) || resized) {
  141. destroyFramebuffer(processor);
  142. createFramebuffer(processor, context);
  143. createCommands(processor, context);
  144. nowDirty = true;
  145. }
  146. return nowDirty;
  147. }
  148. function isSupported(context) {
  149. return context.drawBuffers && context.fragmentDepth;
  150. }
  151. PointCloudEyeDomeLighting.isSupported = isSupported;
  152. function getECShaderProgram(context, shaderProgram) {
  153. var shader = context.shaderCache.getDerivedShaderProgram(shaderProgram, "EC");
  154. if (!defined(shader)) {
  155. var attributeLocations = shaderProgram._attributeLocations;
  156. var fs = shaderProgram.fragmentShaderSource.clone();
  157. fs.sources = fs.sources.map(function (source) {
  158. source = ShaderSource.replaceMain(
  159. source,
  160. "czm_point_cloud_post_process_main"
  161. );
  162. source = source.replace(/gl_FragColor/g, "gl_FragData[0]");
  163. return source;
  164. });
  165. fs.sources.unshift("#extension GL_EXT_draw_buffers : enable \n");
  166. fs.sources.push(
  167. "void main() \n" +
  168. "{ \n" +
  169. " czm_point_cloud_post_process_main(); \n" +
  170. "#ifdef LOG_DEPTH\n" +
  171. " czm_writeLogDepth();\n" +
  172. " gl_FragData[1] = czm_packDepth(gl_FragDepthEXT); \n" +
  173. "#else\n" +
  174. " gl_FragData[1] = czm_packDepth(gl_FragCoord.z);\n" +
  175. "#endif\n" +
  176. "}"
  177. );
  178. shader = context.shaderCache.createDerivedShaderProgram(
  179. shaderProgram,
  180. "EC",
  181. {
  182. vertexShaderSource: shaderProgram.vertexShaderSource,
  183. fragmentShaderSource: fs,
  184. attributeLocations: attributeLocations,
  185. }
  186. );
  187. }
  188. return shader;
  189. }
  190. PointCloudEyeDomeLighting.prototype.update = function (
  191. frameState,
  192. commandStart,
  193. pointCloudShading,
  194. boundingVolume
  195. ) {
  196. if (!isSupported(frameState.context)) {
  197. return;
  198. }
  199. this._strength = pointCloudShading.eyeDomeLightingStrength;
  200. this._radius =
  201. pointCloudShading.eyeDomeLightingRadius * frameState.pixelRatio;
  202. var dirty = createResources(this, frameState.context);
  203. // Hijack existing point commands to render into an offscreen FBO.
  204. var i;
  205. var commandList = frameState.commandList;
  206. var commandEnd = commandList.length;
  207. for (i = commandStart; i < commandEnd; ++i) {
  208. var command = commandList[i];
  209. if (
  210. command.primitiveType !== PrimitiveType.POINTS ||
  211. command.pass === Pass.TRANSLUCENT
  212. ) {
  213. continue;
  214. }
  215. var derivedCommand = command.derivedCommands.pointCloudProcessor;
  216. if (
  217. !defined(derivedCommand) ||
  218. command.dirty ||
  219. dirty ||
  220. derivedCommand.framebuffer !== this._framebuffer
  221. ) {
  222. // Prevent crash when tiles out-of-view come in-view during context size change
  223. derivedCommand = DrawCommand.shallowClone(command);
  224. command.derivedCommands.pointCloudProcessor = derivedCommand;
  225. derivedCommand.framebuffer = this._framebuffer;
  226. derivedCommand.shaderProgram = getECShaderProgram(
  227. frameState.context,
  228. command.shaderProgram
  229. );
  230. derivedCommand.castShadows = false;
  231. derivedCommand.receiveShadows = false;
  232. }
  233. commandList[i] = derivedCommand;
  234. }
  235. var clearCommand = this._clearCommand;
  236. var blendCommand = this._drawCommand;
  237. blendCommand.boundingVolume = boundingVolume;
  238. // Blend EDL into the main FBO
  239. commandList.push(blendCommand);
  240. commandList.push(clearCommand);
  241. };
  242. /**
  243. * Returns true if this object was destroyed; otherwise, false.
  244. * <br /><br />
  245. * If this object was destroyed, it should not be used; calling any function other than
  246. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  247. *
  248. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  249. *
  250. * @see PointCloudEyeDomeLighting#destroy
  251. */
  252. PointCloudEyeDomeLighting.prototype.isDestroyed = function () {
  253. return false;
  254. };
  255. /**
  256. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  257. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  258. * <br /><br />
  259. * Once an object is destroyed, it should not be used; calling any function other than
  260. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  261. * assign the return value (<code>undefined</code>) to the object as done in the example.
  262. *
  263. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  264. *
  265. * @example
  266. * processor = processor && processor.destroy();
  267. *
  268. * @see PointCloudEyeDomeLighting#isDestroyed
  269. */
  270. PointCloudEyeDomeLighting.prototype.destroy = function () {
  271. destroyFramebuffer(this);
  272. return destroyObject(this);
  273. };
  274. export default PointCloudEyeDomeLighting;