-
Notifications
You must be signed in to change notification settings - Fork 3
fix: Use dynamic loop count for VolumetricFog and TSL soft particles #1167
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,7 @@ | |
| */ | ||
|
|
||
| import * as THREE from 'three'; | ||
| import { MeshBasicNodeMaterial } from 'three/webgpu'; | ||
| import { | ||
| Fn, | ||
| vec2, | ||
|
|
@@ -40,6 +41,10 @@ import { | |
| positionLocal, | ||
| modelWorldMatrix, | ||
| cameraPosition, | ||
| modelViewProjection, | ||
| cameraNear, | ||
| cameraFar, | ||
| viewportCoordinate, | ||
| } from 'three/tsl'; | ||
|
|
||
| // ============================================ | ||
|
|
@@ -285,16 +290,20 @@ export class AdvancedParticleSystem { | |
|
|
||
| // Geometry and materials | ||
| private geometry: THREE.PlaneGeometry; | ||
| private material: THREE.MeshBasicMaterial; // Will be enhanced with custom shader | ||
| private material: MeshBasicNodeMaterial; | ||
|
|
||
| // Textures | ||
| private fireTexture: THREE.Texture; | ||
| private smokeTexture: THREE.Texture; | ||
| private glowTexture: THREE.Texture; | ||
|
|
||
| // Uniforms | ||
| // Uniforms for TSL soft particles | ||
| private timeUniform: { value: number }; | ||
| private lightDirUniform: { value: THREE.Vector3 }; | ||
| private depthTextureUniform: ReturnType<typeof uniform>; | ||
| private softnessUniform: ReturnType<typeof uniform>; | ||
| private cameraNearUniform: ReturnType<typeof uniform>; | ||
| private cameraFarUniform: ReturnType<typeof uniform>; | ||
|
|
||
| // Terrain height function | ||
| private getTerrainHeight: ((x: number, z: number) => number) | null = null; | ||
|
|
@@ -352,14 +361,24 @@ export class AdvancedParticleSystem { | |
| // Create billboard geometry | ||
| this.geometry = new THREE.PlaneGeometry(1, 1); | ||
|
|
||
| // Create material with additive blending for glowing particles | ||
| this.material = new THREE.MeshBasicMaterial({ | ||
| map: this.glowTexture, | ||
| transparent: true, | ||
| depthWrite: false, | ||
| blending: THREE.AdditiveBlending, | ||
| side: THREE.DoubleSide, | ||
| }); | ||
| // Initialize TSL uniforms for soft particles | ||
| // Create a placeholder 1x1 depth texture (will be set properly via setDepthTexture) | ||
| const placeholderDepth = new THREE.DataTexture( | ||
| new Float32Array([1.0]), | ||
| 1, | ||
| 1, | ||
| THREE.RedFormat, | ||
| THREE.FloatType | ||
| ); | ||
| placeholderDepth.needsUpdate = true; | ||
|
|
||
| this.depthTextureUniform = uniform(placeholderDepth); | ||
| this.softnessUniform = uniform(0.5); // Default softness | ||
| this.cameraNearUniform = uniform(0.1); | ||
| this.cameraFarUniform = uniform(1000.0); | ||
|
|
||
| // Create TSL soft particle material with depth-aware alpha fade | ||
| this.material = this.createSoftParticleMaterial(); | ||
|
|
||
| // Create instanced mesh | ||
| this.instancedMesh = new THREE.InstancedMesh( | ||
|
|
@@ -505,6 +524,86 @@ export class AdvancedParticleSystem { | |
| return texture; | ||
| } | ||
|
|
||
| /** | ||
| * Create TSL soft particle material with depth-aware alpha fade | ||
| * Particles fade when approaching scene geometry to avoid hard clipping | ||
| */ | ||
| private createSoftParticleMaterial(): MeshBasicNodeMaterial { | ||
| const glowTex = this.glowTexture; | ||
| const depthTex = this.depthTextureUniform; | ||
| const softness = this.softnessUniform; | ||
| const near = this.cameraNearUniform; | ||
| const far = this.cameraFarUniform; | ||
|
|
||
| // TSL fragment shader for soft particles | ||
| const softParticleFragment = Fn(() => { | ||
| // Sample the glow texture | ||
| const texCoord = uv(); | ||
| const texColor = texture(glowTex, texCoord); | ||
|
|
||
| // Get screen-space coordinates for depth sampling | ||
| const screenUV = viewportCoordinate.xy; | ||
|
|
||
| // Sample scene depth and convert to linear | ||
| const sceneDepthSample = texture(depthTex, screenUV).r; | ||
| const sceneNDC = sceneDepthSample.mul(2.0).sub(1.0); | ||
| const sceneLinearDepth = near.mul(far).div( | ||
| far.sub(sceneNDC.mul(far.sub(near))) | ||
| ); | ||
|
|
||
| // Get particle's linear depth from gl_FragCoord.z | ||
| const particleNDC = viewportCoordinate.z.mul(2.0).sub(1.0); | ||
| const particleLinearDepth = near.mul(far).div( | ||
|
Comment on lines
+554
to
+556
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Useful? React with 👍 / 👎. |
||
| far.sub(particleNDC.mul(far.sub(near))) | ||
| ); | ||
|
|
||
| // Soft particle fade: alpha decreases as particle approaches scene geometry | ||
| // depthDiff is positive when particle is in front of scene geometry | ||
| const depthDiff = sceneLinearDepth.sub(particleLinearDepth); | ||
| const softFade = clamp(depthDiff.div(softness), 0.0, 1.0); | ||
|
|
||
| // Apply soft fade to texture alpha | ||
| const finalAlpha = texColor.a.mul(softFade); | ||
|
|
||
| return vec4(texColor.rgb, finalAlpha); | ||
| }); | ||
|
|
||
| // Create MeshBasicNodeMaterial with custom fragment | ||
| const material = new MeshBasicNodeMaterial(); | ||
| material.transparent = true; | ||
| material.depthWrite = false; | ||
| material.blending = THREE.AdditiveBlending; | ||
| material.side = THREE.DoubleSide; | ||
|
|
||
| // Set the color/alpha output using the soft particle node | ||
| material.colorNode = softParticleFragment(); | ||
|
|
||
| return material; | ||
| } | ||
|
|
||
| /** | ||
| * Set the depth texture for soft particle rendering | ||
| * Should be called with the scene's depth buffer texture | ||
| */ | ||
| public setDepthTexture(depthTexture: THREE.Texture): void { | ||
| this.depthTextureUniform.value = depthTexture; | ||
| } | ||
|
|
||
| /** | ||
| * Update camera parameters for soft particle depth calculations | ||
| */ | ||
| public updateCamera(camera: THREE.PerspectiveCamera): void { | ||
| this.cameraNearUniform.value = camera.near; | ||
| this.cameraFarUniform.value = camera.far; | ||
| } | ||
|
|
||
| /** | ||
| * Set the softness value for depth fade (higher = softer transition) | ||
| */ | ||
| public setSoftness(softness: number): void { | ||
| this.softnessUniform.value = softness; | ||
| } | ||
|
|
||
| /** | ||
| * Emit particles at a position | ||
| */ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
viewportCoordinateis defined in TSL as pixel-space coordinates (seenode_modules/three/src/nodes/display/ScreenNode.jsaround theviewportCoordinate/viewportUVdefinitions). Passing pixel units directly intotexture()means the depth lookup will be out of[0,1]for most fragments and clamp to edges, so the soft-fade will read a near-constant depth and won’t correctly fade near geometry. UseviewportUV(or divide byviewportSize) for normalized sampling.Useful? React with 👍 / 👎.