How do you integrate Signed Distance Fields (SDF) into a basic three.js scene?

In principle, it works (extra mesh not visible).

SDF-GeometryGenerator


the code:

<!DOCTYPE html>
<!-- @ hofk customized from https://rawcdn.githack.com/mrdoob/three.js/r168/examples/webgl_geometry_sdf.html --> 
<head>
	<title>SDF-GeometryGenerator</title>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
</head>
<body></body>
 
<script type="module">

import * as THREE from '../jsm/three.module.160.js';
import { OrbitControls } from '../jsm/OrbitControls.160.js';
import { SDFGeometryGenerator } from '../jsm/SDFGeometryGenerator.160.js';
 
let renderer,  meshFromSDF, scene, camera, clock, controls;

const settings = {
	res: 4,
	bounds: 1.0,
	autoRotate: false,
	wireframe: true,
	material: 'normal',
	vertexCount: '10'
};
  

const shader = /* glsl */`

    float sdSphere( vec3 p, float s ) {
        return length( p ) - s;
    }
    
    float sdTorus( vec3 p, vec2 t ) {
        vec2 q = vec2( length( p.xz ) - t.x, p.y );
        return length( q ) - t.y;
    }
    
    float sdRoundBox( vec3 p, vec3 b, float r ) {
        vec3 q = abs( p ) - b + r;
        return length( max( q, 0.0 ) ) + min( max( q.x, max( q.y, q.z ) ), 0.0 ) - r;
    }
        
    float dist( vec3 p ) {

        float dSph = sdSphere( p, 0.4 );
        float dTor = sdTorus( p, vec2( 0.4, 0.15 ) );
        
        float dSub = max( -dSph, dTor ); // SDF Subtraction
          
        float dRBox = sdRoundBox( p, vec3( 0.2, 0.1, 0.99 ), 0.1 );              
        
        float d = min( dSub, dRBox );
        
        return d;
        
    }
    
`;

init();

function init() {

	const w = window.innerWidth;
	const h = window.innerHeight;
    
    camera = new THREE.OrthographicCamera( w / - 2, w / 2, h / 2, h / - 2, 0.01, 1600 );
	camera.position.z = 1100;
	scene = new THREE.Scene();
	clock = new THREE.Clock();

	renderer = new THREE.WebGLRenderer( { antialias: true } );
	renderer.setPixelRatio( window.devicePixelRatio );
	renderer.setSize( window.innerWidth, window.innerHeight );
	renderer.setAnimationLoop( animate );
	document.body.appendChild( renderer.domElement );

	controls = new OrbitControls( camera, renderer.domElement );
	controls.enableDamping = true;

	window.addEventListener( 'resize', onWindowResize );
 
	compile();
    
    // mesh not visible  
    const mesh = new THREE.Mesh( new THREE.SphereGeometry( 0.6 ), new THREE.MeshNormalMaterial( ) );
    scene.add( mesh );
    
}

function compile() {

	const generator = new SDFGeometryGenerator( renderer );
	const geometry = generator.generate( Math.pow( 2, settings.res + 2 ), shader, settings.bounds );
	geometry.computeVertexNormals();

	if ( meshFromSDF ) { // updates mesh

		meshFromSDF.geometry.dispose();
		meshFromSDF.geometry = geometry;

	} else { // inits meshFromSDF : THREE.Mesh

		meshFromSDF = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial() );
		scene.add( meshFromSDF );

		const scale = Math.min( window.innerWidth, window.innerHeight ) / 2 * 0.66;
		meshFromSDF.scale.set( scale, scale, scale );

		setMaterial();

	}

	settings.vertexCount = geometry.attributes.position.count;
    
}

function setMaterial() {

	meshFromSDF.material.dispose();

	if ( settings.material == 'depth' ) {

		meshFromSDF.material = new THREE.MeshDepthMaterial();

	} else if ( settings.material == 'normal' ) {

		meshFromSDF.material = new THREE.MeshNormalMaterial();

	}

	meshFromSDF.material.wireframe = settings.wireframe;

}

function onWindowResize() {

	const w = window.innerWidth;
	const h = window.innerHeight;

	renderer.setSize( w, h );

	camera.left = w / - 2;
	camera.right = w / 2;
	camera.top = h / 2;
	camera.bottom = h / - 2;

	camera.updateProjectionMatrix();

}

function render() {

	renderer.render( scene, camera );

}

function animate() {

	controls.update();

	if ( settings.autoRotate ) {

		meshFromSDF.rotation.y += Math.PI * 0.05 * clock.getDelta();

	}

	render();

}

</script>

</html>
4 Likes