marking.ts 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. /**
  2. * Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { QuadSchema, QuadValues } from '../../mol-gl/compute/util';
  7. import { ComputeRenderable, createComputeRenderable } from '../../mol-gl/renderable';
  8. import { DefineSpec, TextureSpec, UniformSpec, Values } from '../../mol-gl/renderable/schema';
  9. import { ShaderCode } from '../../mol-gl/shader-code';
  10. import { WebGLContext } from '../../mol-gl/webgl/context';
  11. import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
  12. import { Texture } from '../../mol-gl/webgl/texture';
  13. import { Vec2, Vec3 } from '../../mol-math/linear-algebra';
  14. import { ValueCell } from '../../mol-util';
  15. import { ParamDefinition as PD } from '../../mol-util/param-definition';
  16. import { quad_vert } from '../../mol-gl/shader/quad.vert';
  17. import { overlay_frag } from '../../mol-gl/shader/marking/overlay.frag';
  18. import { Viewport } from '../camera/util';
  19. import { RenderTarget } from '../../mol-gl/webgl/render-target';
  20. import { Color } from '../../mol-util/color';
  21. import { edge_frag } from '../../mol-gl/shader/marking/edge.frag';
  22. export const MarkingParams = {
  23. enabled: PD.Boolean(false),
  24. highlightEdgeColor: PD.Color(Color.darken(Color.fromNormalizedRgb(1.0, 0.4, 0.6), 1.0)),
  25. selectEdgeColor: PD.Color(Color.darken(Color.fromNormalizedRgb(0.2, 1.0, 0.1), 1.0)),
  26. edgeScale: PD.Numeric(1, { min: 1, max: 3, step: 1 }, { description: 'Thickness of the edge.' }),
  27. ghostEdgeStrength: PD.Numeric(0.3, { min: 0, max: 1, step: 0.1 }, { description: 'Opacity of the hidden edges that are covered by other geometry. When set to 1, one less geometry render pass is done.' }),
  28. innerEdgeFactor: PD.Numeric(1.5, { min: 0, max: 3, step: 0.1 }, { description: 'Factor to multiply the inner edge color with - for added contrast.' }),
  29. };
  30. export type MarkingProps = PD.Values<typeof MarkingParams>
  31. export class MarkingPass {
  32. static isEnabled(props: MarkingProps) {
  33. return props.enabled;
  34. }
  35. readonly depthTarget: RenderTarget
  36. readonly maskTarget: RenderTarget
  37. private readonly edgesTarget: RenderTarget
  38. private readonly edge: EdgeRenderable
  39. private readonly overlay: OverlayRenderable
  40. constructor(private webgl: WebGLContext, width: number, height: number) {
  41. this.depthTarget = webgl.createRenderTarget(width, height);
  42. this.maskTarget = webgl.createRenderTarget(width, height);
  43. this.edgesTarget = webgl.createRenderTarget(width, height);
  44. this.edge = getEdgeRenderable(webgl, this.maskTarget.texture);
  45. this.overlay = getOverlayRenderable(webgl, this.edgesTarget.texture);
  46. }
  47. private setEdgeState(viewport: Viewport) {
  48. const { gl, state } = this.webgl;
  49. state.enable(gl.SCISSOR_TEST);
  50. state.enable(gl.BLEND);
  51. state.blendFunc(gl.ONE, gl.ONE);
  52. state.blendEquation(gl.FUNC_ADD);
  53. state.disable(gl.DEPTH_TEST);
  54. state.depthMask(false);
  55. const { x, y, width, height } = viewport;
  56. gl.viewport(x, y, width, height);
  57. gl.scissor(x, y, width, height);
  58. state.clearColor(0, 0, 0, 0);
  59. gl.clear(gl.COLOR_BUFFER_BIT);
  60. }
  61. private setOverlayState(viewport: Viewport) {
  62. const { gl, state } = this.webgl;
  63. state.enable(gl.SCISSOR_TEST);
  64. state.enable(gl.BLEND);
  65. state.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
  66. state.blendEquation(gl.FUNC_ADD);
  67. state.disable(gl.DEPTH_TEST);
  68. state.depthMask(false);
  69. const { x, y, width, height } = viewport;
  70. gl.viewport(x, y, width, height);
  71. gl.scissor(x, y, width, height);
  72. }
  73. setSize(width: number, height: number) {
  74. const w = this.depthTarget.getWidth();
  75. const h = this.depthTarget.getHeight();
  76. if (width !== w || height !== h) {
  77. this.depthTarget.setSize(width, height);
  78. this.maskTarget.setSize(width, height);
  79. this.edgesTarget.setSize(width, height);
  80. ValueCell.update(this.edge.values.uTexSizeInv, Vec2.set(this.edge.values.uTexSizeInv.ref.value, 1 / width, 1 / height));
  81. ValueCell.update(this.overlay.values.uTexSizeInv, Vec2.set(this.overlay.values.uTexSizeInv.ref.value, 1 / width, 1 / height));
  82. }
  83. }
  84. update(props: MarkingProps) {
  85. const { highlightEdgeColor, selectEdgeColor, edgeScale, innerEdgeFactor, ghostEdgeStrength } = props;
  86. const { values: edgeValues } = this.edge;
  87. const _edgeScale = Math.round(edgeScale * this.webgl.pixelRatio);
  88. if (edgeValues.dEdgeScale.ref.value !== _edgeScale) {
  89. ValueCell.update(edgeValues.dEdgeScale, _edgeScale);
  90. this.edge.update();
  91. }
  92. const { values: overlayValues } = this.overlay;
  93. ValueCell.update(overlayValues.uHighlightEdgeColor, Color.toVec3Normalized(overlayValues.uHighlightEdgeColor.ref.value, highlightEdgeColor));
  94. ValueCell.update(overlayValues.uSelectEdgeColor, Color.toVec3Normalized(overlayValues.uSelectEdgeColor.ref.value, selectEdgeColor));
  95. ValueCell.update(overlayValues.uInnerEdgeFactor, innerEdgeFactor);
  96. ValueCell.update(overlayValues.uGhostEdgeStrength, ghostEdgeStrength);
  97. }
  98. render(viewport: Viewport, target: RenderTarget | undefined) {
  99. this.edgesTarget.bind();
  100. this.setEdgeState(viewport);
  101. this.edge.render();
  102. if (target) {
  103. target.bind();
  104. } else {
  105. this.webgl.unbindFramebuffer();
  106. }
  107. this.setOverlayState(viewport);
  108. this.overlay.render();
  109. }
  110. }
  111. //
  112. const EdgeSchema = {
  113. ...QuadSchema,
  114. tMaskTexture: TextureSpec('texture', 'rgba', 'ubyte', 'linear'),
  115. uTexSizeInv: UniformSpec('v2'),
  116. dEdgeScale: DefineSpec('number'),
  117. };
  118. const EdgeShaderCode = ShaderCode('edge', quad_vert, edge_frag);
  119. type EdgeRenderable = ComputeRenderable<Values<typeof EdgeSchema>>
  120. function getEdgeRenderable(ctx: WebGLContext, maskTexture: Texture): EdgeRenderable {
  121. const width = maskTexture.getWidth();
  122. const height = maskTexture.getHeight();
  123. const values: Values<typeof EdgeSchema> = {
  124. ...QuadValues,
  125. tMaskTexture: ValueCell.create(maskTexture),
  126. uTexSizeInv: ValueCell.create(Vec2.create(1 / width, 1 / height)),
  127. dEdgeScale: ValueCell.create(1),
  128. };
  129. const schema = { ...EdgeSchema };
  130. const renderItem = createComputeRenderItem(ctx, 'triangles', EdgeShaderCode, schema, values);
  131. return createComputeRenderable(renderItem, values);
  132. }
  133. //
  134. const OverlaySchema = {
  135. ...QuadSchema,
  136. tEdgeTexture: TextureSpec('texture', 'rgba', 'ubyte', 'linear'),
  137. uTexSizeInv: UniformSpec('v2'),
  138. uHighlightEdgeColor: UniformSpec('v3'),
  139. uSelectEdgeColor: UniformSpec('v3'),
  140. uGhostEdgeStrength: UniformSpec('f'),
  141. uInnerEdgeFactor: UniformSpec('f'),
  142. };
  143. const OverlayShaderCode = ShaderCode('overlay', quad_vert, overlay_frag);
  144. type OverlayRenderable = ComputeRenderable<Values<typeof OverlaySchema>>
  145. function getOverlayRenderable(ctx: WebGLContext, edgeTexture: Texture): OverlayRenderable {
  146. const width = edgeTexture.getWidth();
  147. const height = edgeTexture.getHeight();
  148. const values: Values<typeof OverlaySchema> = {
  149. ...QuadValues,
  150. tEdgeTexture: ValueCell.create(edgeTexture),
  151. uTexSizeInv: ValueCell.create(Vec2.create(1 / width, 1 / height)),
  152. uHighlightEdgeColor: ValueCell.create(Vec3()),
  153. uSelectEdgeColor: ValueCell.create(Vec3()),
  154. uGhostEdgeStrength: ValueCell.create(0),
  155. uInnerEdgeFactor: ValueCell.create(0),
  156. };
  157. const schema = { ...OverlaySchema };
  158. const renderItem = createComputeRenderItem(ctx, 'triangles', OverlayShaderCode, schema, values);
  159. return createComputeRenderable(renderItem, values);
  160. }