Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 1 addition & 50 deletions examples/jsm/tsl/utils/Raymarching.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { varying, vec4, modelWorldMatrixInverse, cameraPosition, positionGeometry, float, Fn, Loop, max, min, vec2, vec3, smoothstep, If, Break } from 'three/tsl';
import { varying, vec4, modelWorldMatrixInverse, cameraPosition, positionGeometry, float, Fn, Loop, max, min, vec2, vec3 } from 'three/tsl';

const hitBox = /*@__PURE__*/ Fn( ( { orig, dir } ) => {

Expand Down Expand Up @@ -63,52 +63,3 @@ export const RaymarchingBox = ( steps, callback ) => {
} );

};

/**
* Performs raymarching on the specified 3D texture.
*
* @tsl
* @function
* @param {Object} params - The parameters for the function.
* @param {Texture|Node} params.texture - The 3D texture to sample.
* @param {number|Node} [params.range=0.1] - The range for the smoothstep function.
* @param {number|Node} [params.threshold=0.25] - The threshold for the smoothstep function.
* @param {number|Node} [params.opacity=0.25] - The opacity value for the final color.
* @param {number|Node} [params.steps=100] - The number of steps for raymarching.
* @returns {Function} The generated function that performs raymarching on the 3D texture.
*/
export const raymarchingTexture3D = Fn( ( {
texture,
range = float( 0.1 ),
threshold = float( 0.25 ),
opacity = float( 0.25 ),
steps = float( 100 )
} ) => {

const finalColor = vec4( 0 ).toVar();

RaymarchingBox( steps, ( { positionRay } ) => {

const mapValue = float( texture.sample( positionRay.add( 0.5 ) ).r ).toVar();

mapValue.assign( smoothstep( threshold.sub( range ), threshold.add( range ), mapValue ).mul( opacity ) );

const shading = texture.sample( positionRay.add( vec3( - 0.01 ) ) ).r.sub( texture.sample( positionRay.add( vec3( 0.01 ) ) ).r );

const col = shading.mul( 3.0 ).add( positionRay.x.add( positionRay.y ).mul( 0.25 ) ).add( 0.2 );

finalColor.rgb.addAssign( finalColor.a.oneMinus().mul( mapValue ).mul( col ) );

finalColor.a.addAssign( finalColor.a.oneMinus().mul( mapValue ) );

If( finalColor.a.greaterThanEqual( 0.95 ), () => {

Break();

} );

} );

return finalColor;

} );
60 changes: 47 additions & 13 deletions examples/webgpu_volume_cloud.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
<script type="module">

import * as THREE from 'three';
import { texture3D, uniform } from 'three/tsl';
import { float, vec3, vec4, If, Break, Fn, smoothstep, texture3D, uniform } from 'three/tsl';

import { raymarchingTexture3D } from 'three/addons/tsl/utils/Raymarching.js';
import { RaymarchingBox } from 'three/addons/tsl/utils/Raymarching.js';

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js';
Expand Down Expand Up @@ -111,34 +111,68 @@
texture.unpackAlignment = 1;
texture.needsUpdate = true;

// Cloud Shader
// Shader

const transparentRaymarchingTexture = Fn( ( {
texture,
range = float( 0.1 ),
threshold = float( 0.25 ),
opacity = float( 0.25 ),
steps = float( 100 )
} ) => {

const finalColor = vec4( 0 ).toVar();

RaymarchingBox( steps, ( { positionRay } ) => {

const mapValue = float( texture.sample( positionRay.add( 0.5 ) ).r ).toVar();

mapValue.assign( smoothstep( threshold.sub( range ), threshold.add( range ), mapValue ).mul( opacity ) );

const shading = texture.sample( positionRay.add( vec3( - 0.01 ) ) ).r.sub( texture.sample( positionRay.add( vec3( 0.01 ) ) ).r );

const col = shading.mul( 3.0 ).add( positionRay.x.add( positionRay.y ).mul( 0.25 ) ).add( 0.2 );

finalColor.rgb.addAssign( finalColor.a.oneMinus().mul( mapValue ).mul( col ) );

finalColor.a.addAssign( finalColor.a.oneMinus().mul( mapValue ) );

If( finalColor.a.greaterThanEqual( 0.95 ), () => {

Break();

} );

} );

return finalColor;

} );

