|
@@ -1,5 +1,5 @@
|
|
|
Title: Three.js Textures
|
|
|
-Description: Using Textures in three.js
|
|
|
+Description: Using textures in three.js
|
|
|
|
|
|
This article is one in a series of articles about three.js.
|
|
|
The first article was [about three.js fundamentals](threejs-fundamentals.html).
|
|
@@ -8,22 +8,22 @@ If you haven't read that yet you might want to start there.
|
|
|
|
|
|
Textures are a kind of large topic in Three.js and
|
|
|
I'm not 100% sure at what level to explain them but I will try.
|
|
|
-There are many topics and many of them inter-relate so it's hard to explain
|
|
|
+There are many topics and many of them interrelate so it's hard to explain
|
|
|
them all at once. Here's quick table of contents for this article.
|
|
|
|
|
|
<ul>
|
|
|
<li><a href="#hello">Hello Texture</a></li>
|
|
|
-<li><a href="#six">6 Textures, a different one on each face of a cube</a></li>
|
|
|
-<li><a href="#loading">Loading Textures</a></li>
|
|
|
+<li><a href="#six">6 textures, a different one on each face of a cube</a></li>
|
|
|
+<li><a href="#loading">Loading textures</a></li>
|
|
|
<ul>
|
|
|
- <li><a href="#easy">The Easy Way</a></li>
|
|
|
+ <li><a href="#easy">The easy way</a></li>
|
|
|
<li><a href="#wait1">Waiting for a texture to load</a></li>
|
|
|
<li><a href="#waitmany">Waiting for multiple textures to load</a></li>
|
|
|
<li><a href="#cors">Loading textures from other origins</a></li>
|
|
|
</ul>
|
|
|
-<li><a href="#memory">Memory Usage</a></li>
|
|
|
+<li><a href="#memory">Memory usage</a></li>
|
|
|
<li><a href="#format">JPG vs PNG</a></li>
|
|
|
-<li><a href="#filtering-and-mips">Filtering and Mips</a></li>
|
|
|
+<li><a href="#filtering-and-mips">Filtering and mips</a></li>
|
|
|
<li><a href="#uvmanipulation">Repeating, offseting, rotating, wrapping</a></li>
|
|
|
</ul>
|
|
|
|
|
@@ -56,7 +56,7 @@ Note that we're using `MeshBasicMaterial` so no need for any lights.
|
|
|
|
|
|
## <a name="six"></a> 6 Textures, a different one on each face of a cube
|
|
|
|
|
|
-How about 6 textures, one on each face of a cube?
|
|
|
+How about 6 textures, one on each face of a cube?
|
|
|
|
|
|
<div class="threejs_center">
|
|
|
<div>
|
|
@@ -95,23 +95,23 @@ It works!
|
|
|
|
|
|
{{{example url="../threejs-textured-cube-6-textures.html" }}}
|
|
|
|
|
|
-It should be noted though that by default the only Geometry that supports multiple
|
|
|
+It should be noted though that by default the only geometry that supports multiple
|
|
|
materials is the `BoxGeometry` and `BoxBufferGeometry`. For other cases you will
|
|
|
need to build or load custom geometry and/or modify texture coordinates. It's far
|
|
|
-more common to use a [Texture Atlas](https://en.wikipedia.org/wiki/Texture_atlas)
|
|
|
+more common to use a [Texture Atlas](https://en.wikipedia.org/wiki/Texture_atlas)
|
|
|
if you want to allow multiple images on a single
|
|
|
geometry.
|
|
|
|
|
|
What are texture coordinates? They are data added to each vertex of a piece of geometry
|
|
|
-that specify what part of the texture corresponds to that specific vertex.
|
|
|
+that specify what part of the texture corresponds to that specific vertex.
|
|
|
We'll go over them when we start building custom geometry.
|
|
|
|
|
|
## <a name="loading"></a> Loading Textures
|
|
|
|
|
|
### <a name="easy"></a> The Easy Way
|
|
|
|
|
|
-Most of the code on this site uses the easiest method of loading textures.
|
|
|
-We create a `TextureLoader` and then call its [`load`](TextureLoader.load) method.
|
|
|
+Most of the code on this site uses the easiest method of loading textures.
|
|
|
+We create a `TextureLoader` and then call its [`load`](TextureLoader.load) method.
|
|
|
This returns a `Texture` object.
|
|
|
|
|
|
```js
|
|
@@ -119,7 +119,7 @@ const texture = loader.load('resources/images/flower-1.jpg');
|
|
|
```
|
|
|
|
|
|
It's important to note that using this method our texture will be transparent until
|
|
|
-the image is loaded asychronously by three.js at which point it will update the texture
|
|
|
+the image is loaded asynchronously by three.js at which point it will update the texture
|
|
|
with the downloaded image.
|
|
|
|
|
|
This has the big advantage that we don't have to wait for the texture to load and our
|
|
@@ -243,13 +243,13 @@ the loading bar.
|
|
|
|
|
|
{{{example url="../threejs-textured-cube-wait-for-all-textures.html" }}}
|
|
|
|
|
|
-## <a name="cors"></a> Loading textures from other origins.
|
|
|
+## <a name="cors"></a> Loading textures from other origins
|
|
|
|
|
|
To use images from other servers those servers need to send the correct headers.
|
|
|
-If they don't you can not use the images in three.js and will get an error.
|
|
|
+If they don't you cannot use the images in three.js and will get an error.
|
|
|
If you run the server providing the images make sure it
|
|
|
[sends the correct headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS).
|
|
|
-If you don't control the server hosting the images and it does not send the
|
|
|
+If you don't control the server hosting the images and it does not send the
|
|
|
permission headers then you can't use the images from that server.
|
|
|
|
|
|
For example [imgur](https://imgur.com), [flickr](https://flickr.com), and
|
|
@@ -259,7 +259,7 @@ hosted on their servers in three.js. Most other websites do not.
|
|
|
## <a name="memory"></a> Memory Usage
|
|
|
|
|
|
Textures are often the part of a three.js app that use the most memory. It's important to understand
|
|
|
-that *in general*, textures take `width * height * 4 * 1.33` bytes of memory.
|
|
|
+that *in general*, textures take `width * height * 4 * 1.33` bytes of memory.
|
|
|
|
|
|
Notice that says nothing about compression. I can make a .jpg image and set its compression super
|
|
|
high. For example let's say I was making a scene of a house. Inside the house there is a table
|
|
@@ -267,30 +267,30 @@ and I decide to put this wood texture on the top surface of the table
|
|
|
|
|
|
<div class="threejs_center"><img class="border" src="resources/images/compressed-but-large-wood-texture.jpg" align="center" style="width: 300px"></div>
|
|
|
|
|
|
-That image is only 157k so it will download relatively quickly but [it is actually
|
|
|
-3024 x 3761 pixels in size](resources/images/compressed-but-large-wood-texture.jpg).
|
|
|
+That image is only 157k so it will download relatively quickly but [it is actually
|
|
|
+3024 x 3761 pixels in size](resources/images/compressed-but-large-wood-texture.jpg).
|
|
|
Following the equation above that's
|
|
|
|
|
|
3024 * 3761 * 4 * 1.33 = 60505764.5
|
|
|
|
|
|
-That image will take **60 MEG OF MEMORY!** in three.js.
|
|
|
+That image will take **60 MEG OF MEMORY!** in three.js.
|
|
|
A few textures like that and you'll be out of memory.
|
|
|
|
|
|
-I bring this up because it's important to know that using textures has a hidden cost.
|
|
|
-In order for three.js to use the texture it has to hand it off to the GPU and the
|
|
|
+I bring this up because it's important to know that using textures has a hidden cost.
|
|
|
+In order for three.js to use the texture it has to hand it off to the GPU and the
|
|
|
GPU *in general* requires the texture data to be uncompressed.
|
|
|
|
|
|
-The moral of the story is make your textures small in dimensions not just small
|
|
|
-in file size. Small in file size = fast to download. Small in dimesions = takes
|
|
|
+The moral of the story is make your textures small in dimensions not just small
|
|
|
+in file size. Small in file size = fast to download. Small in dimesions = takes
|
|
|
less memory. How small should you make them?
|
|
|
As small as you can and still look as good as you need them to look.
|
|
|
|
|
|
## <a name="format"></a> JPG vs PNG
|
|
|
|
|
|
-This is pretty much the same as regular HTML in that JPGs have lossy compression,
|
|
|
-PNGs have lossless compression so PNGs are generally slower to download.
|
|
|
+This is pretty much the same as regular HTML in that JPGs have lossy compression,
|
|
|
+PNGs have lossless compression so PNGs are generally slower to download.
|
|
|
But, PNGs support transparency. PNGs are also probably the appropriate format
|
|
|
-for non-image data like normal maps, and other kinds of non-image maps which we'll go over later.
|
|
|
+for non-image data like normal maps, and other kinds of non-image maps which we'll go over later.
|
|
|
|
|
|
It's important to remember that a JPG doesn't use
|
|
|
less memory than a PNG in WebGL. See above.
|
|
@@ -323,8 +323,8 @@ to make those 1 or 2 pixels. That would be a very slow operation. GPUs solve thi
|
|
|
using mipmaps.
|
|
|
|
|
|
Mips are copies of the texture, each one half as wide and half as tall as the previous
|
|
|
-mip where the pixels have been blended to make the next smaller mip. Mips are created
|
|
|
-until we get all the way to a 1x1 pixel mip. For the image above all of the mips would
|
|
|
+mip where the pixels have been blended to make the next smaller mip. Mips are created
|
|
|
+until we get all the way to a 1x1 pixel mip. For the image above all of the mips would
|
|
|
end up being something like this
|
|
|
|
|
|
<div class="threejs_center"><img src="resources/images/mipmap-low-res-enlarged.png" align="center"></div>
|
|
@@ -333,7 +333,7 @@ Now, when the cube is drawn so small that it's only 1 or 2 pixels large the GPU
|
|
|
to use just the smallest or next to smallest mip level to decide what color to make the
|
|
|
tiny cube.
|
|
|
|
|
|
-In three you can choose what happens both what happens when the texture is drawn
|
|
|
+In three you can choose what happens both when the texture is drawn
|
|
|
larger than its original size and what happens when it's drawn smaller than its
|
|
|
original size.
|
|
|
|
|
@@ -364,33 +364,33 @@ you set the [`texture.minFilter`](Texture.minFilter) property to one of 6 values
|
|
|
|
|
|
* `THREE.NearestFilter`
|
|
|
|
|
|
- same as above. Choose the closest pixel in the texture
|
|
|
+ same as above, choose the closest pixel in the texture
|
|
|
|
|
|
* `THREE.LinearFilter`
|
|
|
|
|
|
- same as above, Choose 4 pixels from the texture and blend them
|
|
|
+ same as above, choose 4 pixels from the texture and blend them
|
|
|
|
|
|
* `THREE.NearestMipMapNearestFilter`
|
|
|
|
|
|
- choose the appropriate mip then choose one pixel.
|
|
|
+ choose the appropriate mip then choose one pixel
|
|
|
|
|
|
* `THREE.NearestMipMapLinearFilter`
|
|
|
|
|
|
- choose 2 mips, choose one pixel from each, blend the 2 pixels.
|
|
|
+ choose 2 mips, choose one pixel from each, blend the 2 pixels
|
|
|
|
|
|
* `THREE.LinearMipMapNearestFilter`
|
|
|
|
|
|
- chose the appropriate mip then choose 4 pixels and blend them.
|
|
|
+ chose the appropriate mip then choose 4 pixels and blend them
|
|
|
|
|
|
* `THREE.LinearMipMapLinearFilter`
|
|
|
|
|
|
- choose 2 mips, choose 4 pixels from each and blend all 8 into 1 pixel.
|
|
|
+ choose 2 mips, choose 4 pixels from each and blend all 8 into 1 pixel
|
|
|
|
|
|
Here's an example showing all 6 settings
|
|
|
|
|
|
<div class="spread">
|
|
|
<div data-diagram="filterModes" style="
|
|
|
- height: 450px;
|
|
|
+ height: 450px;
|
|
|
position: relative;
|
|
|
">
|
|
|
<div style="
|
|
@@ -422,13 +422,13 @@ Here's an example showing all 6 settings
|
|
|
|
|
|
One thing to notice is the top left and top middle using `NearestFilter` and `LinearFilter`
|
|
|
don't use the mips. Because of that they flicker in the distance because the GPU is
|
|
|
-picking pixels from the original texture. On the left just one pixel is chosen and
|
|
|
+picking pixels from the original texture. On the left just one pixel is chosen and
|
|
|
in the middle 4 are chosen and blended but it's not enough come up with a good
|
|
|
representative color. The other 4 strips do better with the bottom right,
|
|
|
`LinearMipMapLinearFilter` being best.
|
|
|
|
|
|
If you click the picture above it will toggle between the texture we've been using above
|
|
|
-and a texture where every mip level is a different color.
|
|
|
+and a texture where every mip level is a different color.
|
|
|
|
|
|
<div class="threejs_center">
|
|
|
<div data-texture-diagram="differentColoredMips"></div>
|
|
@@ -461,15 +461,15 @@ They can be set to one of:
|
|
|
|
|
|
* `THREE.ClampToEdgeWrapping`
|
|
|
|
|
|
- The last pixel on each edge is repeated forever
|
|
|
+ the last pixel on each edge is repeated forever
|
|
|
|
|
|
* `THREE.RepeatWrapping`
|
|
|
|
|
|
- The texture is repeated
|
|
|
+ the texture is repeated
|
|
|
|
|
|
* `THREE.MirroredRepeatWrapping`
|
|
|
|
|
|
- The texture is mirrored and repeated.
|
|
|
+ the texture is mirrored and repeated
|
|
|
|
|
|
For example to turn on wrapping in both directions:
|
|
|
|
|
@@ -487,16 +487,16 @@ someTexture.repeat.set(timesToRepeatHorizontally, timesToRepeatVertically);
|
|
|
```
|
|
|
|
|
|
Offseting the texture can be done by setting the `offset` property. Textures
|
|
|
-are offset with units where 1 unit = 1 texture size. On other words 0 = no offset
|
|
|
+are offset with units where 1 unit = 1 texture size. On other words 0 = no offset
|
|
|
and 1 = offset one full texture amount.
|
|
|
|
|
|
```js
|
|
|
const xOffset = .5; // offset by half the texture
|
|
|
-const yOffset = .25; // offset by 1/2 the texture
|
|
|
-someTexture.offset.set(xOffset, yOffset);`
|
|
|
+const yOffset = .25; // offset by 1/4 the texture
|
|
|
+someTexture.offset.set(xOffset, yOffset);
|
|
|
```
|
|
|
|
|
|
-Rotating the texture can be set by setting `rotation` property in radians
|
|
|
+Rotating the texture can be set by setting the `rotation` property in radians
|
|
|
as well as the `center` property for choosing the center of rotation.
|
|
|
It defaults to 0,0 which rotates from the bottom left corner. Like offset
|
|
|
these units are in texture size so setting them to `.5, .5` would rotate
|
|
@@ -504,7 +504,7 @@ around the center of the texture.
|
|
|
|
|
|
```js
|
|
|
someTexture.center.set(.5, .5);
|
|
|
-someTexture.rotation = THREE.Math.degToRad(45);
|
|
|
+someTexture.rotation = THREE.Math.degToRad(45);
|
|
|
```
|
|
|
|
|
|
Let's modify the top sample above to play with these values
|
|
@@ -594,7 +594,7 @@ gui.add(new DegRadHelper(texture, 'rotation'), 'value', -360, 360)
|
|
|
```
|
|
|
|
|
|
The last thing to note about the example is that if you change `wrapS` or
|
|
|
-`wrapT` on the texture you must also set [`texture.needsUpdate`](Texture.needsUpdate)
|
|
|
+`wrapT` on the texture you must also set [`texture.needsUpdate`](Texture.needsUpdate)
|
|
|
so three.js knows to apply those settings. The other settings are automatically applied.
|
|
|
|
|
|
{{{example url="../threejs-textured-cube-adjust.html" }}}
|
|
@@ -606,7 +606,7 @@ to materials.
|
|
|
For now let's move on to [lights](threejs-lights.html).
|
|
|
|
|
|
<!--
|
|
|
-alpha
|
|
|
+alpha
|
|
|
ao
|
|
|
env
|
|
|
light
|
|
@@ -630,7 +630,7 @@ roughness
|
|
|
font-family: monospace;
|
|
|
font-size: small;
|
|
|
text-shadow:
|
|
|
- -1px -1px 0 #000,
|
|
|
+ -1px -1px 0 #000,
|
|
|
1px -1px 0 #000,
|
|
|
-1px 1px 0 #000,
|
|
|
1px 1px 0 #000;
|