|
@@ -1,470 +0,0 @@
|
|
|
-<!DOCTYPE html><html lang="en"><head>
|
|
|
- <meta charset="utf-8">
|
|
|
- <title>Custom Geometry</title>
|
|
|
- <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
|
|
|
- <meta name="twitter:card" content="summary_large_image">
|
|
|
- <meta name="twitter:site" content="@threejs">
|
|
|
- <meta name="twitter:title" content="Three.js – Custom Geometry">
|
|
|
- <meta property="og:image" content="https://threejs.org/files/share.png">
|
|
|
- <link rel="shortcut icon" href="/files/favicon_white.ico" media="(prefers-color-scheme: dark)">
|
|
|
- <link rel="shortcut icon" href="/files/favicon.ico" media="(prefers-color-scheme: light)">
|
|
|
-
|
|
|
- <link rel="stylesheet" href="/manual/resources/lesson.css">
|
|
|
- <link rel="stylesheet" href="/manual/resources/lang.css">
|
|
|
-<!-- Import maps polyfill -->
|
|
|
-<!-- Remove this when import maps will be widely supported -->
|
|
|
-<script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
|
|
|
-
|
|
|
-<script type="importmap">
|
|
|
-{
|
|
|
- "imports": {
|
|
|
- "three": "../../build/three.module.js"
|
|
|
- }
|
|
|
-}
|
|
|
-</script>
|
|
|
- </head>
|
|
|
- <body>
|
|
|
- <div class="container">
|
|
|
- <div class="lesson-title">
|
|
|
- <h1>Custom Geometry</h1>
|
|
|
- </div>
|
|
|
- <div class="lesson">
|
|
|
- <div class="lesson-main">
|
|
|
- <div class="warning">
|
|
|
-<strong>NOTE!</strong> This article is deprecated. Three.js r125
|
|
|
-removed support for <code class="notranslate" translate="no">Geometry</code>. Please refer to
|
|
|
-the article on <a href="custom-buffergeometry.html">custom BufferGeometry</a>.
|
|
|
-</div>
|
|
|
-
|
|
|
-<p>A <a href="primitives.html">previous article</a> gave a tour of
|
|
|
-the various built in primitives included in THREE.js. In this
|
|
|
-article we'll cover making our own geometry.</p>
|
|
|
-<p>Just to be clear, if you are serious about making 3D content,
|
|
|
-the most common way is to use a 3D modeling package like
|
|
|
-<a href="https://blender.org">Blender</a>,
|
|
|
-<a href="https://www.autodesk.com/products/maya/overview">Maya</a>,
|
|
|
-<a href="https://www.autodesk.com/products/3ds-max/overview">3D Studio Max</a>,
|
|
|
-<a href="https://www.maxon.net/en-us/">Cinema4D</a>, etc...
|
|
|
-You'd build a model and then export to <a href="load-gltf.html">gLTF</a>
|
|
|
-or <a href="load-obj.html">.obj</a> and load them up.
|
|
|
-Whichever one you choose, expect to spend 2 or 3 weeks going through
|
|
|
-their respective tutorials as all of them have a learning curve
|
|
|
-to be useful.</p>
|
|
|
-<p>Still, there are times when we might want to generate our own
|
|
|
-3D geometry in code instead of using a modeling package.</p>
|
|
|
-<p>First let's just make a cube. Even though three.js already
|
|
|
-provides us with <a href="/docs/#api/en/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a> and <a href="/docs/#api/en/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a> a
|
|
|
-cube is easy to understand so let's start there.</p>
|
|
|
-<p>There are 2 ways to make custom geometry in THREE.js. One
|
|
|
-is with the <code class="notranslate" translate="no">Geometry</code> class, the other is <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>.
|
|
|
-Each has their advantages. <code class="notranslate" translate="no">Geometry</code> is arguably easier to
|
|
|
-use but slower and uses more memory. For few 1000s triangles
|
|
|
-it's a great choice but for 10s of thousands of triangles
|
|
|
-it might be better to use <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>.</p>
|
|
|
-<p><a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a> is arguably harder to use but uses less
|
|
|
-memory and is faster. If quick rule of thumb might be
|
|
|
-if you're going to generate more than 10000 triangles
|
|
|
-consider using <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>.</p>
|
|
|
-<p>Note when I say <code class="notranslate" translate="no">Geometry</code> is slower I mean it is slower to
|
|
|
-start and slower to modify but it is not slower to draw so
|
|
|
-if you're not planning on modifying your geometry then
|
|
|
-as long as it's not too large there will only be slightly more
|
|
|
-delay for your program to start using <code class="notranslate" translate="no">Geometry</code> vs using
|
|
|
-<a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>. We'll go over both eventually. For now
|
|
|
-though let's use geometry as it's easier to understand IMO.</p>
|
|
|
-<p>First let's make a cube with <code class="notranslate" translate="no">Geometry</code>. We'll start
|
|
|
-with an example from <a href="responsive.html">the article on responsiveness</a>.</p>
|
|
|
-<p>Let's remove the part that uses <a href="/docs/#api/en/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a> and replace it with
|
|
|
-a <code class="notranslate" translate="no">Geometry</code>.</p>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const boxWidth = 1;
|
|
|
--const boxHeight = 1;
|
|
|
--const boxDepth = 1;
|
|
|
--const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
|
|
|
-+const geometry = new THREE.Geometry();
|
|
|
-</pre>
|
|
|
-<p>Now let's add the 8 corners of a cube. Here are the 8 corners.</p>
|
|
|
-<div class="threejs_center"><img src="../resources/cube-vertex-positions.svg" style="width: 500px"></div>
|
|
|
-
|
|
|
-<p>Centered around the origin we can add the vertex positions like this</p>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const geometry = new THREE.Geometry();
|
|
|
-+geometry.vertices.push(
|
|
|
-+ new THREE.Vector3(-1, -1, 1), // 0
|
|
|
-+ new THREE.Vector3( 1, -1, 1), // 1
|
|
|
-+ new THREE.Vector3(-1, 1, 1), // 2
|
|
|
-+ new THREE.Vector3( 1, 1, 1), // 3
|
|
|
-+ new THREE.Vector3(-1, -1, -1), // 4
|
|
|
-+ new THREE.Vector3( 1, -1, -1), // 5
|
|
|
-+ new THREE.Vector3(-1, 1, -1), // 6
|
|
|
-+ new THREE.Vector3( 1, 1, -1), // 7
|
|
|
-+);
|
|
|
-</pre>
|
|
|
-<p>We then need to make triangles, 2 for each face of the cube</p>
|
|
|
-<div class="threejs_center"><img src="../resources/cube-triangles.svg" style="width: 500px"></div>
|
|
|
-
|
|
|
-<p>We do that by creating <a href="/docs/#api/en/core/Face3"><code class="notranslate" translate="no">Face3</code></a> objects and specifying the indices
|
|
|
-of the 3 vertices that make up that face.</p>
|
|
|
-<p>The order we specify the vertices is important. To be pointing toward the
|
|
|
-outside of the cube they must be specified in a counter clockwise direction
|
|
|
-when that triangle is facing the camera.</p>
|
|
|
-<div class="threejs_center"><img src="../resources/cube-vertex-winding-order.svg" style="width: 500px"></div>
|
|
|
-
|
|
|
-<p>Following that pattern we can specify the 12 triangles that make
|
|
|
-the cube like this</p>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.faces.push(
|
|
|
- // front
|
|
|
- new THREE.Face3(0, 3, 2),
|
|
|
- new THREE.Face3(0, 1, 3),
|
|
|
- // right
|
|
|
- new THREE.Face3(1, 7, 3),
|
|
|
- new THREE.Face3(1, 5, 7),
|
|
|
- // back
|
|
|
- new THREE.Face3(5, 6, 7),
|
|
|
- new THREE.Face3(5, 4, 6),
|
|
|
- // left
|
|
|
- new THREE.Face3(4, 2, 6),
|
|
|
- new THREE.Face3(4, 0, 2),
|
|
|
- // top
|
|
|
- new THREE.Face3(2, 7, 6),
|
|
|
- new THREE.Face3(2, 3, 7),
|
|
|
- // bottom
|
|
|
- new THREE.Face3(4, 1, 0),
|
|
|
- new THREE.Face3(4, 5, 1),
|
|
|
-);
|
|
|
-</pre>
|
|
|
-<p>A few other minor changes to the original code and it should
|
|
|
-work.</p>
|
|
|
-<p>These cubes are twice as large as the <a href="/docs/#api/en/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a> we were
|
|
|
-using before so let's move the camera back a little</p>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const fov = 75;
|
|
|
-const aspect = 2; // the canvas default
|
|
|
-const near = 0.1;
|
|
|
--const far = 5;
|
|
|
-+const far = 100;
|
|
|
-const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
|
|
|
--camera.position.z = 2;
|
|
|
-+camera.position.z = 5;
|
|
|
-</pre>
|
|
|
-<p>and let's separate them a little more and I changed their colors just because</p>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cubes = [
|
|
|
-- makeInstance(geometry, 0x44aa88, 0),
|
|
|
-- makeInstance(geometry, 0x8844aa, -2),
|
|
|
-- makeInstance(geometry, 0xaa8844, 2),
|
|
|
-+ makeInstance(geometry, 0x44FF44, 0),
|
|
|
-+ makeInstance(geometry, 0x4444FF, -4),
|
|
|
-+ makeInstance(geometry, 0xFF4444, 4),
|
|
|
-];
|
|
|
-</pre>
|
|
|
-<p>One last thing is we haven't added normals yet so we
|
|
|
-can't do any lighting. Let's change the material
|
|
|
-to something that doesn't need lights.</p>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeInstance(geometry, color, x) {
|
|
|
-- const material = new THREE.MeshPhongMaterial({color});
|
|
|
-+ const material = new THREE.MeshBasicMaterial({color});
|
|
|
-
|
|
|
- const cube = new THREE.Mesh(geometry, material);
|
|
|
- scene.add(cube);
|
|
|
-
|
|
|
- ...
|
|
|
-</pre>
|
|
|
-<p>and we get cubes we made ourselves.</p>
|
|
|
-<p></p><div translate="no" class="threejs_example_container notranslate">
|
|
|
- <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-cube.html"></iframe></div>
|
|
|
- <a class="threejs_center" href="/manual/examples/custom-geometry-cube.html" target="_blank">click here to open in a separate window</a>
|
|
|
-</div>
|
|
|
-
|
|
|
-<p></p>
|
|
|
-<p>We can specify a color per face by setting the <code class="notranslate" translate="no">color</code> property of
|
|
|
-each face.</p>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.faces[ 0].color = geometry.faces[ 1].color = new THREE.Color('red');
|
|
|
-geometry.faces[ 2].color = geometry.faces[ 3].color = new THREE.Color('yellow');
|
|
|
-geometry.faces[ 4].color = geometry.faces[ 5].color = new THREE.Color('green');
|
|
|
-geometry.faces[ 6].color = geometry.faces[ 7].color = new THREE.Color('cyan');
|
|
|
-geometry.faces[ 8].color = geometry.faces[ 9].color = new THREE.Color('blue');
|
|
|
-geometry.faces[10].color = geometry.faces[11].color = new THREE.Color('magenta');
|
|
|
-</pre>
|
|
|
-<p>note we need to tell the material we want to use vertex colors</p>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const material = new THREE.MeshBasicMaterial({color});
|
|
|
-+const material = new THREE.MeshBasicMaterial({vertexColors: true});
|
|
|
-</pre>
|
|
|
-<p></p><div translate="no" class="threejs_example_container notranslate">
|
|
|
- <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-cube-face-colors.html"></iframe></div>
|
|
|
- <a class="threejs_center" href="/manual/examples/custom-geometry-cube-face-colors.html" target="_blank">click here to open in a separate window</a>
|
|
|
-</div>
|
|
|
-
|
|
|
-<p></p>
|
|
|
-<p>We can instead set the color of each individual vertex by setting the <code class="notranslate" translate="no">vertexColors</code>
|
|
|
-property of a <code class="notranslate" translate="no">Face</code> to an array of the 3 colors for the 3 vertices.</p>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.faces.forEach((face, ndx) => {
|
|
|
- face.vertexColors = [
|
|
|
- (new THREE.Color()).setHSL(ndx / 12 , 1, 0.5),
|
|
|
- (new THREE.Color()).setHSL(ndx / 12 + 0.1, 1, 0.5),
|
|
|
- (new THREE.Color()).setHSL(ndx / 12 + 0.2, 1, 0.5),
|
|
|
- ];
|
|
|
-});
|
|
|
-</pre>
|
|
|
-<p></p><div translate="no" class="threejs_example_container notranslate">
|
|
|
- <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-cube-vertex-colors.html"></iframe></div>
|
|
|
- <a class="threejs_center" href="/manual/examples/custom-geometry-cube-vertex-colors.html" target="_blank">click here to open in a separate window</a>
|
|
|
-</div>
|
|
|
-
|
|
|
-<p></p>
|
|
|
-<p>To use lighting we need normals. Normals are vectors that specify direction.
|
|
|
-Just like the colors we can specify a normal for the face by setting the <code class="notranslate" translate="no">normal</code>
|
|
|
-property on each face with</p>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">face.normal = new THREE.Vector3(...)
|
|
|
-</pre>
|
|
|
-<p>or we can specify a normal for each vertex by setting the <code class="notranslate" translate="no">vertexNormals</code>
|
|
|
-property with something like</p>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">face.vertexNormals = [
|
|
|
- new THREE.Vector3(...),
|
|
|
- new THREE.Vector3(...),
|
|
|
- new THREE.Vector3(...),
|
|
|
-]
|
|
|
-</pre>
|
|
|
-<p>but often it's much easier to just ask THREE.js to compute normals
|
|
|
-for us based on the positions we specified.</p>
|
|
|
-<p>For face normals we'd call <code class="notranslate" translate="no">Geometry.computeFaceNormals</code> as in</p>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.computeFaceNormals();
|
|
|
-</pre>
|
|
|
-<p>Removing the vertex color stuff and changing the material back to <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a></p>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const material = new THREE.MeshBasicMaterial({vertexColors: true});
|
|
|
-+const material = new THREE.MeshPhongMaterial({color});
|
|
|
-</pre>
|
|
|
-<p>and now our cubes can be lit.</p>
|
|
|
-<p></p><div translate="no" class="threejs_example_container notranslate">
|
|
|
- <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-cube-face-normals.html"></iframe></div>
|
|
|
- <a class="threejs_center" href="/manual/examples/custom-geometry-cube-face-normals.html" target="_blank">click here to open in a separate window</a>
|
|
|
-</div>
|
|
|
-
|
|
|
-<p></p>
|
|
|
-<p>Using face normals will always give us a faceted look. We can use
|
|
|
-vertex normals for a smoother look by calling <code class="notranslate" translate="no">Geometry.computeVertexNormals</code></p>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-geometry.computeFaceNormals();
|
|
|
-+geometry.computeVertexNormals();
|
|
|
-</pre>
|
|
|
-<p>Unfortunately a cube is not a good candidate for vertex normals since it
|
|
|
-means each vertex gets its normal from the
|
|
|
-normals of all the faces it shares.</p>
|
|
|
-<p></p><div translate="no" class="threejs_example_container notranslate">
|
|
|
- <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-cube-vertex-normals.html"></iframe></div>
|
|
|
- <a class="threejs_center" href="/manual/examples/custom-geometry-cube-vertex-normals.html" target="_blank">click here to open in a separate window</a>
|
|
|
-</div>
|
|
|
-
|
|
|
-<p></p>
|
|
|
-<p>Adding texture coordinates, sometimes called UVs, is done via an array of
|
|
|
-layers of parallel arrays to the <code class="notranslate" translate="no">faces</code> array which is set via <code class="notranslate" translate="no">Geometry.faceVertexUvs</code>.
|
|
|
-For our cube we could do something like</p>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.faceVertexUvs[0].push(
|
|
|
- // front
|
|
|
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
|
|
|
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
|
|
|
- // right
|
|
|
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
|
|
|
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
|
|
|
- // back
|
|
|
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
|
|
|
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
|
|
|
- // left
|
|
|
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
|
|
|
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
|
|
|
- // top
|
|
|
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
|
|
|
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
|
|
|
- // bottom
|
|
|
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
|
|
|
- [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
|
|
|
-);
|
|
|
-</pre>
|
|
|
-<p>It's important to notice <code class="notranslate" translate="no">faceVertexUvs</code> is an array of layers. Each layer
|
|
|
-is another set of UV coordinates. By default there is one layer of UV coordinates,
|
|
|
-layer 0, so we just add our UVs to that layer.</p>
|
|
|
-<p>Let's <a href="textures.html">add a texture</a> to our material and switch back to compute face normals</p>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-geometry.computeVertexNormals();
|
|
|
-+geometry.computeFaceNormals();
|
|
|
-
|
|
|
-+const loader = new THREE.TextureLoader();
|
|
|
-+const texture = loader.load('resources/images/star.png');
|
|
|
-
|
|
|
-function makeInstance(geometry, color, x) {
|
|
|
-- const material = new THREE.MeshPhongMaterial({color});
|
|
|
-+ const material = new THREE.MeshPhongMaterial({color, map: texture});
|
|
|
-
|
|
|
- const cube = new THREE.Mesh(geometry, material);
|
|
|
- scene.add(cube);
|
|
|
-
|
|
|
- ...
|
|
|
-</pre>
|
|
|
-<p></p><div translate="no" class="threejs_example_container notranslate">
|
|
|
- <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-cube-texcoords.html"></iframe></div>
|
|
|
- <a class="threejs_center" href="/manual/examples/custom-geometry-cube-texcoords.html" target="_blank">click here to open in a separate window</a>
|
|
|
-</div>
|
|
|
-
|
|
|
-<p></p>
|
|
|
-<p>Putting that all together, let's make a simple heightmap based
|
|
|
-terrain mesh.</p>
|
|
|
-<p>A heightmap based terrain is where you have a 2D array of heights
|
|
|
-that you apply them to a grid. An easy way to get a 2D array of heights
|
|
|
-is to draw them in an image editing program. Here's an image I drew.
|
|
|
-It's 96x64 pixels</p>
|
|
|
-<div class="threejs_center"><img src="../examples/resources/images/heightmap-96x64.png" style="width: 512px; image-rendering: pixelated;"></div>
|
|
|
-
|
|
|
-<p>We'll load that and then generate a heightmap mesh from it.
|
|
|
-We can use the <a href="/docs/#api/en/loaders/ImageLoader"><code class="notranslate" translate="no">ImageLoader</code></a> to load the image.</p>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const imgLoader = new THREE.ImageLoader();
|
|
|
-imgLoader.load('resources/images/heightmap-96x64.png', createHeightmap);
|
|
|
-
|
|
|
-function createHeightmap(image) {
|
|
|
- // extract the data from the image by drawing it to a canvas
|
|
|
- // and calling getImageData
|
|
|
- const ctx = document.createElement('canvas').getContext('2d');
|
|
|
- const {width, height} = image;
|
|
|
- ctx.canvas.width = width;
|
|
|
- ctx.canvas.height = height;
|
|
|
- ctx.drawImage(image, 0, 0);
|
|
|
- const {data} = ctx.getImageData(0, 0, width, height);
|
|
|
-
|
|
|
- const geometry = new THREE.Geometry();
|
|
|
-</pre>
|
|
|
-<p>We extracted the data from the image, now we'll make a grid of cells.
|
|
|
-The cells are the squares formed by the center points of each pixel
|
|
|
-from the image</p>
|
|
|
-<div class="threejs_center"><img src="../resources/heightmap-points.svg" style="width: 500px"></div>
|
|
|
-
|
|
|
-<p>For each cell we'll generate 5 vertices. One for each corner of the cell
|
|
|
-and one at the center point of the cell with the average height of the 4
|
|
|
-corner heights.</p>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cellsAcross = width - 1;
|
|
|
-const cellsDeep = height - 1;
|
|
|
-for (let z = 0; z < cellsDeep; ++z) {
|
|
|
- for (let x = 0; x < cellsAcross; ++x) {
|
|
|
- // compute row offsets into the height data
|
|
|
- // we multiply by 4 because the data is R,G,B,A but we
|
|
|
- // only care about R
|
|
|
- const base0 = (z * width + x) * 4;
|
|
|
- const base1 = base0 + (width * 4);
|
|
|
-
|
|
|
- // look up the height for the for points
|
|
|
- // around this cell
|
|
|
- const h00 = data[base0] / 32;
|
|
|
- const h01 = data[base0 + 4] / 32;
|
|
|
- const h10 = data[base1] / 32;
|
|
|
- const h11 = data[base1 + 4] / 32;
|
|
|
- // compute the average height
|
|
|
- const hm = (h00 + h01 + h10 + h11) / 4;
|
|
|
-
|
|
|
- // the corner positions
|
|
|
- const x0 = x;
|
|
|
- const x1 = x + 1;
|
|
|
- const z0 = z;
|
|
|
- const z1 = z + 1;
|
|
|
-
|
|
|
- // remember the first index of these 5 vertices
|
|
|
- const ndx = geometry.vertices.length;
|
|
|
-
|
|
|
- // add the 4 corners for this cell and the midpoint
|
|
|
- geometry.vertices.push(
|
|
|
- new THREE.Vector3(x0, h00, z0),
|
|
|
- new THREE.Vector3(x1, h01, z0),
|
|
|
- new THREE.Vector3(x0, h10, z1),
|
|
|
- new THREE.Vector3(x1, h11, z1),
|
|
|
- new THREE.Vector3((x0 + x1) / 2, hm, (z0 + z1) / 2),
|
|
|
- );
|
|
|
-</pre>
|
|
|
-<p>We'll then make 4 triangles from those 5 vertices</p>
|
|
|
-<div class="threejs_center"><img src="../resources/heightmap-triangles.svg" style="width: 500px"></div>
|
|
|
-
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no"> // create 4 triangles
|
|
|
- geometry.faces.push(
|
|
|
- new THREE.Face3(ndx + 0, ndx + 4, ndx + 1),
|
|
|
- new THREE.Face3(ndx + 1, ndx + 4, ndx + 3),
|
|
|
- new THREE.Face3(ndx + 3, ndx + 4, ndx + 2),
|
|
|
- new THREE.Face3(ndx + 2, ndx + 4, ndx + 0),
|
|
|
- );
|
|
|
-
|
|
|
- // add the texture coordinates for each vertex of each face
|
|
|
- const u0 = x / cellsAcross;
|
|
|
- const v0 = z / cellsDeep;
|
|
|
- const u1 = (x + 1) / cellsAcross;
|
|
|
- const v1 = (z + 1) / cellsDeep;
|
|
|
- const um = (u0 + u1) / 2;
|
|
|
- const vm = (v0 + v1) / 2;
|
|
|
- geometry.faceVertexUvs[0].push(
|
|
|
- [ new THREE.Vector2(u0, v0), new THREE.Vector2(um, vm), new THREE.Vector2(u1, v0) ],
|
|
|
- [ new THREE.Vector2(u1, v0), new THREE.Vector2(um, vm), new THREE.Vector2(u1, v1) ],
|
|
|
- [ new THREE.Vector2(u1, v1), new THREE.Vector2(um, vm), new THREE.Vector2(u0, v1) ],
|
|
|
- [ new THREE.Vector2(u0, v1), new THREE.Vector2(um, vm), new THREE.Vector2(u0, v0) ],
|
|
|
- );
|
|
|
- }
|
|
|
-}
|
|
|
-</pre>
|
|
|
-<p>and finish it up</p>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no"> geometry.computeFaceNormals();
|
|
|
-
|
|
|
- // center the geometry
|
|
|
- geometry.translate(width / -2, 0, height / -2);
|
|
|
-
|
|
|
- const loader = new THREE.TextureLoader();
|
|
|
- const texture = loader.load('resources/images/star.png');
|
|
|
-
|
|
|
- const material = new THREE.MeshPhongMaterial({color: 'green', map: texture});
|
|
|
-
|
|
|
- const cube = new THREE.Mesh(geometry, material);
|
|
|
- scene.add(cube);
|
|
|
-}
|
|
|
-</pre>
|
|
|
-<p>A few minor changes to make it easier to view.</p>
|
|
|
-<ul>
|
|
|
-<li>include the <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a></li>
|
|
|
-</ul>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as THREE from '/build/three.module.js';
|
|
|
-+import {OrbitControls} from '/examples/jsm/controls/OrbitControls.js';
|
|
|
-</pre>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const fov = 75;
|
|
|
-const aspect = 2; // the canvas default
|
|
|
-const near = 0.1;
|
|
|
--const far = 100;
|
|
|
-+const far = 200;
|
|
|
-const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
|
|
|
--camera.position.z = 5;
|
|
|
-+camera.position.set(20, 20, 20);
|
|
|
-
|
|
|
-+const controls = new OrbitControls(camera, canvas);
|
|
|
-+controls.target.set(0, 0, 0);
|
|
|
-+controls.update();
|
|
|
-</pre>
|
|
|
-<p>add 2 lights</p>
|
|
|
-<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-{
|
|
|
-+function addLight(...pos) {
|
|
|
- const color = 0xFFFFFF;
|
|
|
- const intensity = 1;
|
|
|
- const light = new THREE.DirectionalLight(color, intensity);
|
|
|
-- light.position.set(-1, 2, 4\);
|
|
|
-+ light.position.set(...pos);
|
|
|
- scene.add(light);
|
|
|
-}
|
|
|
-
|
|
|
-+addLight(-1, 2, 4);
|
|
|
-+addLight(1, 2, -2);
|
|
|
-</pre>
|
|
|
-<p>and we deleted the code related to spinning the cubes.</p>
|
|
|
-<p></p><div translate="no" class="threejs_example_container notranslate">
|
|
|
- <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-heightmap.html"></iframe></div>
|
|
|
- <a class="threejs_center" href="/manual/examples/custom-geometry-heightmap.html" target="_blank">click here to open in a separate window</a>
|
|
|
-</div>
|
|
|
-
|
|
|
-<p></p>
|
|
|
-<p>I hope that was a useful instruction to making your own
|
|
|
-geometry using <code class="notranslate" translate="no">Geometry</code>.</p>
|
|
|
-<p>In <a href="custom-buffergeometry.html">another article</a> we'll go over <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>.</p>
|
|
|
-
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <script src="/manual/resources/prettify.js"></script>
|
|
|
- <script src="/manual/resources/lesson.js"></script>
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-</body></html>
|