webgl_marchingcubes.html 11 KB

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