threejs-custom-geometry-heightmap.html 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. <!-- Licensed under a BSD license. See license.html for license -->
  2. <!DOCTYPE html>
  3. <html>
  4. <head>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
  7. <title>Three.js - Custom Geometry - Heightmap</title>
  8. <style>
  9. body {
  10. margin: 0;
  11. }
  12. #c {
  13. width: 100vw;
  14. height: 100vh;
  15. display: block;
  16. }
  17. </style>
  18. </head>
  19. <body>
  20. <canvas id="c"></canvas>
  21. </body>
  22. <script src="resources/threejs/r105/three.min.js"></script>
  23. <script src="resources/threejs/r105/js/controls/OrbitControls.js"></script>
  24. <script>
  25. 'use strict';
  26. /* global THREE */
  27. function main() {
  28. const canvas = document.querySelector('#c');
  29. const renderer = new THREE.WebGLRenderer({canvas});
  30. const fov = 75;
  31. const aspect = 2; // the canvas default
  32. const near = 0.1;
  33. const far = 200;
  34. const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  35. camera.position.set(20, 20, 20);
  36. const controls = new THREE.OrbitControls(camera, canvas);
  37. controls.target.set(0, 0, 0);
  38. controls.update();
  39. const scene = new THREE.Scene();
  40. function addLight(...pos) {
  41. const color = 0xFFFFFF;
  42. const intensity = 1;
  43. const light = new THREE.DirectionalLight(color, intensity);
  44. light.position.set(...pos);
  45. scene.add(light);
  46. }
  47. addLight(-1, 2, 4);
  48. addLight(1, 2, -2);
  49. const imgLoader = new THREE.ImageLoader();
  50. imgLoader.load('resources/images/heightmap-64x64.png', createHeightmap);
  51. function createHeightmap(image) {
  52. // extract the data from the image by drawing it to a canvas
  53. // and calling getImageData
  54. const ctx = document.createElement('canvas').getContext('2d');
  55. const {width, height} = image;
  56. ctx.canvas.width = width;
  57. ctx.canvas.height = height;
  58. ctx.drawImage(image, 0, 0);
  59. const {data} = ctx.getImageData(0, 0, width, height);
  60. const geometry = new THREE.Geometry();
  61. const cellsAcross = width - 1;
  62. const cellsDeep = height - 1;
  63. for (let z = 0; z < cellsDeep; ++z) {
  64. for (let x = 0; x < cellsAcross; ++x) {
  65. // compute row offsets into the height data
  66. // we multiply by 4 because the data is R,G,B,A but we
  67. // only care about R
  68. const base0 = (z * width + x) * 4;
  69. const base1 = base0 + (width * 4);
  70. // look up the height for the for points
  71. // around this cell
  72. const h00 = data[base0] / 32;
  73. const h01 = data[base0 + 4] / 32;
  74. const h10 = data[base1] / 32;
  75. const h11 = data[base1 + 4] / 32;
  76. // compute the average height
  77. const hm = (h00 + h01 + h10 + h11) / 4;
  78. // the corner positions
  79. const x0 = x;
  80. const x1 = x + 1;
  81. const z0 = z;
  82. const z1 = z + 1;
  83. // remember the first index of these 5 vertices
  84. const ndx = geometry.vertices.length;
  85. // add the 4 corners for this cell and the midpoint
  86. geometry.vertices.push(
  87. new THREE.Vector3(x0, h00, z0),
  88. new THREE.Vector3(x1, h01, z0),
  89. new THREE.Vector3(x0, h10, z1),
  90. new THREE.Vector3(x1, h11, z1),
  91. new THREE.Vector3((x0 + x1) / 2, hm, (z0 + z1) / 2),
  92. );
  93. // 2----3
  94. // |\ /|
  95. // | \/4|
  96. // | /\ |
  97. // |/ \|
  98. // 0----1
  99. // create 4 triangles
  100. geometry.faces.push(
  101. new THREE.Face3(ndx , ndx + 4, ndx + 1),
  102. new THREE.Face3(ndx + 1, ndx + 4, ndx + 3),
  103. new THREE.Face3(ndx + 3, ndx + 4, ndx + 2),
  104. new THREE.Face3(ndx + 2, ndx + 4, ndx + 0),
  105. );
  106. // add the texture coordinates for each vertex of each face.
  107. const u0 = x / cellsAcross;
  108. const v0 = z / cellsAcross;
  109. const u1 = (x + 1) / cellsDeep;
  110. const v1 = (z + 1) / cellsDeep;
  111. const um = (u0 + u1) / 2;
  112. const vm = (v0 + v1) / 2;
  113. geometry.faceVertexUvs[0].push(
  114. [ new THREE.Vector2(u0, v0), new THREE.Vector2(um, vm), new THREE.Vector2(u1, v0) ],
  115. [ new THREE.Vector2(u1, v0), new THREE.Vector2(um, vm), new THREE.Vector2(u1, v1) ],
  116. [ new THREE.Vector2(u1, v1), new THREE.Vector2(um, vm), new THREE.Vector2(u0, v1) ],
  117. [ new THREE.Vector2(u0, v1), new THREE.Vector2(um, vm), new THREE.Vector2(u0, v0) ],
  118. );
  119. }
  120. }
  121. geometry.computeFaceNormals();
  122. // center the geometry
  123. geometry.translate(width / -2, 0, height / -2);
  124. const loader = new THREE.TextureLoader();
  125. const texture = loader.load('resources/images/star.png');
  126. const material = new THREE.MeshPhongMaterial({color: 'green', map: texture});
  127. const cube = new THREE.Mesh(geometry, material);
  128. scene.add(cube);
  129. }
  130. function resizeRendererToDisplaySize(renderer) {
  131. const canvas = renderer.domElement;
  132. const width = canvas.clientWidth;
  133. const height = canvas.clientHeight;
  134. const needResize = canvas.width !== width || canvas.height !== height;
  135. if (needResize) {
  136. renderer.setSize(width, height, false);
  137. }
  138. return needResize;
  139. }
  140. function render() {
  141. if (resizeRendererToDisplaySize(renderer)) {
  142. const canvas = renderer.domElement;
  143. camera.aspect = canvas.clientWidth / canvas.clientHeight;
  144. camera.updateProjectionMatrix();
  145. }
  146. renderer.render(scene, camera);
  147. requestAnimationFrame(render);
  148. }
  149. requestAnimationFrame(render);
  150. }
  151. main();
  152. </script>
  153. </html>