-
-
Notifications
You must be signed in to change notification settings - Fork 35.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
SDFGeometryGenerator
addon. (#26837)
* add SDFGeometry ammend * remove comment * Update webgl_geometry_sdf.jpg * pass that check.... * PR fixes ~ Code scanning checks ~ Update class name to SDFGeometryGenerator ~ Change class design ~ Remove confirm dialog ~ Standardize example structure ~ Remove shader editor * 📺🔫 * Update SDFGeometryGenerator.js - Remove extends Buffergeometry - Replace logs by error - Added option for more res if device supports 32k texture - Remove unused properties * semi missing * move surface nets to jsm/libs * Update SDFGeometryGenerator.js Improve code style. * Update SDFGeometryGenerator.js Remove `maxTexSize` property. * Update SDFGeometryGenerator.html Improve doc page. * Update webgl_geometry_sdf.html * Update to example Remove large glsl examples Take screenshot * Update SDFGeometryGenerator.js More clean up. * Update webgl_geometry_sdf.html Clean up. * Update webgl_geometry_sdf.html Use `dispose()` methods. * Update webgl_geometry_sdf.html Avoid using textarea. * Update webgl_geometry_sdf.html Improve code style. * Update SDFGeometryGenerator.js Fix memory leak. * Update SDFGeometryGenerator.js Simplify usage of `setIndex()`. * Remove w from position buffer --------- Co-authored-by: Michael Herzog <michael.herzog@human-interactive.org>
- Loading branch information
1 parent
cb42582
commit eb073dd
Showing
7 changed files
with
632 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="utf-8" /> | ||
<base href="../../../" /> | ||
<script src="page.js"></script> | ||
<link type="text/css" rel="stylesheet" href="page.css" /> | ||
</head> | ||
<body> | ||
|
||
<h1>[name]</h1> | ||
|
||
<p class="desc"> | ||
[name] generates instances of [page:BufferGeometry] from a Signed Distance Function</br> | ||
Uses <a href="https://www.npmjs.com/package/isosurface" target="_blank" > Mikola Lysenko's Isosurface</a> | ||
</p> | ||
|
||
<h2>Import</h2> | ||
|
||
<p> | ||
[name] is an add-on, and must be imported explicitly. | ||
See [link:#manual/introduction/Installation Installation / Addons]. | ||
</p> | ||
|
||
<code> | ||
import { SDFGeometryGenerator } from 'three/addons/geometries/SDFGeometryGenerator.js'; | ||
</code> | ||
|
||
<h2>Code Example</h2> | ||
|
||
<code> | ||
const generator = new SDFGeometryGenerator( renderer ); | ||
const sdf = 'float dist( vec3 p ){ return length(p) - 0.5; }' // glsl | ||
const geometry = generator.generate( 64, sdf, 1 ); // ~> THREE.BufferGeometry | ||
</code> | ||
|
||
<h2>Examples</h2> | ||
|
||
<p>[example:webgl_geometry_sdf geometry / sdf ]</p> | ||
|
||
<h2>Constructor</h2> | ||
|
||
<h3>[name]( [param:WebGLRenderer renderer] )</h3> | ||
|
||
<p> | ||
[page:WebGLRenderer renderer] -- The renderer used to render the scene. <br /> | ||
</p> | ||
|
||
<h2>Methods</h2> | ||
|
||
<h3>[method:BufferGeometry generate]( [param:Int resolution], [param:String distanceField], [param:Int bounds] )</h3> | ||
|
||
<p> | ||
<b>resolution</b> - Int [ mandatory ] Amount of 'voxels' used for triangulation. Must be power of 2. <br/>Gets heavy after 256, most machines won't be able to process over 512. Defaults to 64. | ||
</p> | ||
<p> | ||
<b>distanceField</b> - String [ mandatory ] String with glsl distance function. Name of function must be 'dist', with a vec3 argument. ( see code above ). Defaults to a sphere distance. | ||
</p> | ||
<p> | ||
<b>bounds</b> - Int [ optional ] Bounds in which signed distance field will be evaluated. Defaults to 1. | ||
</p> | ||
|
||
|
||
<h2>Source</h2> | ||
|
||
<p> | ||
[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/geometries/SDFGeometry.js examples/jsm/geometries/SDFGeometryGenerator.js] | ||
</p> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
/** | ||
* @author santiago / @glitch_life | ||
* wrapper of https://www.npmjs.com/package/isosurface by https://github.com/mikolalysenko | ||
* | ||
* Returns BufferGeometry from SDF | ||
*/ | ||
|
||
import { | ||
BufferAttribute, | ||
BufferGeometry, | ||
FloatType, | ||
Mesh, | ||
OrthographicCamera, | ||
PlaneGeometry, | ||
Scene, | ||
ShaderMaterial, | ||
Vector2, | ||
WebGLRenderTarget | ||
} from 'three'; | ||
|
||
import { surfaceNet } from './../libs/surfaceNet.js'; | ||
|
||
class SDFGeometryGenerator { | ||
|
||
constructor( renderer ) { | ||
|
||
this.renderer = renderer; | ||
|
||
} | ||
|
||
generate( res = 64, distFunc = 'float dist( vec3 p ){ return length(p) - 0.5; }', bounds = 1 ) { | ||
|
||
let w, h; | ||
if ( res == 8 ) [ w, h ] = [ 32, 16 ]; | ||
else if ( res == 16 ) [ w, h ] = [ 64, 64 ]; | ||
else if ( res == 32 ) [ w, h ] = [ 256, 128 ]; | ||
else if ( res == 64 ) [ w, h ] = [ 512, 512 ]; | ||
else if ( res == 128 ) [ w, h ] = [ 2048, 1024 ]; | ||
else if ( res == 256 ) [ w, h ] = [ 4096, 4096 ]; | ||
else if ( res == 512 ) [ w, h ] = [ 16384, 8096 ]; | ||
else if ( res == 1024 ) [ w, h ] = [ 32768, 32768 ]; | ||
else throw new Error( 'THREE.SDFGeometryGenerator: Resolution must be in range 8 < res < 1024 and must be ^2' ); | ||
|
||
const maxTexSize = this.renderer.capabilities.maxTextureSize; | ||
|
||
if ( w > maxTexSize || h > maxTexSize ) throw new Error( 'THREE.SDFGeometryGenerator: Your device does not support this resolution ( ' + res + ' ), decrease [res] param.' ); | ||
|
||
const [ tilesX, tilesY ] = [ ( w / res ), ( h / res ) ]; | ||
|
||
const sdfCompute = ` | ||
varying vec2 vUv; | ||
uniform float tileNum; | ||
uniform float bounds; | ||
[#dist#] | ||
void main() { gl_FragColor=vec4( ( dist( vec3( vUv, tileNum ) * 2.0 * bounds - vec3( bounds ) ) < 0.00001 ) ? 1.0 : 0.0 ); } | ||
`; | ||
|
||
const sdfRT = this.computeSDF( w, h, tilesX, tilesY, bounds, sdfCompute.replace( '[#dist#]', distFunc ) ); | ||
|
||
const read = new Float32Array( w * h * 4 ); | ||
this.renderer.readRenderTargetPixels( sdfRT, 0, 0, w, h, read ); | ||
sdfRT.dispose(); | ||
|
||
// | ||
|
||
const mesh = surfaceNet( [ res, res, res ], ( x, y, z ) => { | ||
|
||
x = ( x + bounds ) * ( res / ( bounds * 2 ) ); | ||
y = ( y + bounds ) * ( res / ( bounds * 2 ) ); | ||
z = ( z + bounds ) * ( res / ( bounds * 2 ) ); | ||
let p = ( x + ( z % tilesX ) * res ) + y * w + ( Math.floor( z / tilesX ) * res * w ); | ||
p *= 4; | ||
return ( read[ p + 3 ] > 0 ) ? - 0.000000001 : 1; | ||
|
||
}, [[ - bounds, - bounds, - bounds ], [ bounds, bounds, bounds ]] ); | ||
|
||
const ps = [], ids = []; | ||
const geometry = new BufferGeometry(); | ||
mesh.positions.forEach( p => { | ||
|
||
ps.push( p[ 0 ], p[ 1 ], p[ 2 ] ); | ||
|
||
} ); | ||
mesh.cells.forEach( p => ids.push( p[ 0 ], p[ 1 ], p[ 2 ] ) ); | ||
geometry.setAttribute( 'position', new BufferAttribute( new Float32Array( ps ), 3 ) ); | ||
geometry.setIndex( ids ); | ||
|
||
return geometry; | ||
|
||
} | ||
|
||
computeSDF( width, height, tilesX, tilesY, bounds, shader ) { | ||
|
||
const rt = new WebGLRenderTarget( width, height, { type: FloatType } ); | ||
const scn = new Scene(); | ||
const cam = new OrthographicCamera(); | ||
const tiles = tilesX * tilesY; | ||
let currentTile = 0; | ||
|
||
Object.assign( cam, { left: width / - 2, right: width / 2, top: height / 2, bottom: height / - 2 } ).updateProjectionMatrix(); | ||
cam.position.z = 2; | ||
|
||
const tileSize = width / tilesX; | ||
const geometry = new PlaneGeometry( tileSize, tileSize ); | ||
|
||
while ( currentTile ++ < tiles ) { | ||
|
||
const c = currentTile - 1; | ||
const [ px, py ] = [ ( tileSize ) / 2 + ( c % tilesX ) * ( tileSize ) - width / 2, ( tileSize ) / 2 + Math.floor( c / tilesX ) * ( tileSize ) - height / 2 ]; | ||
const compPlane = new Mesh( geometry, new ShaderMaterial( { | ||
uniforms: { | ||
res: { value: new Vector2( width, height ) }, | ||
tileNum: { value: c / ( tilesX * tilesY - 1 ) }, | ||
bounds: { value: bounds } | ||
}, | ||
vertexShader: 'varying vec2 vUv;void main(){vUv=uv;gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);}', | ||
fragmentShader: shader | ||
} ) ); | ||
compPlane.position.set( px, py, 0 ); | ||
scn.add( compPlane ); | ||
|
||
} | ||
|
||
this.renderer.setRenderTarget( rt ); | ||
this.renderer.render( scn, cam ); | ||
this.renderer.setRenderTarget( null ); | ||
|
||
// | ||
|
||
geometry.dispose(); | ||
|
||
scn.traverse( function ( object ) { | ||
|
||
if ( object.material !== undefined ) object.material.dispose(); | ||
|
||
} ); | ||
|
||
return rt; | ||
|
||
} | ||
|
||
} | ||
|
||
export { SDFGeometryGenerator }; |
Oops, something went wrong.