PointCloudEyeDomeLighting.js 11 KB

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