webgl_marchingcubes.html 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  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. var container, stats;
  24. var camera, scene, renderer;
  25. var materials, current_material;
  26. var light, pointLight, ambientLight;
  27. var effect, resolution;
  28. var effectController;
  29. var time = 0;
  30. var 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. renderer.domElement.style.position = "absolute";
  67. renderer.domElement.style.top = "0px";
  68. renderer.domElement.style.left = "0px";
  69. container.appendChild( renderer.domElement );
  70. // CONTROLS
  71. var controls = new OrbitControls( camera, renderer.domElement );
  72. // STATS
  73. stats = new Stats();
  74. container.appendChild( stats.dom );
  75. // GUI
  76. setupGui();
  77. // EVENTS
  78. window.addEventListener( 'resize', onWindowResize, false );
  79. }
  80. //
  81. function onWindowResize() {
  82. camera.aspect = window.innerWidth / window.innerHeight;
  83. camera.updateProjectionMatrix();
  84. renderer.setSize( window.innerWidth, window.innerHeight );
  85. }
  86. function generateMaterials() {
  87. // environment map
  88. var path = "textures/cube/SwedishRoyalCastle/";
  89. var format = '.jpg';
  90. var urls = [
  91. path + 'px' + format, path + 'nx' + format,
  92. path + 'py' + format, path + 'ny' + format,
  93. path + 'pz' + format, path + 'nz' + format
  94. ];
  95. var cubeTextureLoader = new THREE.CubeTextureLoader();
  96. var reflectionCube = cubeTextureLoader.load( urls );
  97. var refractionCube = cubeTextureLoader.load( urls );
  98. refractionCube.mapping = THREE.CubeRefractionMapping;
  99. // toons
  100. var toonMaterial1 = createShaderMaterial( ToonShader1, light, ambientLight );
  101. var toonMaterial2 = createShaderMaterial( ToonShader2, light, ambientLight );
  102. var hatchingMaterial = createShaderMaterial( ToonShaderHatching, light, ambientLight );
  103. var dottedMaterial = createShaderMaterial( ToonShaderDotted, light, ambientLight );
  104. var texture = new THREE.TextureLoader().load( "textures/uv_grid_opengl.jpg" );
  105. texture.wrapS = THREE.RepeatWrapping;
  106. texture.wrapT = THREE.RepeatWrapping;
  107. var materials = {
  108. "chrome": {
  109. m: new THREE.MeshLambertMaterial( { color: 0xffffff, envMap: reflectionCube } ),
  110. h: 0, s: 0, l: 1
  111. },
  112. "liquid": {
  113. m: new THREE.MeshLambertMaterial( { color: 0xffffff, envMap: refractionCube, refractionRatio: 0.85 } ),
  114. h: 0, s: 0, l: 1
  115. },
  116. "shiny": {
  117. m: new THREE.MeshStandardMaterial( { color: 0x550000, envMap: reflectionCube, roughness: 0.1, metalness: 1.0 } ),
  118. h: 0, s: 0.8, l: 0.2
  119. },
  120. "matte": {
  121. m: new THREE.MeshPhongMaterial( { color: 0x000000, specular: 0x111111, shininess: 1 } ),
  122. h: 0, s: 0, l: 1
  123. },
  124. "flat": {
  125. m: new THREE.MeshLambertMaterial( { color: 0x000000, flatShading: true } ),
  126. h: 0, s: 0, l: 1
  127. },
  128. "textured": {
  129. m: new THREE.MeshPhongMaterial( { color: 0xffffff, specular: 0x111111, shininess: 1, map: texture } ),
  130. h: 0, s: 0, l: 1
  131. },
  132. "colors": {
  133. m: new THREE.MeshPhongMaterial( { color: 0xffffff, specular: 0xffffff, shininess: 2, vertexColors: THREE.VertexColors } ),
  134. h: 0, s: 0, l: 1
  135. },
  136. "multiColors": {
  137. m: new THREE.MeshPhongMaterial( { shininess: 2, vertexColors: THREE.VertexColors } ),
  138. h: 0, s: 0, l: 1
  139. },
  140. "plastic": {
  141. m: new THREE.MeshPhongMaterial( { color: 0x000000, specular: 0x888888, shininess: 250 } ),
  142. h: 0.6, s: 0.8, l: 0.1
  143. },
  144. "toon1": {
  145. m: toonMaterial1,
  146. h: 0.2, s: 1, l: 0.75
  147. },
  148. "toon2": {
  149. m: toonMaterial2,
  150. h: 0.4, s: 1, l: 0.75
  151. },
  152. "hatching": {
  153. m: hatchingMaterial,
  154. h: 0.2, s: 1, l: 0.9
  155. },
  156. "dotted": {
  157. m: dottedMaterial,
  158. h: 0.2, s: 1, l: 0.9
  159. }
  160. };
  161. return materials;
  162. }
  163. function createShaderMaterial( shader, light, ambientLight ) {
  164. var u = THREE.UniformsUtils.clone( shader.uniforms );
  165. var vs = shader.vertexShader;
  166. var fs = shader.fragmentShader;
  167. var material = new THREE.ShaderMaterial( { uniforms: u, vertexShader: vs, fragmentShader: fs } );
  168. material.uniforms[ "uDirLightPos" ].value = light.position;
  169. material.uniforms[ "uDirLightColor" ].value = light.color;
  170. material.uniforms[ "uAmbientLightColor" ].value = ambientLight.color;
  171. return material;
  172. }
  173. //
  174. function setupGui() {
  175. var createHandler = function ( id ) {
  176. return function () {
  177. var mat_old = materials[ current_material ];
  178. mat_old.h = m_h.getValue();
  179. mat_old.s = m_s.getValue();
  180. mat_old.l = m_l.getValue();
  181. current_material = id;
  182. var mat = materials[ id ];
  183. effect.material = mat.m;
  184. m_h.setValue( mat.h );
  185. m_s.setValue( mat.s );
  186. m_l.setValue( mat.l );
  187. effect.enableUvs = ( current_material === "textured" ) ? true : false;
  188. effect.enableColors = ( current_material === "colors" || current_material === "multiColors" ) ? true : false;
  189. };
  190. };
  191. effectController = {
  192. material: "shiny",
  193. speed: 1.0,
  194. numBlobs: 10,
  195. resolution: 28,
  196. isolation: 80,
  197. floor: true,
  198. wallx: false,
  199. wallz: false,
  200. hue: 0.0,
  201. saturation: 0.8,
  202. lightness: 0.1,
  203. lhue: 0.04,
  204. lsaturation: 1.0,
  205. llightness: 0.5,
  206. lx: 0.5,
  207. ly: 0.5,
  208. lz: 1.0,
  209. dummy: function () {}
  210. };
  211. var h, m_h, m_s, m_l;
  212. var gui = new GUI();
  213. // material (type)
  214. h = gui.addFolder( "Materials" );
  215. for ( var m in materials ) {
  216. effectController[ m ] = createHandler( m );
  217. h.add( effectController, m ).name( m );
  218. }
  219. // material (color)
  220. h = gui.addFolder( "Material color" );
  221. m_h = h.add( effectController, "hue", 0.0, 1.0, 0.025 );
  222. m_s = h.add( effectController, "saturation", 0.0, 1.0, 0.025 );
  223. m_l = h.add( effectController, "lightness", 0.0, 1.0, 0.025 );
  224. // light (point)
  225. h = gui.addFolder( "Point light color" );
  226. h.add( effectController, "lhue", 0.0, 1.0, 0.025 ).name( "hue" );
  227. h.add( effectController, "lsaturation", 0.0, 1.0, 0.025 ).name( "saturation" );
  228. h.add( effectController, "llightness", 0.0, 1.0, 0.025 ).name( "lightness" );
  229. // light (directional)
  230. h = gui.addFolder( "Directional light orientation" );
  231. h.add( effectController, "lx", - 1.0, 1.0, 0.025 ).name( "x" );
  232. h.add( effectController, "ly", - 1.0, 1.0, 0.025 ).name( "y" );
  233. h.add( effectController, "lz", - 1.0, 1.0, 0.025 ).name( "z" );
  234. // simulation
  235. h = gui.addFolder( "Simulation" );
  236. h.add( effectController, "speed", 0.1, 8.0, 0.05 );
  237. h.add( effectController, "numBlobs", 1, 50, 1 );
  238. h.add( effectController, "resolution", 14, 100, 1 );
  239. h.add( effectController, "isolation", 10, 300, 1 );
  240. h.add( effectController, "floor" );
  241. h.add( effectController, "wallx" );
  242. h.add( effectController, "wallz" );
  243. }
  244. // this controls content of marching cubes voxel field
  245. function updateCubes( object, time, numblobs, floor, wallx, wallz ) {
  246. object.reset();
  247. // fill the field with some metaballs
  248. var i, ballx, bally, ballz, subtract, strength;
  249. var rainbow = [
  250. new THREE.Color( 0xff0000 ),
  251. new THREE.Color( 0xff7f00 ),
  252. new THREE.Color( 0xffff00 ),
  253. new THREE.Color( 0x00ff00 ),
  254. new THREE.Color( 0x0000ff ),
  255. new THREE.Color( 0x4b0082 ),
  256. new THREE.Color( 0x9400d3 )
  257. ];
  258. subtract = 12;
  259. strength = 1.2 / ( ( Math.sqrt( numblobs ) - 1 ) / 4 + 1 );
  260. for ( i = 0; i < numblobs; i ++ ) {
  261. ballx = Math.sin( i + 1.26 * time * ( 1.03 + 0.5 * Math.cos( 0.21 * i ) ) ) * 0.27 + 0.5;
  262. bally = Math.abs( Math.cos( i + 1.12 * time * Math.cos( 1.22 + 0.1424 * i ) ) ) * 0.77; // dip into the floor
  263. ballz = Math.cos( i + 1.32 * time * 0.1 * Math.sin( ( 0.92 + 0.53 * i ) ) ) * 0.27 + 0.5;
  264. if ( current_material === 'multiColors' ) {
  265. object.addBall( ballx, bally, ballz, strength, subtract, rainbow[ i % 7 ] );
  266. } else {
  267. object.addBall( ballx, bally, ballz, strength, subtract );
  268. }
  269. }
  270. if ( floor ) object.addPlaneY( 2, 12 );
  271. if ( wallz ) object.addPlaneZ( 2, 12 );
  272. if ( wallx ) object.addPlaneX( 2, 12 );
  273. }
  274. //
  275. function animate() {
  276. requestAnimationFrame( animate );
  277. render();
  278. stats.update();
  279. }
  280. function render() {
  281. var delta = clock.getDelta();
  282. time += delta * effectController.speed * 0.5;
  283. // marching cubes
  284. if ( effectController.resolution !== resolution ) {
  285. resolution = effectController.resolution;
  286. effect.init( Math.floor( resolution ) );
  287. }
  288. if ( effectController.isolation !== effect.isolation ) {
  289. effect.isolation = effectController.isolation;
  290. }
  291. updateCubes( effect, time, effectController.numBlobs, effectController.floor, effectController.wallx, effectController.wallz );
  292. // materials
  293. if ( effect.material instanceof THREE.ShaderMaterial ) {
  294. effect.material.uniforms[ "uBaseColor" ].value.setHSL( effectController.hue, effectController.saturation, effectController.lightness );
  295. } else {
  296. effect.material.color.setHSL( effectController.hue, effectController.saturation, effectController.lightness );
  297. }
  298. // lights
  299. light.position.set( effectController.lx, effectController.ly, effectController.lz );
  300. light.position.normalize();
  301. pointLight.color.setHSL( effectController.lhue, effectController.lsaturation, effectController.llightness );
  302. // render
  303. renderer.render( scene, camera );
  304. }
  305. </script>
  306. </body>
  307. </html>