UNPKG

@takram/three-atmosphere

Version:
200 lines (175 loc) 5.46 kB
import { Color, GLSL3, Matrix4, Uniform, Vector3, type BufferGeometry, type Camera, type Group, type Object3D, type Scene, type Texture, type WebGLRenderer } from 'three' import { define, resolveIncludes } from '@takram/three-geospatial' import { raySphereIntersection } from '@takram/three-geospatial/shaders' import { AtmosphereMaterialBase, atmosphereMaterialParametersBaseDefaults, type AtmosphereMaterialBaseParameters, type AtmosphereMaterialBaseUniforms } from './AtmosphereMaterialBase' import { type AtmosphereShadowLength } from './types' import functions from './shaders/functions.glsl?raw' import parameters from './shaders/parameters.glsl?raw' import fragmentShader from './shaders/sky.frag?raw' import sky from './shaders/sky.glsl?raw' import vertexShader from './shaders/sky.vert?raw' declare module 'three' { interface Camera { isPerspectiveCamera?: boolean } } export interface SkyMaterialParameters extends AtmosphereMaterialBaseParameters { sun?: boolean moon?: boolean moonDirection?: Vector3 moonAngularRadius?: number lunarRadianceScale?: number groundAlbedo?: Color } export const skyMaterialParametersDefaults = { ...atmosphereMaterialParametersBaseDefaults, sun: true, moon: true, moonAngularRadius: 0.0045, // ≈ 15.5 arcminutes lunarRadianceScale: 1 } satisfies SkyMaterialParameters export interface SkyMaterialUniforms { [key: string]: Uniform<unknown> inverseProjectionMatrix: Uniform<Matrix4> inverseViewMatrix: Uniform<Matrix4> moonDirection: Uniform<Vector3> moonAngularRadius: Uniform<number> lunarRadianceScale: Uniform<number> groundAlbedo: Uniform<Color> shadowLengthBuffer: Uniform<Texture | null> } export class SkyMaterial extends AtmosphereMaterialBase { declare uniforms: AtmosphereMaterialBaseUniforms & SkyMaterialUniforms shadowLength: AtmosphereShadowLength | null = null constructor(params?: SkyMaterialParameters) { const { sun, moon, moonDirection, moonAngularRadius, lunarRadianceScale, groundAlbedo, ...others } = { ...skyMaterialParametersDefaults, ...params } super({ name: 'SkyMaterial', glslVersion: GLSL3, vertexShader: resolveIncludes(vertexShader, { parameters }), fragmentShader: resolveIncludes(fragmentShader, { core: { raySphereIntersection }, parameters, functions, sky }), ...others, uniforms: { inverseProjectionMatrix: new Uniform(new Matrix4()), inverseViewMatrix: new Uniform(new Matrix4()), moonDirection: new Uniform(moonDirection?.clone() ?? new Vector3()), moonAngularRadius: new Uniform(moonAngularRadius), lunarRadianceScale: new Uniform(lunarRadianceScale), groundAlbedo: new Uniform(groundAlbedo?.clone() ?? new Color(0)), shadowLengthBuffer: new Uniform(null), ...others.uniforms } satisfies SkyMaterialUniforms, defines: { PERSPECTIVE_CAMERA: '1' }, depthTest: true }) this.sun = sun this.moon = moon } override onBeforeRender( renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D, group: Group ): void { super.onBeforeRender(renderer, scene, camera, geometry, object, group) const { uniforms, defines } = this uniforms.inverseProjectionMatrix.value.copy(camera.projectionMatrixInverse) uniforms.inverseViewMatrix.value.copy(camera.matrixWorld) const prevPerspectiveCamera = defines.PERSPECTIVE_CAMERA != null const nextPerspectiveCamera = camera.isPerspectiveCamera === true if (nextPerspectiveCamera !== prevPerspectiveCamera) { if (nextPerspectiveCamera) { defines.PERSPECTIVE_CAMERA = '1' } else { delete defines.PERSPECTIVE_CAMERA } this.needsUpdate = true } const color = this.groundAlbedo const prevGroundAlbedo = defines.GROUND_ALBEDO != null const nextGroundAlbedo = color.r !== 0 || color.g !== 0 || color.b !== 0 if (nextGroundAlbedo !== prevGroundAlbedo) { if (nextGroundAlbedo) { this.defines.GROUND_ALBEDO = '1' } else { delete this.defines.GROUND_ALBEDO } this.needsUpdate = true } const shadowLength = this.shadowLength const prevShadowLength = defines.HAS_SHADOW_LENGTH != null const nextShadowLength = shadowLength != null if (nextShadowLength !== prevShadowLength) { if (nextShadowLength) { defines.HAS_SHADOW_LENGTH = '1' } else { delete defines.HAS_SHADOW_LENGTH uniforms.shadowLengthBuffer.value = null } this.needsUpdate = true } if (nextShadowLength) { uniforms.shadowLengthBuffer.value = shadowLength.map } } @define('SUN') sun: boolean @define('MOON') moon: boolean get moonDirection(): Vector3 { return this.uniforms.moonDirection.value } get moonAngularRadius(): number { return this.uniforms.moonAngularRadius.value } set moonAngularRadius(value: number) { this.uniforms.moonAngularRadius.value = value } get lunarRadianceScale(): number { return this.uniforms.lunarRadianceScale.value } set lunarRadianceScale(value: number) { this.uniforms.lunarRadianceScale.value = value } get groundAlbedo(): Color { return this.uniforms.groundAlbedo.value } }