OIT.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. /*global define*/
  2. define([
  3. '../Core/Color',
  4. '../Core/defined',
  5. '../Core/destroyObject',
  6. '../Core/PixelFormat',
  7. '../Renderer/ClearCommand',
  8. '../Renderer/PixelDatatype',
  9. '../Renderer/RenderState',
  10. '../Renderer/ShaderSource',
  11. '../Shaders/AdjustTranslucentFS',
  12. '../Shaders/CompositeOITFS',
  13. './BlendEquation',
  14. './BlendFunction'
  15. ], function(
  16. Color,
  17. defined,
  18. destroyObject,
  19. PixelFormat,
  20. ClearCommand,
  21. PixelDatatype,
  22. RenderState,
  23. ShaderSource,
  24. AdjustTranslucentFS,
  25. CompositeOITFS,
  26. BlendEquation,
  27. BlendFunction) {
  28. "use strict";
  29. /*global WebGLRenderingContext*/
  30. /**
  31. * @private
  32. */
  33. var OIT = function(context) {
  34. var extensionsSupported = context.floatingPointTexture && context.depthTexture;
  35. this._translucentMRTSupport = context.drawBuffers && extensionsSupported;
  36. // We support multipass for the Chrome D3D9 backend and ES 2.0 on mobile.
  37. this._translucentMultipassSupport = !this._translucentMRTSupport && extensionsSupported;
  38. this._opaqueTexture = undefined;
  39. this._accumulationTexture = undefined;
  40. this._depthTexture = undefined;
  41. this._opaqueFBO = undefined;
  42. this._translucentFBO = undefined;
  43. this._alphaFBO = undefined;
  44. this._adjustTranslucentFBO = undefined;
  45. this._adjustAlphaFBO = undefined;
  46. this._opaqueClearCommand = new ClearCommand({
  47. color : new Color(0.0, 0.0, 0.0, 0.0),
  48. owner : this
  49. });
  50. this._translucentMRTClearCommand = new ClearCommand({
  51. color : new Color(0.0, 0.0, 0.0, 1.0),
  52. owner : this
  53. });
  54. this._translucentMultipassClearCommand = new ClearCommand({
  55. color : new Color(0.0, 0.0, 0.0, 0.0),
  56. owner : this
  57. });
  58. this._alphaClearCommand = new ClearCommand({
  59. color : new Color(1.0, 1.0, 1.0, 1.0),
  60. owner : this
  61. });
  62. this._translucentRenderStateCache = {};
  63. this._alphaRenderStateCache = {};
  64. this._translucentShaderCache = {};
  65. this._alphaShaderCache = {};
  66. this._compositeCommand = undefined;
  67. this._adjustTranslucentCommand = undefined;
  68. this._adjustAlphaCommand = undefined;
  69. };
  70. function destroyTextures(oit) {
  71. oit._opaqueTexture = oit._opaqueTexture && !oit._opaqueTexture.isDestroyed() && oit._opaqueTexture.destroy();
  72. oit._accumulationTexture = oit._accumulationTexture && !oit._accumulationTexture.isDestroyed() && oit._accumulationTexture.destroy();
  73. oit._revealageTexture = oit._revealageTexture && !oit._revealageTexture.isDestroyed() && oit._revealageTexture.destroy();
  74. oit._depthTexture = oit._depthTexture && !oit._depthTexture.isDestroyed() && oit._depthTexture.destroy();
  75. }
  76. function destroyFramebuffers(oit) {
  77. oit._opaqueFBO = oit._opaqueFBO && !oit._opaqueFBO.isDestroyed() && oit._opaqueFBO.destroy();
  78. oit._translucentFBO = oit._translucentFBO && !oit._translucentFBO.isDestroyed() && oit._translucentFBO.destroy();
  79. oit._alphaFBO = oit._alphaFBO && !oit._alphaFBO.isDestroyed() && oit._alphaFBO.destroy();
  80. oit._adjustTranslucentFBO = oit._adjustTranslucentFBO && !oit._adjustTranslucentFBO.isDestroyed() && oit._adjustTranslucentFBO.destroy();
  81. oit._adjustAlphaFBO = oit._adjustAlphaFBO && !oit._adjustAlphaFBO.isDestroyed() && oit._adjustAlphaFBO.destroy();
  82. }
  83. function destroyResources(oit) {
  84. destroyTextures(oit);
  85. destroyFramebuffers(oit);
  86. }
  87. function updateTextures(oit, context, width, height) {
  88. destroyTextures(oit);
  89. oit._opaqueTexture = context.createTexture2D({
  90. width : width,
  91. height : height,
  92. pixelFormat : PixelFormat.RGBA,
  93. pixelDatatype : PixelDatatype.UNSIGNED_BYTE
  94. });
  95. oit._accumulationTexture = context.createTexture2D({
  96. width : width,
  97. height : height,
  98. pixelFormat : PixelFormat.RGBA,
  99. pixelDatatype : PixelDatatype.FLOAT
  100. });
  101. oit._revealageTexture = context.createTexture2D({
  102. width : width,
  103. height : height,
  104. pixelFormat : PixelFormat.RGBA,
  105. pixelDatatype : PixelDatatype.FLOAT
  106. });
  107. oit._depthTexture = context.createTexture2D({
  108. width : width,
  109. height : height,
  110. pixelFormat : PixelFormat.DEPTH_COMPONENT,
  111. pixelDatatype : PixelDatatype.UNSIGNED_SHORT
  112. });
  113. }
  114. function updateFramebuffers(oit, context) {
  115. destroyFramebuffers(oit);
  116. var completeFBO = WebGLRenderingContext.FRAMEBUFFER_COMPLETE;
  117. var supported = true;
  118. // if MRT is supported, attempt to make an FBO with multiple color attachments
  119. if (oit._translucentMRTSupport) {
  120. oit._translucentFBO = context.createFramebuffer({
  121. colorTextures : [oit._accumulationTexture, oit._revealageTexture],
  122. depthTexture : oit._depthTexture,
  123. destroyAttachments : false
  124. });
  125. oit._adjustTranslucentFBO = context.createFramebuffer({
  126. colorTextures : [oit._accumulationTexture, oit._revealageTexture],
  127. destroyAttachments : false
  128. });
  129. if (oit._translucentFBO.status !== completeFBO || oit._adjustTranslucentFBO.status !== completeFBO) {
  130. destroyFramebuffers(oit);
  131. oit._translucentMRTSupport = false;
  132. }
  133. }
  134. // either MRT isn't supported or FBO creation failed, attempt multipass
  135. if (!oit._translucentMRTSupport) {
  136. oit._translucentFBO = context.createFramebuffer({
  137. colorTextures : [oit._accumulationTexture],
  138. depthTexture : oit._depthTexture,
  139. destroyAttachments : false
  140. });
  141. oit._alphaFBO = context.createFramebuffer({
  142. colorTextures : [oit._revealageTexture],
  143. depthTexture : oit._depthTexture,
  144. destroyAttachments : false
  145. });
  146. oit._adjustTranslucentFBO = context.createFramebuffer({
  147. colorTextures : [oit._accumulationTexture],
  148. destroyAttachments : false
  149. });
  150. oit._adjustAlphaFBO = context.createFramebuffer({
  151. colorTextures : [oit._revealageTexture],
  152. destroyAttachments : false
  153. });
  154. var translucentComplete = oit._translucentFBO.status === completeFBO;
  155. var alphaComplete = oit._alphaFBO.status === completeFBO;
  156. var adjustTranslucentComplete = oit._adjustTranslucentFBO.status === completeFBO;
  157. var adjustAlphaComplete = oit._adjustAlphaFBO.status === completeFBO;
  158. if (!translucentComplete || !alphaComplete || !adjustTranslucentComplete || !adjustAlphaComplete) {
  159. destroyResources(oit);
  160. oit._translucentMultipassSupport = false;
  161. supported = false;
  162. }
  163. }
  164. if (supported) {
  165. oit._opaqueFBO = context.createFramebuffer({
  166. colorTextures : [oit._opaqueTexture],
  167. depthTexture : oit._depthTexture,
  168. destroyAttachments : false
  169. });
  170. }
  171. return supported;
  172. }
  173. OIT.prototype.update = function(context) {
  174. if (!this.isSupported()) {
  175. return;
  176. }
  177. var width = context.drawingBufferWidth;
  178. var height = context.drawingBufferHeight;
  179. var opaqueTexture = this._opaqueTexture;
  180. var textureChanged = !defined(opaqueTexture) || opaqueTexture.width !== width || opaqueTexture.height !== height;
  181. if (textureChanged) {
  182. updateTextures(this, context, width, height);
  183. }
  184. if (!defined(this._opaqueFBO) || textureChanged) {
  185. if (!updateFramebuffers(this, context)) {
  186. // framebuffer creation failed
  187. return;
  188. }
  189. }
  190. var that = this;
  191. var fs;
  192. var uniformMap;
  193. if (!defined(this._compositeCommand)) {
  194. fs = new ShaderSource({
  195. sources : [CompositeOITFS]
  196. });
  197. if (this._translucentMRTSupport) {
  198. fs.defines.push('MRT');
  199. }
  200. uniformMap = {
  201. u_opaque : function() {
  202. return that._opaqueTexture;
  203. },
  204. u_accumulation : function() {
  205. return that._accumulationTexture;
  206. },
  207. u_revealage : function() {
  208. return that._revealageTexture;
  209. }
  210. };
  211. this._compositeCommand = context.createViewportQuadCommand(fs, {
  212. renderState : context.createRenderState(),
  213. uniformMap : uniformMap,
  214. owner : this
  215. });
  216. }
  217. if (!defined(this._adjustTranslucentCommand)) {
  218. if (this._translucentMRTSupport) {
  219. fs = new ShaderSource({
  220. defines : ['MRT'],
  221. sources : [AdjustTranslucentFS]
  222. });
  223. uniformMap = {
  224. u_bgColor : function() {
  225. return that._translucentMRTClearCommand.color;
  226. },
  227. u_depthTexture : function() {
  228. return that._depthTexture;
  229. }
  230. };
  231. this._adjustTranslucentCommand = context.createViewportQuadCommand(fs, {
  232. renderState : context.createRenderState(),
  233. uniformMap : uniformMap,
  234. owner : this
  235. });
  236. } else if (this._translucentMultipassSupport) {
  237. fs = new ShaderSource({
  238. sources : [AdjustTranslucentFS]
  239. });
  240. uniformMap = {
  241. u_bgColor : function() {
  242. return that._translucentMultipassClearCommand.color;
  243. },
  244. u_depthTexture : function() {
  245. return that._depthTexture;
  246. }
  247. };
  248. this._adjustTranslucentCommand = context.createViewportQuadCommand(fs, {
  249. renderState : context.createRenderState(),
  250. uniformMap : uniformMap,
  251. owner : this
  252. });
  253. uniformMap = {
  254. u_bgColor : function() {
  255. return that._alphaClearCommand.color;
  256. },
  257. u_depthTexture : function() {
  258. return that._depthTexture;
  259. }
  260. };
  261. this._adjustAlphaCommand = context.createViewportQuadCommand(fs, {
  262. renderState : context.createRenderState(),
  263. uniformMap : uniformMap,
  264. owner : this
  265. });
  266. }
  267. }
  268. };
  269. var translucentMRTBlend = {
  270. enabled : true,
  271. color : new Color(0.0, 0.0, 0.0, 0.0),
  272. equationRgb : BlendEquation.ADD,
  273. equationAlpha : BlendEquation.ADD,
  274. functionSourceRgb : BlendFunction.ONE,
  275. functionDestinationRgb : BlendFunction.ONE,
  276. functionSourceAlpha : BlendFunction.ZERO,
  277. functionDestinationAlpha : BlendFunction.ONE_MINUS_SOURCE_ALPHA
  278. };
  279. var translucentColorBlend = {
  280. enabled : true,
  281. color : new Color(0.0, 0.0, 0.0, 0.0),
  282. equationRgb : BlendEquation.ADD,
  283. equationAlpha : BlendEquation.ADD,
  284. functionSourceRgb : BlendFunction.ONE,
  285. functionDestinationRgb : BlendFunction.ONE,
  286. functionSourceAlpha : BlendFunction.ONE,
  287. functionDestinationAlpha : BlendFunction.ONE
  288. };
  289. var translucentAlphaBlend = {
  290. enabled : true,
  291. color : new Color(0.0, 0.0, 0.0, 0.0),
  292. equationRgb : BlendEquation.ADD,
  293. equationAlpha : BlendEquation.ADD,
  294. functionSourceRgb : BlendFunction.ZERO,
  295. functionDestinationRgb : BlendFunction.ONE_MINUS_SOURCE_ALPHA,
  296. functionSourceAlpha : BlendFunction.ZERO,
  297. functionDestinationAlpha : BlendFunction.ONE_MINUS_SOURCE_ALPHA
  298. };
  299. function getTranslucentRenderState(context, translucentBlending, cache, renderState) {
  300. var translucentState = cache[renderState.id];
  301. if (!defined(translucentState)) {
  302. var rs = RenderState.clone(renderState);
  303. rs.depthMask = false;
  304. rs.blending = translucentBlending;
  305. translucentState = context.createRenderState(rs);
  306. cache[renderState.id] = translucentState;
  307. }
  308. return translucentState;
  309. }
  310. function getTranslucentMRTRenderState(oit, context, renderState) {
  311. return getTranslucentRenderState(context, translucentMRTBlend, oit._translucentRenderStateCache, renderState);
  312. }
  313. function getTranslucentColorRenderState(oit, context, renderState) {
  314. return getTranslucentRenderState(context, translucentColorBlend, oit._translucentRenderStateCache, renderState);
  315. }
  316. function getTranslucentAlphaRenderState(oit, context, renderState) {
  317. return getTranslucentRenderState(context, translucentAlphaBlend, oit._alphaRenderStateCache, renderState);
  318. }
  319. var mrtShaderSource =
  320. ' vec3 Ci = czm_gl_FragColor.rgb * czm_gl_FragColor.a;\n' +
  321. ' float ai = czm_gl_FragColor.a;\n' +
  322. ' float wzi = czm_alphaWeight(ai);\n' +
  323. ' gl_FragData[0] = vec4(Ci * wzi, ai);\n' +
  324. ' gl_FragData[1] = vec4(ai * wzi);\n';
  325. var colorShaderSource =
  326. ' vec3 Ci = czm_gl_FragColor.rgb * czm_gl_FragColor.a;\n' +
  327. ' float ai = czm_gl_FragColor.a;\n' +
  328. ' float wzi = czm_alphaWeight(ai);\n' +
  329. ' gl_FragColor = vec4(Ci, ai) * wzi;\n';
  330. var alphaShaderSource =
  331. ' float ai = czm_gl_FragColor.a;\n' +
  332. ' gl_FragColor = vec4(ai);\n';
  333. function getTranslucentShaderProgram(context, shaderProgram, cache, source) {
  334. var id = shaderProgram.id;
  335. var shader = cache[id];
  336. if (!defined(shader)) {
  337. var attributeLocations = shaderProgram._attributeLocations;
  338. var fs = shaderProgram.fragmentShaderSource.clone();
  339. fs.sources = fs.sources.map(function(source) {
  340. source = source.replace(/void\s+main\s*\(\s*(?:void)?\s*\)/g, 'void czm_translucent_main()');
  341. source = source.replace(/gl_FragColor/g, 'czm_gl_FragColor');
  342. source = source.replace(/\bdiscard\b/g, 'czm_discard = true');
  343. source = source.replace(/czm_phong/g, 'czm_translucentPhong');
  344. return source;
  345. });
  346. // Discarding the fragment in main is a workaround for ANGLE D3D9
  347. // shader compilation errors.
  348. fs.sources.splice(0, 0,
  349. (source.indexOf('gl_FragData') !== -1 ? '#extension GL_EXT_draw_buffers : enable \n' : '') +
  350. 'vec4 czm_gl_FragColor;\n' +
  351. 'bool czm_discard = false;\n');
  352. fs.sources.push(
  353. 'void main()\n' +
  354. '{\n' +
  355. ' czm_translucent_main();\n' +
  356. ' if (czm_discard)\n' +
  357. ' {\n' +
  358. ' discard;\n' +
  359. ' }\n' +
  360. source +
  361. '}\n');
  362. shader = context.createShaderProgram(shaderProgram.vertexShaderSource, fs, attributeLocations);
  363. cache[id] = shader;
  364. }
  365. return shader;
  366. }
  367. function getTranslucentMRTShaderProgram(oit, context, shaderProgram) {
  368. return getTranslucentShaderProgram(context, shaderProgram, oit._translucentShaderCache, mrtShaderSource);
  369. }
  370. function getTranslucentColorShaderProgram(oit, context, shaderProgram) {
  371. return getTranslucentShaderProgram(context, shaderProgram, oit._translucentShaderCache, colorShaderSource);
  372. }
  373. function getTranslucentAlphaShaderProgram(oit, context, shaderProgram) {
  374. return getTranslucentShaderProgram(context, shaderProgram, oit._alphaShaderCache, alphaShaderSource);
  375. }
  376. function executeTranslucentCommandsSortedMultipass(oit, scene, executeFunction, passState, commands) {
  377. var command;
  378. var renderState;
  379. var shaderProgram;
  380. var j;
  381. var context = scene.context;
  382. var framebuffer = passState.framebuffer;
  383. var length = commands.length;
  384. passState.framebuffer = oit._adjustTranslucentFBO;
  385. oit._adjustTranslucentCommand.execute(context, passState);
  386. passState.framebuffer = oit._adjustAlphaFBO;
  387. oit._adjustAlphaCommand.execute(context, passState);
  388. var debugFramebuffer = oit._opaqueFBO;
  389. passState.framebuffer = oit._translucentFBO;
  390. for (j = 0; j < length; ++j) {
  391. command = commands[j];
  392. if (!defined(command.oit) || command.shaderProgram.id !== command.oit.shaderProgramId) {
  393. command.oit = {
  394. colorRenderState : getTranslucentColorRenderState(oit, context, command.renderState),
  395. alphaRenderState : getTranslucentAlphaRenderState(oit, context, command.renderState),
  396. colorShaderProgram : getTranslucentColorShaderProgram(oit, context, command.shaderProgram),
  397. alphaShaderProgram : getTranslucentAlphaShaderProgram(oit, context, command.shaderProgram),
  398. shaderProgramId : command.shaderProgram.id
  399. };
  400. }
  401. renderState = command.oit.colorRenderState;
  402. shaderProgram = command.oit.colorShaderProgram;
  403. executeFunction(command, scene, context, passState, renderState, shaderProgram, debugFramebuffer);
  404. }
  405. passState.framebuffer = oit._alphaFBO;
  406. for (j = 0; j < length; ++j) {
  407. command = commands[j];
  408. renderState = command.oit.alphaRenderState;
  409. shaderProgram = command.oit.alphaShaderProgram;
  410. executeFunction(command, scene, context, passState, renderState, shaderProgram, debugFramebuffer);
  411. }
  412. passState.framebuffer = framebuffer;
  413. }
  414. function executeTranslucentCommandsSortedMRT(oit, scene, executeFunction, passState, commands) {
  415. var context = scene.context;
  416. var framebuffer = passState.framebuffer;
  417. var length = commands.length;
  418. passState.framebuffer = oit._adjustTranslucentFBO;
  419. oit._adjustTranslucentCommand.execute(context, passState);
  420. var debugFramebuffer = oit._opaqueFBO;
  421. passState.framebuffer = oit._translucentFBO;
  422. for (var j = 0; j < length; ++j) {
  423. var command = commands[j];
  424. if (!defined(command.oit) || command.shaderProgram.id !== command.oit.shaderProgramId) {
  425. command.oit = {
  426. translucentRenderState : getTranslucentMRTRenderState(oit, context, command.renderState),
  427. translucentShaderProgram : getTranslucentMRTShaderProgram(oit, context, command.shaderProgram),
  428. shaderProgramId : command.shaderProgram.id
  429. };
  430. }
  431. var renderState = command.oit.translucentRenderState;
  432. var shaderProgram = command.oit.translucentShaderProgram;
  433. executeFunction(command, scene, context, passState, renderState, shaderProgram, debugFramebuffer);
  434. }
  435. passState.framebuffer = framebuffer;
  436. }
  437. OIT.prototype.executeCommands = function(scene, executeFunction, passState, commands) {
  438. if (this._translucentMRTSupport) {
  439. executeTranslucentCommandsSortedMRT(this, scene, executeFunction, passState, commands);
  440. return;
  441. }
  442. executeTranslucentCommandsSortedMultipass(this, scene, executeFunction, passState, commands);
  443. };
  444. OIT.prototype.execute = function(context, passState) {
  445. this._compositeCommand.execute(context, passState);
  446. };
  447. OIT.prototype.clear = function(context, passState, clearColor) {
  448. var framebuffer = passState.framebuffer;
  449. passState.framebuffer = this._opaqueFBO;
  450. Color.clone(clearColor, this._opaqueClearCommand.color);
  451. this._opaqueClearCommand.execute(context, passState);
  452. passState.framebuffer = this._translucentFBO;
  453. var translucentClearCommand = this._translucentMRTSupport ? this._translucentMRTClearCommand : this._translucentMultipassClearCommand;
  454. translucentClearCommand.execute(context, passState);
  455. if (this._translucentMultipassSupport) {
  456. passState.framebuffer = this._alphaFBO;
  457. this._alphaClearCommand.execute(context, passState);
  458. }
  459. passState.framebuffer = framebuffer;
  460. };
  461. OIT.prototype.getColorFramebuffer = function() {
  462. return this._opaqueFBO;
  463. };
  464. OIT.prototype.isSupported = function() {
  465. return this._translucentMRTSupport || this._translucentMultipassSupport;
  466. };
  467. OIT.prototype.isDestroyed = function() {
  468. return false;
  469. };
  470. OIT.prototype.destroy = function() {
  471. destroyResources(this);
  472. if (defined(this._compositeCommand)) {
  473. this._compositeCommand.shaderProgram = this._compositeCommand.shaderProgram && this._compositeCommand.shaderProgram.destroy();
  474. }
  475. if (defined(this._adjustTranslucentCommand)) {
  476. this._adjustTranslucentCommand.shaderProgram = this._adjustTranslucentCommand.shaderProgram && this._adjustTranslucentCommand.shaderProgram.destroy();
  477. }
  478. if (defined(this._adjustAlphaCommand)) {
  479. this._adjustAlphaCommand.shaderProgram = this._adjustAlphaCommand.shaderProgram && this._adjustAlphaCommand.shaderProgram.destroy();
  480. }
  481. var name;
  482. var cache = this._translucentShaderCache;
  483. for (name in cache) {
  484. if (cache.hasOwnProperty(name) && defined(cache[name])) {
  485. cache[name].destroy();
  486. }
  487. }
  488. this._translucentShaderCache = {};
  489. cache = this._alphaShaderCache;
  490. for (name in cache) {
  491. if (cache.hasOwnProperty(name) && defined(cache[name])) {
  492. cache[name].destroy();
  493. }
  494. }
  495. this._alphaShaderCache = {};
  496. return destroyObject(this);
  497. };
  498. return OIT;
  499. });