最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Mipmap a planet in three.js? - Stack Overflow

programmeradmin3浏览0评论

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: .html

I also saw this:

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 prise 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 puter decide which is appropiate depending on how far you are? Not sure how this mipmapping works.

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/examples/webgl_materials_texture_manualmipmap.html

I also saw this: http://threejs/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 prise 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 puter decide which is appropiate depending on how far you are? Not sure how this mipmapping works.

Share Improve this question edited Dec 3, 2015 at 4:35 user128511 asked Dec 2, 2015 at 10:56 AspergerAsperger 3,22211 gold badges59 silver badges106 bronze badges 3
  • mipmapping covered here but I don't think it's going to be enough for your problem – user128511 Commented Dec 3, 2015 at 4:53
  • MIP mapping is used to convert 1 texture into many different resolutions basically to improve the look of a texture at different viewing distances. Don't think it's what you are looking for here. You'll have to code that manually. – beiller Commented Dec 3, 2015 at 23:20
  • I wonder if this question should be edited so it does not have "planet" in the title as it appears to be superfluous. – Steven Lu Commented Jul 22, 2016 at 15:07
Add a ment  | 

1 Answer 1

Reset to default 19 +150

Mipmapping

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.

How This Affects You in Three.js

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.

Example Breakdown

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.

Bonus: Anisotropy

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)?

The Result

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:

  • The top left (no mipmapping) has an extremely noticeable moire pattern as you tilt the camera up. This is why we need mipmapping!
  • The top right (mipmapping) looks better but is still blurry off in the distance. Why is that?
  • The bottom left (colored mipmap) shows us why. The blurring is caused by all the mipmaps being interpolated together by the linear filter. As we get farther away from the camera, the smaller images are used, indicated by color.
  • The bottom right (anisotropic filtering) should look the best, making textures look crisp regardless of how far away they are.
发布评论

评论列表(0)

  1. 暂无评论