@takram/three-atmosphere
Version:
A Three.js and R3F implementation of Precomputed Atmospheric Scattering
164 lines (140 loc) • 5.11 kB
text/typescript
import {
ClearPass,
DepthCopyPass,
DepthMaskMaterial,
DepthTestStrategy,
Pass,
RenderPass,
Selection,
ShaderPass
} from 'postprocessing'
import {
BasicDepthPacking,
Color,
DepthTexture,
LessEqualDepth,
MeshBasicMaterial,
RedFormat,
RGBADepthPacking,
Uniform,
UnsignedIntType,
WebGLRenderTarget,
type Camera,
type DepthPackingStrategies,
type Scene,
type Texture,
type TextureDataType,
type WebGLRenderer
} from 'three'
import { resolveIncludes } from '@takram/three-geospatial'
import { depth } from '@takram/three-geospatial/shaders'
import fragmentShader from './shaders/lightingMask.frag?raw'
export class LightingMaskPass extends Pass {
private readonly renderPass: RenderPass
private readonly depthTexture: DepthTexture
private readonly renderTarget: WebGLRenderTarget
private readonly depthCopyPass0: DepthCopyPass
private readonly depthCopyPass1: DepthCopyPass
private readonly clearPass: ClearPass
private readonly depthMaskMaterial: DepthMaskMaterial
private readonly depthMaskPass: ShaderPass
readonly selection = new Selection()
constructor(scene: Scene, camera: Camera) {
super('LightingMaskPass')
this.needsSwap = false
this.needsDepthTexture = true
this.renderPass = new RenderPass(scene, camera, new MeshBasicMaterial())
this.renderPass.ignoreBackground = true
this.renderPass.skipShadowMapUpdate = true
this.renderPass.selection = this.selection
// We need a separate depth buffer. See the discussion below.
this.depthTexture = new DepthTexture(1, 1, UnsignedIntType)
this.renderTarget = new WebGLRenderTarget(1, 1, {
format: RedFormat,
depthTexture: this.depthTexture
})
this.depthCopyPass0 = new DepthCopyPass({ depthPacking: RGBADepthPacking })
this.depthCopyPass1 = new DepthCopyPass({ depthPacking: RGBADepthPacking })
this.clearPass = new ClearPass(true, false, false)
this.clearPass.overrideClearColor = new Color(0xffffff)
this.clearPass.overrideClearAlpha = 1
const depthMaskMaterial = new DepthMaskMaterial()
depthMaskMaterial.fragmentShader = resolveIncludes(fragmentShader, {
core: { depth }
})
depthMaskMaterial.uniforms.inverted = new Uniform(false)
depthMaskMaterial.copyCameraSettings(camera)
depthMaskMaterial.depthBuffer0 = this.depthCopyPass0.texture
depthMaskMaterial.depthPacking0 = RGBADepthPacking
depthMaskMaterial.depthBuffer1 = this.depthCopyPass1.texture
depthMaskMaterial.depthPacking1 = RGBADepthPacking
depthMaskMaterial.depthMode = LessEqualDepth
depthMaskMaterial.maxDepthStrategy = DepthTestStrategy.DISCARD_MAX_DEPTH
this.depthMaskMaterial = depthMaskMaterial
this.depthMaskPass = new ShaderPass(depthMaskMaterial)
}
// eslint-disable-next-line accessor-pairs
override set mainScene(value: Scene) {
this.renderPass.mainScene = value
}
// eslint-disable-next-line accessor-pairs
override set mainCamera(value: Camera) {
this.renderPass.mainCamera = value
this.depthMaskMaterial.copyCameraSettings(value)
}
override initialize(
renderer: WebGLRenderer,
alpha: boolean,
frameBufferType: TextureDataType
): void {
this.renderPass.initialize(renderer, alpha, frameBufferType)
this.clearPass.initialize(renderer, alpha, frameBufferType)
this.depthMaskPass.initialize(renderer, alpha, frameBufferType)
}
override setDepthTexture(
depthTexture: Texture,
depthPacking: DepthPackingStrategies = BasicDepthPacking
): void {
this.depthCopyPass0.setDepthTexture(depthTexture, depthPacking)
this.depthCopyPass1.setDepthTexture(this.depthTexture, depthPacking)
}
override render(
renderer: WebGLRenderer,
inputBuffer: WebGLRenderTarget | null,
outputBuffer: WebGLRenderTarget | null,
deltaTime?: number,
stencilTest?: boolean
): void {
const autoClear = renderer.autoClear
renderer.autoClear = false
// We cannot precisely compare the depth buffer and a texture rendered with
// MeshDepthMaterial. Store the current depth and the selection depth with
// the same packing and create a mask.
this.depthCopyPass0.render(renderer, null, null)
this.renderPass.render(renderer, this.renderTarget, null)
this.depthCopyPass1.render(renderer, null, null)
this.clearPass.render(renderer, this.renderTarget, null)
this.depthMaskPass.render(renderer, null, this.renderTarget)
renderer.autoClear = autoClear
}
override setSize(width: number, height: number): void {
this.renderTarget.setSize(width, height)
this.depthCopyPass0.setSize(width, height)
this.depthCopyPass1.setSize(width, height)
}
get texture(): Texture {
return this.renderTarget.texture
}
get selectionLayer(): number {
return this.selection.layer
}
set selectionLayer(value: number) {
this.selection.layer = value
}
get inverted(): boolean {
return this.depthMaskMaterial.uniforms.inverted.value
}
set inverted(value: boolean) {
this.depthMaskMaterial.uniforms.inverted.value = value
}
}