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