2
0

webgl_marchingcubes.html 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title>three.js webgl - marching cubes</title>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
  7. <link type="text/css" rel="stylesheet" href="main.css">
  8. </head>
  9. <body>
  10. <div id="container"></div>
  11. <div id="info">
  12. <a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> -
  13. marching cubes<br/>
  14. based on greggman's <a href="https://webglsamples.org/blob/blob.html">blob</a>, original code by Henrik Rydgård
  15. </div>
  16. <script type="module">
  17. import * as THREE from '../build/three.module.js';
  18. import Stats from './jsm/libs/stats.module.js';
  19. import { GUI } from './jsm/libs/dat.gui.module.js';
  20. import { OrbitControls } from './jsm/controls/OrbitControls.js';
  21. import { MarchingCubes } from './jsm/objects/MarchingCubes.js';
  22. import { ToonShader1, ToonShader2, ToonShaderHatching, ToonShaderDotted } from './jsm/shaders/ToonShader.js';
  23. let container, stats;
  24. let camera, scene, renderer;
  25. let materials, current_material;
  26. let light, pointLight, ambientLight;
  27. let effect, resolution;
  28. let effectController;
  29. let time = 0;
  30. const clock = new THREE.Clock();
  31. init();
  32. animate();
  33. function init() {
  34. container = document.getElementById( 'container' );
  35. // CAMERA
  36. camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
  37. camera.position.set( - 500, 500, 1500 );
  38. // SCENE
  39. scene = new THREE.Scene();
  40. scene.background = new THREE.Color( 0x050505 );
  41. // LIGHTS
  42. light = new THREE.DirectionalLight( 0xffffff );
  43. light.position.set( 0.5, 0.5, 1 );
  44. scene.add( light );
  45. pointLight = new THREE.PointLight( 0xff3300 );
  46. pointLight.position.set( 0, 0, 100 );
  47. scene.add( pointLight );
  48. ambientLight = new THREE.AmbientLight( 0x080808 );
  49. scene.add( ambientLight );
  50. // MATERIALS
  51. materials = generateMaterials();
  52. current_material = "shiny";
  53. // MARCHING CUBES
  54. resolution = 28;
  55. effect = new MarchingCubes( resolution, materials[ current_material ].m, true, true );
  56. effect.position.set( 0, 0, 0 );
  57. effect.scale.set( 700, 700, 700 );
  58. effect.enableUvs = false;
  59. effect.enableColors = false;
  60. scene.add( effect );
  61. // RENDERER
  62. renderer = new THREE.WebGLRenderer();
  63. renderer.outputEncoding = THREE.sRGBEncoding;
  64. renderer.setPixelRatio( window.devicePixelRatio );
  65. renderer.setSize( window.innerWidth, window.innerHeight );
  66. container.appendChild( renderer.domElement );
  67. // CONTROLS
  68. const controls = new OrbitControls( camera, renderer.domElement );
  69. controls.minDistance = 500;
  70. controls.maxDistance = 5000;
  71. // STATS
  72. stats = new Stats();
  73. container.appendChild( stats.dom );
  74. // GUI
  75. setupGui();
  76. // EVENTS
  77. window.addEventListener( 'resize', onWindowResize );
  78. }
  79. //
  80. function onWindowResize() {
  81. camera.aspect = window.innerWidth / window.innerHeight;
  82. camera.updateProjectionMatrix();
  83. renderer.setSize( window.innerWidth, window.innerHeight );
  84. }
  85. function generateMaterials() {
  86. // environment map
  87. const path = "textures/cube/SwedishRoyalCastle/";
  88. const format = '.jpg';
  89. const urls = [
  90. path + 'px' + format, path + 'nx' + format,
  91. path + 'py' + format, path + 'ny' + format,
  92. path + 'pz' + format, path + 'nz' + format
  93. ];
  94. const cubeTextureLoader = new THREE.CubeTextureLoader();
  95. const reflectionCube = cubeTextureLoader.load( urls );
  96. const refractionCube = cubeTextureLoader.load( urls );
  97. refractionCube.mapping = THREE.CubeRefractionMapping;
  98. // toons
  99. const toonMaterial1 = createShaderMaterial( ToonShader1, light, ambientLight );
  100. const toonMaterial2 = createShaderMaterial( ToonShader2, light, ambientLight );
  101. const hatchingMaterial = createShaderMaterial( ToonShaderHatching, light, ambientLight );
  102. const dottedMaterial = createShaderMaterial( ToonShaderDotted, light, ambientLight );
  103. const texture = new THREE.TextureLoader().load( "textures/uv_grid_opengl.jpg" );
  104. texture.wrapS = THREE.RepeatWrapping;
  105. texture.wrapT = THREE.RepeatWrapping;
  106. const materials = {
  107. "chrome": {
  108. m: new THREE.MeshLambertMaterial( { color: 0xffffff, envMap: reflectionCube } ),
  109. h: 0, s: 0, l: 1
  110. },
  111. "liquid": {
  112. m: new THREE.MeshLambertMaterial( { color: 0xffffff, envMap: refractionCube, refractionRatio: 0.85 } ),
  113. h: 0, s: 0, l: 1
  114. },
  115. "shiny": {
  116. m: new THREE.MeshStandardMaterial( { color: 0x550000, envMap: reflectionCube, roughness: 0.1, metalness: 1.0 } ),
  117. h: 0, s: 0.8, l: 0.2
  118. },
  119. "matte": {
  120. m: new THREE.MeshPhongMaterial( { color: 0x000000, specular: 0x111111, shininess: 1 } ),
  121. h: 0, s: 0, l: 1
  122. },
  123. "flat": {
  124. m: new THREE.MeshLambertMaterial( { color: 0x000000, flatShading: true } ),
  125. h: 0, s: 0, l: 1
  126. },
  127. "textured": {
  128. m: new THREE.MeshPhongMaterial( { color: 0xffffff, specular: 0x111111, shininess: 1, map: texture } ),
  129. h: 0, s: 0, l: 1
  130. },
  131. "colors": {
  132. m: new THREE.MeshPhongMaterial( { color: 0xffffff, specular: 0xffffff, shininess: 2, vertexColors: true } ),
  133. h: 0, s: 0, l: 1
  134. },
  135. "multiColors": {
  136. m: new THREE.MeshPhongMaterial( { shininess: 2, vertexColors: true } ),
  137. h: 0, s: 0, l: 1
  138. },
  139. "plastic": {
  140. m: new THREE.MeshPhongMaterial( { color: 0x000000, specular: 0x888888, shininess: 250 } ),
  141. h: 0.6, s: 0.8, l: 0.1
  142. },
  143. "toon1": {
  144. m: toonMaterial1,
  145. h: 0.2, s: 1, l: 0.75
  146. },
  147. "toon2": {
  148. m: toonMaterial2,
  149. h: 0.4, s: 1, l: 0.75
  150. },
  151. "hatching": {
  152. m: hatchingMaterial,
  153. h: 0.2, s: 1, l: 0.9
  154. },
  155. "dotted": {
  156. m: dottedMaterial,
  157. h: 0.2, s: 1, l: 0.9
  158. }
  159. };
  160. return materials;
  161. }
  162. function createShaderMaterial( shader, light, ambientLight ) {
  163. const u = THREE.UniformsUtils.clone( shader.uniforms );
  164. const vs = shader.vertexShader;
  165. const fs = shader.fragmentShader;
  166. const material = new THREE.ShaderMaterial( { uniforms: u, vertexShader: vs, fragmentShader: fs } );
  167. material.uniforms[ "uDirLightPos" ].value = light.position;
  168. material.uniforms[ "uDirLightColor" ].value = light.color;
  169. material.uniforms[ "uAmbientLightColor" ].value = ambientLight.color;
  170. return material;
  171. }
  172. //
  173. function setupGui() {
  174. const createHandler = function ( id ) {
  175. return function () {
  176. const mat_old = materials[ current_material ];
  177. mat_old.h = m_h.getValue();
  178. mat_old.s = m_s.getValue();
  179. mat_old.l = m_l.getValue();
  180. current_material = id;
  181. const mat = materials[ id ];
  182. effect.material = mat.m;
  183. m_h.setValue( mat.h );
  184. m_s.setValue( mat.s );
  185. m_l.setValue( mat.l );
  186. effect.enableUvs = ( current_material === "textured" ) ? true : false;
  187. effect.enableColors = ( current_material === "colors" || current_material === "multiColors" ) ? true : false;
  188. };
  189. };
  190. effectController = {
  191. material: "shiny",
  192. speed: 1.0,
  193. numBlobs: 10,
  194. resolution: 28,
  195. isolation: 80,
  196. floor: true,
  197. wallx: false,
  198. wallz: false,
  199. hue: 0.0,
  200. saturation: 0.8,
  201. lightness: 0.1,
  202. lhue: 0.04,
  203. lsaturation: 1.0,
  204. llightness: 0.5,
  205. lx: 0.5,
  206. ly: 0.5,
  207. lz: 1.0,
  208. dummy: function () {}
  209. };
  210. let h;
  211. const gui = new GUI();
  212. // material (type)
  213. h = gui.addFolder( "Materials" );
  214. for ( const m in materials ) {
  215. effectController[ m ] = createHandler( m );
  216. h.add( effectController, m ).name( m );
  217. }
  218. // material (color)
  219. h = gui.addFolder( "Material color" );
  220. const m_h = h.add( effectController, "hue", 0.0, 1.0, 0.025 );
  221. const m_s = h.add( effectController, "saturation", 0.0, 1.0, 0.025 );
  222. const m_l = h.add( effectController, "lightness", 0.0, 1.0, 0.025 );
  223. // light (point)
  224. h = gui.addFolder( "Point light color" );
  225. h.add( effectController, "lhue", 0.0, 1.0, 0.025 ).name( "hue" );
  226. h.add( effectController, "lsaturation", 0.0, 1.0, 0.025 ).name( "saturation" );
  227. h.add( effectController, "llightness", 0.0, 1.0, 0.025 ).name( "lightness" );
  228. // light (directional)
  229. h = gui.addFolder( "Directional light orientation" );
  230. h.add( effectController, "lx", - 1.0, 1.0, 0.025 ).name( "x" );
  231. h.add( effectController, "ly", - 1.0, 1.0, 0.025 ).name( "y" );
  232. h.add( effectController, "lz", - 1.0, 1.0, 0.025 ).name( "z" );
  233. // simulation
  234. h = gui.addFolder( "Simulation" );
  235. h.add( effectController, "speed", 0.1, 8.0, 0.05 );
  236. h.add( effectController, "numBlobs", 1, 50, 1 );
  237. h.add( effectController, "resolution", 14, 100, 1 );
  238. h.add( effectController, "isolation", 10, 300, 1 );
  239. h.add( effectController, "floor" );
  240. h.add( effectController, "wallx" );
  241. h.add( effectController, "wallz" );
  242. }
  243. // this controls content of marching cubes voxel field
  244. function updateCubes( object, time, numblobs, floor, wallx, wallz ) {
  245. object.reset();
  246. // fill the field with some metaballs
  247. const rainbow = [
  248. new THREE.Color( 0xff0000 ),
  249. new THREE.Color( 0xff7f00 ),
  250. new THREE.Color( 0xffff00 ),
  251. new THREE.Color( 0x00ff00 ),
  252. new THREE.Color( 0x0000ff ),
  253. new THREE.Color( 0x4b0082 ),
  254. new THREE.Color( 0x9400d3 )
  255. ];
  256. const subtract = 12;
  257. const strength = 1.2 / ( ( Math.sqrt( numblobs ) - 1 ) / 4 + 1 );
  258. for ( let i = 0; i < numblobs; i ++ ) {
  259. const ballx = Math.sin( i + 1.26 * time * ( 1.03 + 0.5 * Math.cos( 0.21 * i ) ) ) * 0.27 + 0.5;
  260. const bally = Math.abs( Math.cos( i + 1.12 * time * Math.cos( 1.22 + 0.1424 * i ) ) ) * 0.77; // dip into the floor
  261. const ballz = Math.cos( i + 1.32 * time * 0.1 * Math.sin( ( 0.92 + 0.53 * i ) ) ) * 0.27 + 0.5;
  262. if ( current_material === 'multiColors' ) {
  263. object.addBall( ballx, bally, ballz, strength, subtract, rainbow[ i % 7 ] );
  264. } else {
  265. object.addBall( ballx, bally, ballz, strength, subtract );
  266. }
  267. }
  268. if ( floor ) object.addPlaneY( 2, 12 );
  269. if ( wallz ) object.addPlaneZ( 2, 12 );
  270. if ( wallx ) object.addPlaneX( 2, 12 );
  271. }
  272. //
  273. function animate() {
  274. requestAnimationFrame( animate );
  275. render();
  276. stats.update();
  277. }
  278. function render() {
  279. const delta = clock.getDelta();
  280. time += delta * effectController.speed * 0.5;
  281. // marching cubes
  282. if ( effectController.resolution !== resolution ) {
  283. resolution = effectController.resolution;
  284. effect.init( Math.floor( resolution ) );
  285. }
  286. if ( effectController.isolation !== effect.isolation ) {
  287. effect.isolation = effectController.isolation;
  288. }
  289. updateCubes( effect, time, effectController.numBlobs, effectController.floor, effectController.wallx, effectController.wallz );
  290. // materials
  291. if ( effect.material instanceof THREE.ShaderMaterial ) {
  292. effect.material.uniforms[ "uBaseColor" ].value.setHSL( effectController.hue, effectController.saturation, effectController.lightness );
  293. } else {
  294. effect.material.color.setHSL( effectController.hue, effectController.saturation, effectController.lightness );
  295. }
  296. // lights
  297. light.position.set( effectController.lx, effectController.ly, effectController.lz );
  298. light.position.normalize();
  299. pointLight.color.setHSL( effectController.lhue, effectController.lsaturation, effectController.llightness );
  300. // render
  301. renderer.render( scene, camera );
  302. }
  303. </script>
  304. </body>
  305. </html>