threepipe
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

ContactShadowGroundPlugin.ts 8.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. import {getOrCall, onChange, serialize} from 'ts-browser-helpers'
  2. import {
  3. BasicDepthPacking,
  4. Color,
  5. Euler,
  6. LinearFilter,
  7. MeshDepthMaterial,
  8. NoBlending,
  9. NoColorSpace,
  10. OrthographicCamera,
  11. RGBAFormat, Texture,
  12. UnsignedByteType,
  13. Vector3,
  14. WebGLRenderTarget,
  15. } from 'three'
  16. import {BaseGroundPlugin} from '../base/BaseGroundPlugin'
  17. import {GBufferRenderPass} from '../../postprocessing'
  18. import {ThreeViewer} from '../../viewer'
  19. import {IRenderTarget} from '../../rendering'
  20. import {uiPanelContainer, uiSlider, uiToggle} from 'uiconfig.js'
  21. import {HVBlurHelper} from '../../three/utils/HVBlurHelper'
  22. import {shaderReplaceString} from '../../utils'
  23. import {PhysicalMaterial} from '../../core'
  24. @uiPanelContainer('Contact Shadow Ground')
  25. export class ContactShadowGroundPlugin extends BaseGroundPlugin {
  26. static readonly PluginType = 'ContactShadowGroundPlugin'
  27. @uiToggle('Contact Shadows')
  28. @onChange(ContactShadowGroundPlugin.prototype.refresh)
  29. @serialize() contactShadows = true
  30. @uiSlider('Shadow Scale', [0, 2])
  31. @serialize()
  32. @onChange(ContactShadowGroundPlugin.prototype._refreshShadowCameraFrustum)
  33. shadowScale = 1
  34. @uiSlider('Shadow Height', [0, 20])
  35. @serialize()
  36. @onChange(ContactShadowGroundPlugin.prototype._refreshShadowCameraFrustum)
  37. shadowHeight = 5
  38. @uiSlider('Blur Amount', [0, 10])
  39. @serialize()
  40. @onChange(ContactShadowGroundPlugin.prototype._setDirty)
  41. blurAmount = 1
  42. shadowCamera = new OrthographicCamera(-1, 1, 1, -1, 0.001, this.shadowHeight)
  43. private _depthPass?: GBufferRenderPass<'contactShadowGround', WebGLRenderTarget|undefined>
  44. private _blurHelper?: HVBlurHelper
  45. constructor() {
  46. super()
  47. this._refreshShadowCameraFrustum = this._refreshShadowCameraFrustum.bind(this)
  48. this.refresh = this.refresh.bind(this)
  49. }
  50. onAdded(viewer: ThreeViewer): void {
  51. const target = viewer.renderManager.createTarget<IRenderTarget & WebGLRenderTarget>({
  52. type: UnsignedByteType,
  53. format: RGBAFormat,
  54. colorSpace: NoColorSpace,
  55. size: {width: 512, height: 512},
  56. generateMipmaps: false,
  57. depthBuffer: true,
  58. minFilter: LinearFilter,
  59. magFilter: LinearFilter,
  60. // isAntialiased: this._viewer.isAntialiased,
  61. })
  62. target.texture.name = 'groundContactDepthTexture'
  63. // https://github.com/mrdoob/three.js/blob/master/examples/webgl_shadow_contact.html
  64. const material = new MeshDepthMaterial({
  65. // depthPacking: RGBADepthPacking, // todo
  66. depthPacking: BasicDepthPacking,
  67. transparent: false,
  68. blending: NoBlending,
  69. })
  70. material.onBeforeCompile = (shader) => {
  71. shader.uniforms.opacity.value = 1.
  72. shader.fragmentShader = shaderReplaceString(shader.fragmentShader,
  73. 'gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );',
  74. 'gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), 1.0 );',
  75. // 'gl_FragColor = vec4( vec3( 0.0 ), ( 1.0 - fragCoordZ ) * darkness );',
  76. )
  77. }
  78. this._depthPass = new GBufferRenderPass('contactShadowGround', target, material, new Color(0, 0, 0), 0)
  79. this._blurHelper = new HVBlurHelper(viewer)
  80. super.onAdded(viewer)
  81. }
  82. onRemove(viewer: ThreeViewer): void {
  83. const target = getOrCall(this._depthPass?.target)
  84. if (target) this._viewer?.renderManager.disposeTarget(target)
  85. this._depthPass?.dispose()
  86. this._depthPass = undefined
  87. this._blurHelper?.dispose()
  88. this._blurHelper = undefined
  89. return super.onRemove(viewer)
  90. }
  91. // todo: dispose target, material, pass and stuff
  92. protected _postFrame() {
  93. super._postFrame()
  94. if (!this._viewer) return
  95. }
  96. protected _preRender() {
  97. super._preRender()
  98. if (!this._viewer || !this._depthPass || !this._blurHelper || !this.contactShadows || !this.visible) return
  99. this._depthPass.scene = this._viewer.scene
  100. this._depthPass.camera = this.shadowCamera
  101. this._depthPass.render(this._viewer.renderManager.renderer, null)
  102. const target = getOrCall(this._depthPass.target)
  103. if (!target) return
  104. const blurTarget = this._viewer.renderManager.getTempTarget<IRenderTarget & WebGLRenderTarget>({
  105. type: UnsignedByteType,
  106. format: RGBAFormat,
  107. colorSpace: NoColorSpace,
  108. size: {width: 1024, height: 1024},
  109. generateMipmaps: false,
  110. depthBuffer: false,
  111. minFilter: LinearFilter,
  112. magFilter: LinearFilter,
  113. // isAntialiased: this._viewer.isAntialiased,
  114. })
  115. this._blurHelper.blur(target.texture, target, blurTarget, this.blurAmount / 256)
  116. this._blurHelper.blur(target.texture, target, blurTarget, 0.4 * this.blurAmount / 256)
  117. this._viewer.renderManager.releaseTempTarget(blurTarget)
  118. }
  119. protected _refreshTransform() {
  120. if (!super._refreshTransform()) return false
  121. if (!this._mesh) return false
  122. if (!this._viewer) return false
  123. this.shadowCamera.position.copy(this._mesh.getWorldPosition(new Vector3()))
  124. this.shadowCamera.setRotationFromEuler(new Euler(Math.PI / 2., 0, 0))
  125. this.shadowCamera.updateMatrixWorld()
  126. this._refreshShadowCameraFrustum()
  127. this._mesh.scale.y = -this.size
  128. return true
  129. }
  130. private _refreshShadowCameraFrustum() {
  131. if (!this.shadowCamera) return
  132. this.shadowCamera.left = -this.size / (2 * this.shadowScale)
  133. this.shadowCamera.right = this.size / (2 * this.shadowScale)
  134. this.shadowCamera.top = this.size / (2 * this.shadowScale)
  135. this.shadowCamera.bottom = -this.size / (2 * this.shadowScale)
  136. this.shadowCamera.far = this.shadowHeight
  137. this.shadowCamera.updateProjectionMatrix()
  138. this._setDirty()
  139. }
  140. private _setDirty() {
  141. this._viewer?.setDirty()
  142. }
  143. protected _removeMaterial() {
  144. if (!this._material) return
  145. this._material.alphaMap = null
  146. if (this._material.userData.ssreflDisabled) delete this._material.userData.ssreflDisabled
  147. if (this._material.userData.ssreflNonPhysical) delete this._material.userData.ssreflNonPhysical
  148. super._removeMaterial()
  149. }
  150. private _depthTex: Texture|null = null
  151. public refresh(): void {
  152. if (!this._viewer) return
  153. if (!this.contactShadows) {
  154. if (this._material?.alphaMap === this._depthTex) {
  155. this._material.alphaMap = null
  156. this._material.setDirty()
  157. }
  158. if (this._material?.userData.__csgpParamsSet) {
  159. delete this._material.userData.__csgpParamsSet
  160. delete this._material.userData.ssreflDisabled
  161. delete this._material.userData.ssreflNonPhysical
  162. }
  163. this._depthTex = null
  164. } else {
  165. this._depthTex = getOrCall(this._depthPass?.target)?.texture || null
  166. }
  167. super.refresh()
  168. }
  169. protected _createMaterial(material?: PhysicalMaterial): PhysicalMaterial {
  170. const mat = super._createMaterial(material)
  171. mat.roughness = 1
  172. mat.metalness = 0
  173. mat.color.set(0x111111)
  174. mat.transparent = true
  175. // mat.userData.inverseAlphaMap = false // this must be false, if getting inverted colors, check clear color of gbuffer render pass.
  176. return mat
  177. }
  178. protected _refreshMaterial() {
  179. if (!this._viewer) return false
  180. const isNewMaterial = super._refreshMaterial()
  181. if (!this._material) return isNewMaterial
  182. if (this.contactShadows) {
  183. this._material.userData.ssreflDisabled = true
  184. this._material.userData.ssreflNonPhysical = false
  185. this._material.userData.__csgpParamsSet = true
  186. this._material.alphaMap = this._depthTex || null
  187. this._material.setDirty()
  188. }
  189. return isNewMaterial
  190. }
  191. }