dpoit.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. /**
  2. * Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Gianluca Tomasello <giagitom@gmail.com>
  5. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  6. *
  7. * Adapted from https://github.com/tsherif/webgl2examples, The MIT License, Copyright © 2017 Tarek Sherif, Shuai Shao
  8. */
  9. import { QuadSchema, QuadValues } from '../../mol-gl/compute/util';
  10. import { ComputeRenderable, createComputeRenderable } from '../../mol-gl/renderable';
  11. import { TextureSpec, UniformSpec, Values } from '../../mol-gl/renderable/schema';
  12. import { ShaderCode } from '../../mol-gl/shader-code';
  13. import { WebGLContext } from '../../mol-gl/webgl/context';
  14. import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
  15. import { Texture } from '../../mol-gl/webgl/texture';
  16. import { ValueCell } from '../../mol-util';
  17. import { quad_vert } from '../../mol-gl/shader/quad.vert';
  18. import { evaluateDpoit_frag } from '../../mol-gl/shader/evaluate-dpoit.frag';
  19. import { blendBackDpoit_frag } from '../../mol-gl/shader/blend-back-dpoit.frag';
  20. import { Framebuffer } from '../../mol-gl/webgl/framebuffer';
  21. import { Vec2 } from '../../mol-math/linear-algebra';
  22. import { isDebugMode, isTimingMode } from '../../mol-util/debug';
  23. import { isWebGL2 } from '../../mol-gl/webgl/compat';
  24. const BlendBackDpoitSchema = {
  25. ...QuadSchema,
  26. tDpoitBackColor: TextureSpec('texture', 'rgba', 'float', 'nearest'),
  27. uTexSize: UniformSpec('v2'),
  28. };
  29. const BlendBackDpoitShaderCode = ShaderCode('blend-back-dpoit', quad_vert, blendBackDpoit_frag);
  30. type BlendBackDpoitRenderable = ComputeRenderable<Values<typeof BlendBackDpoitSchema>>
  31. function getBlendBackDpoitRenderable(ctx: WebGLContext, dopitBlendBackTexture: Texture): BlendBackDpoitRenderable {
  32. const values: Values<typeof BlendBackDpoitSchema> = {
  33. ...QuadValues,
  34. tDpoitBackColor: ValueCell.create(dopitBlendBackTexture),
  35. uTexSize: ValueCell.create(Vec2.create(dopitBlendBackTexture.getWidth(), dopitBlendBackTexture.getHeight())),
  36. };
  37. const schema = { ...BlendBackDpoitSchema };
  38. const renderItem = createComputeRenderItem(ctx, 'triangles', BlendBackDpoitShaderCode, schema, values);
  39. return createComputeRenderable(renderItem, values);
  40. }
  41. const EvaluateDpoitSchema = {
  42. ...QuadSchema,
  43. tDpoitFrontColor: TextureSpec('texture', 'rgba', 'float', 'nearest'),
  44. uTexSize: UniformSpec('v2'),
  45. };
  46. const EvaluateDpoitShaderCode = ShaderCode('evaluate-dpoit', quad_vert, evaluateDpoit_frag);
  47. type EvaluateDpoitRenderable = ComputeRenderable<Values<typeof EvaluateDpoitSchema>>
  48. function getEvaluateDpoitRenderable(ctx: WebGLContext, dpoitFrontColorTexture: Texture): EvaluateDpoitRenderable {
  49. const values: Values<typeof EvaluateDpoitSchema> = {
  50. ...QuadValues,
  51. tDpoitFrontColor: ValueCell.create(dpoitFrontColorTexture),
  52. uTexSize: ValueCell.create(Vec2.create(dpoitFrontColorTexture.getWidth(), dpoitFrontColorTexture.getHeight())),
  53. };
  54. const schema = { ...EvaluateDpoitSchema };
  55. const renderItem = createComputeRenderItem(ctx, 'triangles', EvaluateDpoitShaderCode, schema, values);
  56. return createComputeRenderable(renderItem, values);
  57. }
  58. export class DpoitPass {
  59. private readonly DEPTH_CLEAR_VALUE = -99999.0; // NOTE same constant is set in shaders
  60. private readonly MAX_DEPTH = 1.0;
  61. private readonly MIN_DEPTH = 0.0;
  62. private passCount = 0;
  63. private writeId: number;
  64. private readId: number;
  65. private readonly blendBackRenderable: BlendBackDpoitRenderable;
  66. private readonly renderable: EvaluateDpoitRenderable;
  67. private readonly depthFramebuffers: Framebuffer[];
  68. private readonly colorFramebuffers: Framebuffer[];
  69. private readonly depthTextures: Texture[];
  70. private readonly colorFrontTextures: Texture[];
  71. private readonly colorBackTextures: Texture[];
  72. private _supported = false;
  73. get supported() {
  74. return this._supported;
  75. }
  76. bind() {
  77. const { state, gl, extensions: { blendMinMax } } = this.webgl;
  78. // initialize
  79. this.passCount = 0;
  80. this.depthFramebuffers[0].bind();
  81. state.clearColor(this.DEPTH_CLEAR_VALUE, this.DEPTH_CLEAR_VALUE, 0, 0);
  82. gl.clear(gl.COLOR_BUFFER_BIT);
  83. this.depthFramebuffers[1].bind();
  84. state.clearColor(-this.MIN_DEPTH, this.MAX_DEPTH, 0, 0);
  85. gl.clear(gl.COLOR_BUFFER_BIT);
  86. this.colorFramebuffers[0].bind();
  87. state.clearColor(0, 0, 0, 0);
  88. gl.clear(gl.COLOR_BUFFER_BIT);
  89. this.colorFramebuffers[1].bind();
  90. state.clearColor(0, 0, 0, 0);
  91. gl.clear(gl.COLOR_BUFFER_BIT);
  92. this.depthFramebuffers[0].bind();
  93. state.blendEquation(blendMinMax!.MAX);
  94. return { depth: this.depthTextures[1], frontColor: this.colorFrontTextures[1], backColor: this.colorBackTextures[1] };
  95. }
  96. bindDualDepthPeeling() {
  97. const { state, gl, extensions: { blendMinMax } } = this.webgl;
  98. this.readId = this.passCount % 2;
  99. this.writeId = 1 - this.readId; // ping-pong: 0 or 1
  100. this.passCount += 1; // increment for next pass
  101. this.depthFramebuffers[this.writeId].bind();
  102. state.clearColor(this.DEPTH_CLEAR_VALUE, this.DEPTH_CLEAR_VALUE, 0, 0);
  103. gl.clear(gl.COLOR_BUFFER_BIT);
  104. this.colorFramebuffers[this.writeId].bind();
  105. state.clearColor(0, 0, 0, 0);
  106. gl.clear(gl.COLOR_BUFFER_BIT);
  107. this.depthFramebuffers[this.writeId].bind();
  108. state.blendEquation(blendMinMax!.MAX);
  109. return { depth: this.depthTextures[this.readId], frontColor: this.colorFrontTextures[this.readId], backColor: this.colorBackTextures[this.readId] };
  110. }
  111. renderBlendBack() {
  112. if (isTimingMode) this.webgl.timer.mark('DpoitPass.renderBlendBack');
  113. const { state, gl } = this.webgl;
  114. state.blendEquation(gl.FUNC_ADD);
  115. state.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
  116. ValueCell.update(this.blendBackRenderable.values.tDpoitBackColor, this.colorBackTextures[this.writeId]);
  117. this.blendBackRenderable.update();
  118. this.blendBackRenderable.render();
  119. if (isTimingMode) this.webgl.timer.markEnd('DpoitPass.renderBlendBack');
  120. }
  121. render() {
  122. if (isTimingMode) this.webgl.timer.mark('DpoitPass.render');
  123. const { state, gl } = this.webgl;
  124. state.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
  125. ValueCell.update(this.renderable.values.tDpoitFrontColor, this.colorFrontTextures[this.writeId]);
  126. this.renderable.update();
  127. this.renderable.render();
  128. if (isTimingMode) this.webgl.timer.markEnd('DpoitPass.render');
  129. }
  130. setSize(width: number, height: number) {
  131. const [w, h] = this.renderable.values.uTexSize.ref.value;
  132. if (width !== w || height !== h) {
  133. for (let i = 0; i < 2; i++) {
  134. this.depthTextures[i].define(width, height);
  135. this.colorFrontTextures[i].define(width, height);
  136. this.colorBackTextures[i].define(width, height);
  137. }
  138. ValueCell.update(this.renderable.values.uTexSize, Vec2.set(this.renderable.values.uTexSize.ref.value, width, height));
  139. ValueCell.update(this.blendBackRenderable.values.uTexSize, Vec2.set(this.blendBackRenderable.values.uTexSize.ref.value, width, height));
  140. }
  141. }
  142. reset() {
  143. if (this._supported) this._init();
  144. }
  145. private _init() {
  146. const { extensions: { drawBuffers } } = this.webgl;
  147. for (let i = 0; i < 2; i++) {
  148. // depth
  149. this.depthFramebuffers[i].bind();
  150. drawBuffers!.drawBuffers([
  151. drawBuffers!.COLOR_ATTACHMENT0,
  152. drawBuffers!.COLOR_ATTACHMENT1,
  153. drawBuffers!.COLOR_ATTACHMENT2
  154. ]);
  155. this.colorFrontTextures[i].attachFramebuffer(this.depthFramebuffers[i], 'color0');
  156. this.colorBackTextures[i].attachFramebuffer(this.depthFramebuffers[i], 'color1');
  157. this.depthTextures[i].attachFramebuffer(this.depthFramebuffers[i], 'color2');
  158. // color
  159. this.colorFramebuffers[i].bind();
  160. drawBuffers!.drawBuffers([
  161. drawBuffers!.COLOR_ATTACHMENT0,
  162. drawBuffers!.COLOR_ATTACHMENT1
  163. ]);
  164. this.colorFrontTextures[i].attachFramebuffer(this.colorFramebuffers[i], 'color0');
  165. this.colorBackTextures[i].attachFramebuffer(this.colorFramebuffers[i], 'color1');
  166. }
  167. }
  168. static isSupported(webgl: WebGLContext) {
  169. const { extensions: { drawBuffers, textureFloat, colorBufferFloat, blendMinMax } } = webgl;
  170. if (!textureFloat || !colorBufferFloat || !drawBuffers || !blendMinMax) {
  171. if (isDebugMode) {
  172. const missing: string[] = [];
  173. if (!textureFloat) missing.push('textureFloat');
  174. if (!colorBufferFloat) missing.push('colorBufferFloat');
  175. if (!drawBuffers) missing.push('drawBuffers');
  176. if (!blendMinMax) missing.push('blendMinMax');
  177. console.log(`Missing "${missing.join('", "')}" extensions required for "dpoit"`);
  178. }
  179. return false;
  180. } else {
  181. return true;
  182. }
  183. }
  184. constructor(private webgl: WebGLContext, width: number, height: number) {
  185. if (!DpoitPass.isSupported(webgl)) return;
  186. const { resources, extensions: { colorBufferHalfFloat, textureHalfFloat } } = webgl;
  187. // textures
  188. this.depthTextures = [
  189. resources.texture('image-float32', 'rg', 'float', 'nearest'),
  190. resources.texture('image-float32', 'rg', 'float', 'nearest')
  191. ];
  192. this.depthTextures[0].define(width, height);
  193. this.depthTextures[1].define(width, height);
  194. if (isWebGL2(webgl.gl)) {
  195. this.colorFrontTextures = colorBufferHalfFloat && textureHalfFloat ? [
  196. resources.texture('image-float16', 'rgba', 'fp16', 'nearest'),
  197. resources.texture('image-float16', 'rgba', 'fp16', 'nearest')
  198. ] : [
  199. resources.texture('image-float32', 'rgba', 'float', 'nearest'),
  200. resources.texture('image-float32', 'rgba', 'float', 'nearest')
  201. ];
  202. this.colorBackTextures = colorBufferHalfFloat && textureHalfFloat ? [
  203. resources.texture('image-float16', 'rgba', 'fp16', 'nearest'),
  204. resources.texture('image-float16', 'rgba', 'fp16', 'nearest')
  205. ] : [
  206. resources.texture('image-float32', 'rgba', 'float', 'nearest'),
  207. resources.texture('image-float32', 'rgba', 'float', 'nearest')
  208. ];
  209. } else {
  210. // in webgl1 drawbuffers must be in the same format for some reason
  211. this.colorFrontTextures = [
  212. resources.texture('image-float32', 'rgba', 'float', 'nearest'),
  213. resources.texture('image-float32', 'rgba', 'float', 'nearest')
  214. ];
  215. this.colorBackTextures = [
  216. resources.texture('image-float32', 'rgba', 'float', 'nearest'),
  217. resources.texture('image-float32', 'rgba', 'float', 'nearest')
  218. ];
  219. }
  220. this.colorFrontTextures[0].define(width, height);
  221. this.colorFrontTextures[1].define(width, height);
  222. this.colorBackTextures[0].define(width, height);
  223. this.colorBackTextures[1].define(width, height);
  224. // framebuffers
  225. this.depthFramebuffers = [resources.framebuffer(), resources.framebuffer()];
  226. this.colorFramebuffers = [resources.framebuffer(), resources.framebuffer()];
  227. // renderables
  228. this.blendBackRenderable = getBlendBackDpoitRenderable(webgl, this.colorBackTextures[0]);
  229. this.renderable = getEvaluateDpoitRenderable(webgl, this.colorFrontTextures[0]);
  230. this._supported = true;
  231. this._init();
  232. }
  233. }