// Material

const baseColor = uniform( new THREE.Color( 0x798aa0 ) );
const range = uniform( 0.1 );
const threshold = uniform( 0.25 );
const opacity = uniform( 0.25 );
const steps = uniform( 100 );

const cloud3d = raymarchingTexture3D( {
const cloud3d = transparentRaymarchingTexture( {
texture: texture3D( texture, null, 0 ),
range: range,
threshold: threshold,
opacity: opacity,
steps: steps
range,
threshold,
opacity,
steps
} );

const finalCloud = cloud3d.setRGB( cloud3d.rgb.add( baseColor ) );

//

const geometry = new THREE.BoxGeometry( 1, 1, 1 );

const material = new THREE.NodeMaterial();
material.colorNode = finalCloud;
material.side = THREE.BackSide;
material.transparent = true;

mesh = new THREE.Mesh( geometry, material );
mesh = new THREE.Mesh( new THREE.BoxGeometry( 1, 1, 1 ), material );
scene.add( mesh );

//
Expand Down
61 changes: 33 additions & 28 deletions examples/webgpu_volume_perlin.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@
</script>

<script type="module">

import * as THREE from 'three';
import { Break, If, vec3, materialReference, Fn } from 'three/tsl';
import { Break, If, vec3, vec4, texture3D, uniform, Fn } from 'three/tsl';

import { RaymarchingBox } from 'three/addons/tsl/utils/Raymarching.js';

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js';
Expand Down Expand Up @@ -86,52 +89,54 @@
texture.unpackAlignment = 1;
texture.needsUpdate = true;

// Material
// Shader

const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.VolumeNodeMaterial( {
side: THREE.BackSide
} );
const opaqueRaymarchingTexture = Fn( ( { texture, steps, threshold } ) => {

material.base = new THREE.Color( 0x798aa0 );
material.map = texture;
material.steps = 200;
material.threshold = 0.6;
const finalColor = vec4( 0 ).toVar();

const threshold = materialReference( 'threshold', 'float' );
RaymarchingBox( steps, ( { positionRay } ) => {

material.testNode = Fn( ( { map, mapValue, probe, finalColor } ) => {
const mapValue = texture.sample( positionRay.add( 0.5 ) ).r.toVar();

If( mapValue.greaterThan( threshold ), () => {
If( mapValue.greaterThan( threshold ), () => {

const p = vec3( probe ).add( 0.5 );
const p = vec3( positionRay ).add( 0.5 );

finalColor.rgb.assign( map.normal( p ).mul( 0.5 ).add( probe.mul( 1.5 ).add( 0.25 ) ) );
finalColor.a.assign( 1 );
Break();
finalColor.rgb.assign( texture.normal( p ).mul( 0.5 ).add( positionRay.mul( 1.5 ).add( 0.25 ) ) );
finalColor.a.assign( 1 );
Break();

} );
} );

} );
} );

mesh = new THREE.Mesh( geometry, material );
return finalColor;

scene.add( mesh );
} );

//

const parameters = { threshold: 0.6, steps: 200 };
const threshold = uniform( 0.6 );
const steps = uniform( 200 );

function update() {
const material = new THREE.NodeMaterial();
material.colorNode = opaqueRaymarchingTexture( {
texture: texture3D( texture, null, 0 ),
steps,
threshold
} );
material.side = THREE.BackSide;
material.transparent = true;

material.threshold = parameters.threshold;
material.steps = parameters.steps;
mesh = new THREE.Mesh( new THREE.BoxGeometry( 1, 1, 1 ), material );
scene.add( mesh );

}
//

const gui = new GUI();
gui.add( parameters, 'threshold', 0, 1, 0.01 ).onChange( update );
gui.add( parameters, 'steps', 0, 300, 1 ).onChange( update );
gui.add( threshold, 'value', 0, 1, 0.01 ).name( 'threshold' );
gui.add( steps, 'value', 0, 300, 1 ).name( 'steps' );

window.addEventListener( 'resize', onWindowResize );

Expand Down