Reputation: 3222
So I recently learned about the definition of mipmapping but im unsure of how to properly use that technique within three.js.
I had a look at this example: http://threejs.org/examples/webgl_materials_texture_manualmipmap.html
I also saw this: http://threejs.org/examples/#webgl_materials_texture_anisotropy
Both seem to use mipmapping. The first example has this section of code:
function mipmap( size, color ) {
var imageCanvas = document.createElement( "canvas" ),
context = imageCanvas.getContext( "2d" );
imageCanvas.width = imageCanvas.height = size;
context.fillStyle = "#444";
context.fillRect( 0, 0, size, size );
context.fillStyle = color;
context.fillRect( 0, 0, size / 2, size / 2 );
context.fillRect( size / 2, size / 2, size / 2, size / 2 );
return imageCanvas;
}
var canvas = mipmap( 128, '#f00' );
var textureCanvas1 = new THREE.CanvasTexture( canvas );
textureCanvas1.mipmaps[ 0 ] = canvas;
textureCanvas1.mipmaps[ 1 ] = mipmap( 64, '#0f0' );
textureCanvas1.mipmaps[ 2 ] = mipmap( 32, '#00f' );
textureCanvas1.mipmaps[ 3 ] = mipmap( 16, '#400' );
textureCanvas1.mipmaps[ 4 ] = mipmap( 8, '#040' );
textureCanvas1.mipmaps[ 5 ] = mipmap( 4, '#004' );
textureCanvas1.mipmaps[ 6 ] = mipmap( 2, '#044' );
textureCanvas1.mipmaps[ 7 ] = mipmap( 1, '#404' );
textureCanvas1.repeat.set( 1000, 1000 );
textureCanvas1.wrapS = THREE.RepeatWrapping;
textureCanvas1.wrapT = THREE.RepeatWrapping;
var textureCanvas2 = textureCanvas1.clone();
textureCanvas2.magFilter = THREE.NearestFilter;
textureCanvas2.minFilter = THREE.NearestMipMapNearestFilter;
materialCanvas1 = new THREE.MeshBasicMaterial( { map: textureCanvas1 } );
materialCanvas2 = new THREE.MeshBasicMaterial( { color: 0xffccaa, map: textureCanvas2 } );
var geometry = new THREE.PlaneBufferGeometry( 100, 100 );
var meshCanvas1 = new THREE.Mesh( geometry, materialCanvas1 );
meshCanvas1.rotation.x = -Math.PI / 2;
meshCanvas1.scale.set(1000, 1000, 1000);
var meshCanvas2 = new THREE.Mesh( geometry, materialCanvas2 );
meshCanvas2.rotation.x = -Math.PI / 2;
meshCanvas2.scale.set( 1000, 1000, 1000 );
So unclear is:
textureCanvas1.mipmaps[ 1 ] = mipmap( 64, '#0f0' );
and the usage of a 2d context.
Either way, given the nature of the examples, I am still unaware how to mipmap a planet. So yes, I am unsure how to mitmap a sphere properly. First I would need my planet / sphere to comprise of seperate sections so that I can put the different pieces of the broken up texture on each of those sections of the sphere. Then I create power of 2 size variations but what then?
So my question is, how does mipmapping in three.js look like when used for cubes , spheres etc? A simplified demo would be very appreciated as the existing examples (which are rare) all seem either too bloated or undocumented.
EDIT: Another user in stackoverflow posted this:
var texture = THREE.ImageUtils.loadTexture( 'images/512.png', undefined, function() {
texture.repeat.set( 1, 1 );
texture.mipmaps[ 0 ] = texture.image;
texture.generateMipmaps = true;
texture.needsUpdate = true;
};
It seems the key to mipmaps is texture.mipmaps[]. Here the person only specified one image though. Shouldnt we serve various images and let the computer decide which is appropiate depending on how far you are? Not sure how this mipmapping works.
Upvotes: 4
Views: 5965
Reputation:
Mipmapping is a texture rendering technique that you apply on a per-texture basis. The basic gist of it is that when mipmapping is enabled, the GPU will use smaller versions of a texture to render a surface depending on how far away the surface is from the camera.
In order to use mipmapping, you need to have a set of mipmaps for your texture; the mipmaps are the smaller versions of your texture. You can provide these mipmaps yourself, and in olden days you might have had to, however with recent graphics APIs (OpenGL >= 3.0) they can be automatically generated. It is very unlikely you need to generate your own mipmaps if all you're doing is applying a basic texture map to the surface of a sphere.
Mipmapping does not have anything to do with the 3D shape of the object you are texturing. Whether you are applying the texture to a cube, sphere, or any other model, the steps you need to take as a programmer to enable mipmapping are going to be the same. You do not need to enable mipmapping to render textures, although it will probably make your textures look prettier.
By default in three.js you do not need to do anything to generate mipmaps for your textures. Referring to the three.js docs for Texture, there is a generateMipmaps
property that controls the automatic generation of mipmaps, and defaults to true. This feature is implemented in the renderer here. This means the bare minimum you need to do to get a mipmapped texture is this:
var texture1 = THREE.ImageUtils.loadTexture("surface.png");
// our mipmaps will generate automatically now!
There is also a mipmaps
property that can be manually populated with mipmap images, as in the example you provided. Curiously, an undocumented feature is that if this array is not empty, it disables automatic mipmap generation. You can see the source for that here.
In the first example with the painting on the tiled floor, the mipmap()
function draws a 2D texture on an HTML canvas. It is responsible for drawing the tiled texture you see on the ground plane. These textures are then loaded as mipmaps by putting them in the mipmaps
array, so that they can then be rendered in 3D by three.js.
var canvas = mipmap( 128, '#f00' );
var textureCanvas1 = new THREE.CanvasTexture( canvas );
// manually set up some mipmaps
textureCanvas1.mipmaps[ 0 ] = canvas;
textureCanvas1.mipmaps[ 1 ] = mipmap( 64, '#0f0' );
textureCanvas1.mipmaps[ 2 ] = mipmap( 32, '#00f' );
textureCanvas1.mipmaps[ 3 ] = mipmap( 16, '#400' );
textureCanvas1.mipmaps[ 4 ] = mipmap( 8, '#040' );
textureCanvas1.mipmaps[ 5 ] = mipmap( 4, '#004' );
textureCanvas1.mipmaps[ 6 ] = mipmap( 2, '#044' );
textureCanvas1.mipmaps[ 7 ] = mipmap( 1, '#404' );
Did you notice how each successive mipmap is twice as small? The starting texture (which we have to put in mipmaps[0]
) is 128x128, the second is 64x64, the third is 32x32, and so on. The colors (#0f0
, #00f
, #400
, etc) are what cause the weird rainbow effect on the tiles. They are colored differently to illustrate the edges of the different mipmaps.
The second example is used to show off an effect called anisotropic filtering, which is a further enhancement on top of mipmapping; it chooses the size of texture to use based on the distance from the camera and the viewing angle to the camera. This can make far-off textures look even nicer when they are tilted away from the camera.
var maxAnisotropy = renderer.getMaxAnisotropy();
var texture1 = THREE.ImageUtils.loadTexture( "textures/crate.gif" );
// no need to generate mipmaps here, we get them automatically!
texture1.anisotropy = maxAnisotropy;
texture1.wrapS = texture1.wrapT = THREE.RepeatWrapping;
texture1.repeat.set( 512, 512 );
Did you notice how the crate texture on the left (texture1
) is a lot sharper and less blurry than the one on the right (texture2
)?
I put together a more in-depth example in a plunker to hopefully make it a little clearer what is happening in all these scenarios. Some notes:
Upvotes: 19