【问题标题】:Mipmap a planet in three.js?在 three.js 中 Mipmap 一个星球?
【发布时间】:2016-03-06 14:09:56
【问题描述】:

所以我最近了解了 mipmapping 的定义,但我不确定如何在 three.js 中正确使用该技术。

我看过这个例子: http://threejs.org/examples/webgl_materials_texture_manualmipmap.html

我也看到了这个: http://threejs.org/examples/#webgl_materials_texture_anisotropy

两者似乎都使用 mipmapping。第一个例子有这段代码:

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

不清楚的是:

textureCanvas1.mipmaps[ 1 ] = mipmap( 64, '#0f0' );

以及二维上下文的使用。

无论如何,鉴于示例的性质,我仍然不知道如何对行星进行 mipmap。所以是的,我不确定如何正确映射球体。首先,我需要我的行星/球体由单独的部分组成,以便我可以将分解纹理的不同部分放在球体的每个部分上。然后我创建了 2 种尺寸变化的幂,然后呢?

所以我的问题是,当用于立方体、球体等时,three.js 中的 mipmapping 看起来如何?非常感谢简化的演示,因为现有示例(很少见)似乎都过于臃肿或没有文档。

编辑:stackoverflow 中的另一个用户发布了这个:

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;
};

mipmaps 的关键似乎是 texture.mipmaps[]。在这里,这个人只指定了一张图片。我们不应该提供各种图像并让计算机根据您的距离来决定哪个合适吗?不确定这个 mipmapping 是如何工作的。

【问题讨论】:

  • mipmapping 覆盖了here,但我认为这不足以解决您的问题
  • MIP 映射用于将 1 个纹理转换为许多不同的分辨率,基本上是为了改善纹理在不同观看距离下的外观。不要认为这是你在这里寻找的东西。您必须手动编写代码。
  • 我想知道是否应该编辑这个问题,使其标题中没有“行星”,因为它似乎是多余的。

标签: javascript three.js


【解决方案1】:

微映射

Mipmapping 是一种纹理渲染技术,您可以在每个纹理的基础上应用它。它的基本要点是,当启用 mipmapping 时,GPU 将使用较小版本的纹理来渲染表面,具体取决于表面与相机的距离。

为了使用 mipmapping,您需要为您的纹理设置一组 mipmap; mipmap 是纹理的较小版本。您可以自己提供这些 mipmap,在过去您可能不得不提供这些 mipmap,但是使用最近的图形 API (OpenGL >= 3.0),它们可以自动生成。 如果您所做的只是将基本纹理贴图应用到球体表面,那么您不太可能需要生成自己的 mipmap。

Mipmapping 与您正在纹理化的对象的 3D 形状没有任何关系。无论您是将纹理应用于立方体、球体还是任何其他模型,作为程序员启用 mipmapping 所需采取的步骤都是相同的。您不需要启用 mipmapping 来渲染纹理,尽管它可能会使您的纹理看起来更漂亮。

这对 Three.js 中的你有何影响

默认情况下,在three.js 中,您无需执行任何操作即可为您的纹理生成mipmap。参考Texture的three.js文档,有一个generateMipmaps属性控制mipmaps的自动生成,默认为true。此功能在渲染器here 中实现。这意味着获得 mipmapped 纹理所需的最低限度是:

var texture1 = THREE.ImageUtils.loadTexture("surface.png");
// our mipmaps will generate automatically now!

还有一个 mipmaps 属性可以手动填充 mipmap 图像,如您提供的示例中所示。奇怪的是,一个未记录的特性是,如果这个数组不为空,它会禁用自动 mipmap 生成。您可以查看该here 的来源。

示例细分

在带有瓷砖地板上绘画的first example 中,mipmap() 函数在 HTML 画布上绘制 2D 纹理。它负责绘制您在地平面上看到的平铺纹理。然后将这些纹理加载为 mipmap,将它们放入 mipmaps 数组中,以便它们可以通过 three.js 以 3D 形式呈现。

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' );

你注意到每个连续的 mipmap 是如何小两倍的吗?起始纹理(我们必须放入 mipmaps[0])是 128x128,第二个是 64x64,第三个是 32x32,依此类推。颜色(#0f0#00f#400 等)是导致瓷砖出现奇怪彩虹效果的原因。它们用不同的颜色来说明不同 mipmap 的边缘。

奖励:各向异性

second example 用于展示一种称为各向异性过滤的效果,这是对 mipmapping 的进一步增强;它根据与相机的距离到相机的视角来选择要使用的纹理大小。这可以使远处的纹理在远离相机倾斜时看起来更好。

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

您是否注意到左侧的箱子纹理 (texture1) 比右侧的 (texture2) 更加清晰且不那么模糊?

结果

我在plunker 中整理了一个更深入的示例,希望能更清楚地了解所有这些场景中发生的情况。一些注意事项:

  • 当您向上倾斜相机时,左上方(无 mipmapping)有一个非常明显的波纹图案。这就是我们需要 mipmapping 的原因!
  • 右上角(mipmapping)看起来更好,但远处仍然模糊。这是为什么呢?
  • 左下角(彩色mipmap)向我们展示了原因。模糊是由线性滤波器插值在一起的所有 mipmap 引起的。当我们离相机越来越远时,会使用较小的图像,用颜色表示。
  • 右下角(各向异性过滤)应该看起来最好,无论距离多远,纹理看起来都很清晰。

【讨论】:

  • 非常可爱的 Plunker 演示。我想知道如果有第五个视口来显示使用平面彩色 mip 级别的常规过滤和各向异性过滤之间的差异,这会不会很棒。
  • 我最初把它作为练习留给读者,但现在添加它可能是有意义的:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-03
  • 1970-01-01
  • 2013-12-19
相关资源
最近更新 更多