UNPKG

marching

Version:

Marching.js is a JavaScript library that compiles GLSL ray marchers.

290 lines (257 loc) 7.83 kB
const getFog = require( './fog.js' ) const vignette = require( './vignette.js' ) const { param_wrap, MaterialID } = require( './utils.js' ) const __lighting = require( './lighting.js' ) const { Var, float_var_gen, vec2_var_gen, vec3_var_gen, vec4_var_gen, int_var_gen, VarAlloc } = require('./var.js') const getScene = function( SDF ) { Scene = function( objs, canvas, steps=100, minDistance=.001, maxDistance=40, size=2, shouldAnimate=false ) { const scene = Object.create( Scene.prototype ) MaterialID.clear() SDF.lighting.lights = [] scene.__prerender = objs if( objs.length > 1 ) { // reduce objects to nested Unions scene.__prerender = objs.reduce( ( current, next ) => SDF.Union( current, next ) ) } Object.assign( scene, { objs, canvas, postprocessing:[], __shadow:8, __followLight:null, __postprocessingFlag:false, __steps:null, __thresold:null, __farPlane:null, __resolution:null }) scene.useQuality = true scene.useVoxels = false SDF.__scene = scene return scene } Scene.prototype = { animate( v ) { this.__animate = v; return this }, setdim( w, h ) { this.width = w this.height = h this.donotuseresolution = true return this }, resolution( v ) { this.width = Math.floor( this.canvas.width = window.innerWidth * v ) this.height = Math.floor( this.canvas.height = window.innerHeight * v ) this.__resolution = v; return this }, voxel( v = .1 ) { this.useVoxels = true this.__voxelSize = v return this }, threshold( v ) { this.__threshold = v; return this }, steps( v ) { this.__steps = v; return this }, farPlane( v ) { this.__farPlane = v; return this }, camera( x=0, y=0, z=5, speed=1 ) { SDF.camera.__camera.position[0] = x SDF.camera.__camera.position[1] = y SDF.camera.__camera.position[2] = z SDF.camera.__camera.rotationSpeed = speed * .01 SDF.camera.__camera.positionSpeed = speed * -.25 SDF.camera.update() return this }, shadow( k=0 ) { this.__shadow = k; return this; }, quality( quality=10 ) { if( this.__thresold === null ) this.threshold( .1 / (quality * quality * quality ) ) if( this.__steps === null ) this.steps( quality * 20 ) if( this.__farPlane === null ) this.farPlane( quality * 5 ) if( this.donotuseresolution === undefined && this.__resolultion === null ) this.resolution( Math.min( .2 * quality, 2 ) ) return this }, follow( light, distance=3 ) { this.__followLight = light SDF.camera.onmove = function( camera ) { const offset = SDF.camera.offset() light.pos.x = SDF.camera.__camera.position[0] - offset[0] light.pos.y = SDF.camera.__camera.position[1] - offset[1] light.pos.z = SDF.camera.__camera.position[2] - offset[2] light.dirty = true } SDF.lighting.lights = [light] return this }, light( ...lights ) { SDF.lighting.lights = SDF.lighting.lights.concat( lights ) if( this.__followLight !== null ) SDF.lighting.lights.push( this.__followLight ) return this }, fog: getFog( Scene, SDF ), vignette: vignette( Scene, SDF ), background: require( './background.js' )( Scene, SDF ), applyPreset( presetName ) { const preset = this.presets[ presetName ] if( preset.farPlane !== undefined ) { this.farPlane( this.__farPlane || preset.farPlane ) }else{ this.__farPlane = 0 } this.steps( this.__steps || preset.steps ) if( this.donotuseresolution === undefined ) this.resolution( this.__resolution || preset.resolution ) this.threshold( this.__threshold || preset.threshold || .001 ) return preset.animated }, post( ...fx ) { this.__postprocessingFlag = true SDF.fx.clear() SDF.fx.post( ...fx ) return this }, render( quality=10, animate=false, useQuality=true ) { // adds default if none has been specified this.background() if( this.__postprocessingFlag === false ) { SDF.fx.clear() } if( typeof quality === 'string' ) { animate = this.applyPreset( quality ) }else if( this.useQuality === true ) { this.quality( quality ) } this.animate( animate ) SDF.distanceOps.__clear() SDF.alterations.__clear() SDF.textures.clear() const geometries = SDF.primitives.emit_geometries() let [ variablesDeclaration, sceneRendering, postprocessing ] = SDF.generateSDF( this ) const lighting = SDF.lighting.gen( this.__shadow, geometries ) variablesDeclaration += SDF.materials.emit_decl() variablesDeclaration += SDF.textures.emit_decl() variablesDeclaration += SDF.lighting.emit_decl() variablesDeclaration += this.__background.emit_decl() this.fs = SDF.renderFragmentShader( variablesDeclaration, sceneRendering.out, sceneRendering.preface, SDF.requiredGeometries.join('\n') + SDF.requiredOps.join('\n'), lighting, postprocessing, this.__steps, this.__threshold, this.__farPlane.toFixed(1), SDF.distanceOps.__getGLSL() + SDF.alterations.__getGLSL(), this.useVoxels ? this.__voxelSize : 0 ) if( this.width === undefined ) this.width = window.innerWidth if( this.height === undefined ) this.height = window.innerHeight SDF.start( this.fs, this.width, this.height, this.__animate ) //SDF.materials.materials.length = 0 this.useQuality = true this.__postprocessingFlag = false this.__threshold = null this.__farPlane = null this.__steps = null this.__resolution = null return this }, presets: { 'fractal.close': { farPlane:1, resolution:1, steps:150, animated:true, threshold:.000125 }, 'fractal.kindaclose': { farPlane:2, resolution:1, steps:250, animated:true, threshold:.000125/2 }, 'fractal.med': { farPlane:5, resolution:.75, steps:80, animated:true, threshold:.001, }, 'fractal.low': { farPlane:3.0, resolution:.5, animated:true, steps:50, threshold:.005, }, 'fractal.high': { farPlane:10, resolution:1, animated:true, steps:100, threshold:.001, }, 'repeat.low': { farPlane:25, resolution:.5, animated:true, steps:50 }, 'repeat.med': { farPlane:35, resolution:1, animated:true, steps:75 }, 'repeat.high': { farPlane:40, resolution:1, animated:true, steps:100 }, 'voxel.high': { resolution:1, animated:true, steps:30 }, 'voxel.med': { resolution:1, animated:true, steps:20 }, 'voxel.low': { resolution:.5, animated:true, steps:10 }, low: { threshold:.05, steps:45, farPlane:12, resolution:.4, animated:true }, medium: { threshold:.01, steps:80, farPlane:18, resolution:.5, animated:true }, med: { threshold:.01, steps:80, farPlane:18, resolution:.5, animated:true }, high: { threshold:.005, steps:90, farPlane:20, resolution:1, animated:true } }, } return Scene } module.exports = getScene