postprocessing.ts 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. /**
  2. * Copyright (c) 2019 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 { TextureSpec, Values, UniformSpec, DefineSpec } from '../../mol-gl/renderable/schema';
  8. import { ShaderCode } from '../../mol-gl/shader-code';
  9. import { WebGLContext } from '../../mol-gl/webgl/context';
  10. import { Texture } from '../../mol-gl/webgl/texture';
  11. import { ValueCell } from '../../mol-util';
  12. import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
  13. import { createComputeRenderable, ComputeRenderable } from '../../mol-gl/renderable';
  14. import { Vec2, Vec3 } from '../../mol-math/linear-algebra';
  15. import { ParamDefinition as PD } from '../../mol-util/param-definition';
  16. import { createRenderTarget, RenderTarget } from '../../mol-gl/webgl/render-target';
  17. import { DrawPass } from './draw';
  18. import { Camera } from '../../mol-canvas3d/camera';
  19. import quad_vert from '../../mol-gl/shader/quad.vert'
  20. import postprocessing_frag from '../../mol-gl/shader/postprocessing.frag'
  21. const PostprocessingSchema = {
  22. ...QuadSchema,
  23. tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
  24. tDepth: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
  25. uTexSize: UniformSpec('v2'),
  26. dUseFog: DefineSpec('boolean'),
  27. dOrthographic: DefineSpec('number'),
  28. uNear: UniformSpec('f'),
  29. uFar: UniformSpec('f'),
  30. uFogNear: UniformSpec('f'),
  31. uFogFar: UniformSpec('f'),
  32. uFogColor: UniformSpec('v3'),
  33. dOcclusionEnable: DefineSpec('boolean'),
  34. dOcclusionKernelSize: DefineSpec('number'),
  35. uOcclusionBias: UniformSpec('f'),
  36. uOcclusionRadius: UniformSpec('f'),
  37. dOutlineEnable: DefineSpec('boolean'),
  38. uOutlineScale: UniformSpec('f'),
  39. uOutlineThreshold: UniformSpec('f'),
  40. dPackedDepth: DefineSpec('boolean'),
  41. }
  42. export const PostprocessingParams = {
  43. occlusionEnable: PD.Boolean(false),
  44. occlusionKernelSize: PD.Numeric(4, { min: 1, max: 32, step: 1 }),
  45. occlusionBias: PD.Numeric(0.5, { min: 0, max: 1, step: 0.01 }),
  46. occlusionRadius: PD.Numeric(32, { min: 0, max: 256, step: 1 }),
  47. outlineEnable: PD.Boolean(false),
  48. outlineScale: PD.Numeric(1, { min: 0, max: 10, step: 1 }),
  49. outlineThreshold: PD.Numeric(0.8, { min: 0, max: 1, step: 0.01 }),
  50. useFog: PD.Boolean(true),
  51. }
  52. export type PostprocessingProps = PD.Values<typeof PostprocessingParams>
  53. type PostprocessingRenderable = ComputeRenderable<Values<typeof PostprocessingSchema>>
  54. function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, depthTexture: Texture, packedDepth: boolean, props: Partial<PostprocessingProps>): PostprocessingRenderable {
  55. const p = { ...PD.getDefaultValues(PostprocessingParams), ...props }
  56. const values: Values<typeof PostprocessingSchema> = {
  57. ...QuadValues,
  58. tColor: ValueCell.create(colorTexture),
  59. tDepth: ValueCell.create(depthTexture),
  60. uTexSize: ValueCell.create(Vec2.create(colorTexture.width, colorTexture.height)),
  61. dUseFog: ValueCell.create(p.useFog),
  62. dOrthographic: ValueCell.create(0),
  63. uNear: ValueCell.create(1),
  64. uFar: ValueCell.create(10000),
  65. uFogNear: ValueCell.create(10000),
  66. uFogFar: ValueCell.create(10000),
  67. uFogColor: ValueCell.create(Vec3.create(1, 1, 1)),
  68. dOcclusionEnable: ValueCell.create(p.occlusionEnable),
  69. dOcclusionKernelSize: ValueCell.create(p.occlusionKernelSize),
  70. uOcclusionBias: ValueCell.create(p.occlusionBias),
  71. uOcclusionRadius: ValueCell.create(p.occlusionRadius),
  72. dOutlineEnable: ValueCell.create(p.outlineEnable),
  73. uOutlineScale: ValueCell.create(p.outlineScale * ctx.pixelRatio),
  74. uOutlineThreshold: ValueCell.create(p.outlineThreshold),
  75. dPackedDepth: ValueCell.create(packedDepth),
  76. }
  77. const schema = { ...PostprocessingSchema }
  78. const shaderCode = ShaderCode(quad_vert, postprocessing_frag)
  79. const renderItem = createComputeRenderItem(ctx, 'triangles', shaderCode, schema, values)
  80. return createComputeRenderable(renderItem, values)
  81. }
  82. export class PostprocessingPass {
  83. target: RenderTarget
  84. props: PostprocessingProps
  85. renderable: PostprocessingRenderable
  86. constructor(private webgl: WebGLContext, private camera: Camera, drawPass: DrawPass, props: Partial<PostprocessingProps>) {
  87. const { gl } = webgl
  88. this.target = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
  89. this.props = { ...PD.getDefaultValues(PostprocessingParams), ...props }
  90. const { colorTarget, depthTexture, packedDepth } = drawPass
  91. this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTexture, packedDepth, this.props)
  92. }
  93. get enabled() {
  94. return this.props.occlusionEnable || this.props.outlineEnable
  95. }
  96. setSize(width: number, height: number) {
  97. this.target.setSize(width, height)
  98. ValueCell.update(this.renderable.values.uTexSize, Vec2.set(this.renderable.values.uTexSize.ref.value, width, height))
  99. }
  100. setProps(props: Partial<PostprocessingProps>) {
  101. if (props.occlusionEnable !== undefined) {
  102. this.props.occlusionEnable = props.occlusionEnable
  103. ValueCell.update(this.renderable.values.dOcclusionEnable, props.occlusionEnable)
  104. }
  105. if (props.occlusionKernelSize !== undefined) {
  106. this.props.occlusionKernelSize = props.occlusionKernelSize
  107. ValueCell.update(this.renderable.values.dOcclusionKernelSize, props.occlusionKernelSize)
  108. }
  109. if (props.occlusionBias !== undefined) {
  110. this.props.occlusionBias = props.occlusionBias
  111. ValueCell.update(this.renderable.values.uOcclusionBias, props.occlusionBias)
  112. }
  113. if (props.occlusionRadius !== undefined) {
  114. this.props.occlusionRadius = props.occlusionRadius
  115. ValueCell.update(this.renderable.values.uOcclusionRadius, props.occlusionRadius)
  116. }
  117. if (props.outlineEnable !== undefined) {
  118. this.props.outlineEnable = props.outlineEnable
  119. ValueCell.update(this.renderable.values.dOutlineEnable, props.outlineEnable)
  120. }
  121. if (props.outlineScale !== undefined) {
  122. this.props.outlineScale = props.outlineScale
  123. ValueCell.update(this.renderable.values.uOutlineScale, props.outlineScale * this.webgl.pixelRatio)
  124. }
  125. if (props.outlineThreshold !== undefined) {
  126. this.props.outlineThreshold = props.outlineThreshold
  127. ValueCell.update(this.renderable.values.uOutlineThreshold, props.outlineThreshold)
  128. }
  129. if (props.useFog !== undefined) {
  130. this.props.useFog = props.useFog
  131. ValueCell.update(this.renderable.values.dUseFog, props.useFog)
  132. }
  133. this.renderable.update()
  134. }
  135. render(toDrawingBuffer: boolean) {
  136. ValueCell.update(this.renderable.values.uFar, this.camera.far)
  137. ValueCell.update(this.renderable.values.uNear, this.camera.near)
  138. ValueCell.update(this.renderable.values.uFogFar, this.camera.fogFar)
  139. ValueCell.update(this.renderable.values.uFogNear, this.camera.fogNear)
  140. ValueCell.update(this.renderable.values.dOrthographic, this.camera.state.mode === 'orthographic' ? 1 : 0)
  141. const { gl, state } = this.webgl
  142. if (toDrawingBuffer) {
  143. this.webgl.unbindFramebuffer()
  144. gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight)
  145. } else {
  146. this.target.bind()
  147. }
  148. state.disable(gl.SCISSOR_TEST)
  149. state.disable(gl.BLEND)
  150. state.disable(gl.DEPTH_TEST)
  151. state.depthMask(false)
  152. this.renderable.render()
  153. }
  154. }