three.js开发我的世界minecraft(一)地形的生成

版权声明:本文为作者的原创文章,未经允许不得转载。 https://blog.csdn.net/lin5165352/article/details/83965754

我的世界minecraft地形生成

官网提供了一个minecraft案例:examples/webgl_geometry_minecraft.html

生成的效果,中间是一个直径10的小球,我作为参考用的,这里面的立方体在three.js中的单位是100。

案例中的地形生成,主要是采用Perlin Noise随机生成。也可以采用灰度图来生成。难点是理解Perlin Noise算法。

Perlin Noise (柏林噪声)

柏林噪声是由Ken Perlin于1983年提出的一种梯度噪声(Gradient Noise,通常由计算机模拟得到的一组噪声,相较于传统的离散数值噪声value noise要更加连续平滑)他在1985年的SIGGRAPH会议上,做了一场以“An Image Synthesizer”为题的学术报告,正式提出他的这一发现。

      柏林噪声的应用非常广泛: 合成地形高度图、生成物体表面的复杂纹理、火焰烟雾特效、波动效果的模拟等等。
 Ken Perlin的个人主页:http://mrl.nyu.edu/~perlin/(纽约大学-媒体研究实验室 nyu Media Research Lab)

看了一上午,这个perlin noise 的坑有点大,还是直接跳过,不跳进去了,免得出不来。泪奔啊,这几天每天上午写bug,下午找bug,一天时间就浪费了。

基本单位立方体

我的世界,是由“像素块”组成的,以一个立方体为单位。我想以立方体为基本模型单位来生成整个地形。但是研究官方的代码之后,发现人家更高明。一个立方体是有5个面组成的,底部的面直接省略掉,牛。

案例中修改了uv的坐标,这样的好处是使用一张图就可以贴出不同的效果。下面是未修改uv的贴图情况。关于attributes.uv.array在文档中没有找到说明。自己的理解就是 平面有四个顶点,对于的uv坐标是8个值,存在数组中,顺序是上左,上右,下左,下右,先x后y。这样就可以生成侧面和顶面的纹理了。

 原贴图文件很小,16X32像素。放大后呈现方块化效果,正符合我的世界的风格。主要是设置了magFilter属性,指定了纹理的放大方式,nearestFilter最邻近过滤。

// 右边
var pxGeometry = new THREE.PlaneBufferGeometry( 100, 100 );
pxGeometry.attributes.uv.array[ 1 ] = 0.5;
pxGeometry.attributes.uv.array[ 3 ] = 0.5;
pxGeometry.rotateY( Math.PI / 2 );
pxGeometry.translate( 50, 0, 0 );
//左边
var nxGeometry = new THREE.PlaneBufferGeometry( 100, 100 );
nxGeometry.attributes.uv.array[ 1 ] = 0.5;
nxGeometry.attributes.uv.array[ 3 ] = 0.5;
nxGeometry.rotateY( - Math.PI / 2 );
nxGeometry.translate( - 50, 0, 0 );
//上面
var pyGeometry = new THREE.PlaneBufferGeometry( 100, 100 );
pyGeometry.attributes.uv.array[ 5 ] = 0.5;
pyGeometry.attributes.uv.array[ 7 ] = 0.5;
pyGeometry.rotateX( - Math.PI / 2 );
pyGeometry.translate( 0, 50, 0 );
//前面
var pzGeometry = new THREE.PlaneBufferGeometry( 100, 100 );
pzGeometry.attributes.uv.array[ 1 ] = 0.5;
pzGeometry.attributes.uv.array[ 3 ] = 0.5;
pzGeometry.translate( 0, 0, 50 );
//后面
var nzGeometry = new THREE.PlaneBufferGeometry( 100, 100 );
nzGeometry.attributes.uv.array[ 1 ] = 0.5;
nzGeometry.attributes.uv.array[ 3 ] = 0.5;
nzGeometry.rotateY( Math.PI );
nzGeometry.translate( 0, 0, -50 );

地形生成

部分代码,先定义了地图的长宽。然后是生成的noise数组,放在data中。

var worldWidth = 128, worldDepth = 128,
    worldHalfWidth = worldWidth / 2, worldHalfDepth = worldDepth / 2,
    data = generateHeight( worldWidth, worldDepth );
function generateHeight( width, height ) {
    var data = [], perlin = new ImprovedNoise(),
        size = width * height, quality = 2, z = Math.random() * 100;
    for ( var j = 0; j < 4; j ++ ) {
        if ( j === 0 ) for ( var i = 0; i < size; i ++ ) data[ i ] = 0;
        for ( var i = 0; i < size; i ++ ) {
            var x = i % width, y = ( i / width ) | 0;
            data[ i ] += perlin.noise( x / quality, y / quality, z ) * quality;
        }
        quality *= 4;
    }
    return data;
}
function getY( x, z ) {

    return ( data[ x + z * worldWidth ] * 0.2 ) | 0;

}

var matrix = new THREE.Matrix4();
var geometries = [];
for ( var z = 0; z < worldDepth; z ++ ) {
    for ( var x = 0; x < worldWidth; x ++ ) {
        var h = getY( x, z );
        matrix.makeTranslation(
            x * 100 - worldHalfWidth * 100,
            h * 100,
            z * 100 - worldHalfDepth * 100
        );
        var px = getY( x + 1, z );
        var nx = getY( x - 1, z );
        var pz = getY( x, z + 1 );
        var nz = getY( x, z - 1 );
        geometries.push( pyGeometry.clone().applyMatrix( matrix ) );
        if ( ( px !== h && px !== h + 1 ) || x === 0 ) {
            geometries.push( pxGeometry.clone().applyMatrix( matrix ) );
        }
        if ( ( nx !== h && nx !== h + 1 ) || x === worldWidth - 1 ) {
            geometries.push( nxGeometry.clone().applyMatrix( matrix ) );
        }
        if ( ( pz !== h && pz !== h + 1 ) || z === worldDepth - 1 ) {
            geometries.push( pzGeometry.clone().applyMatrix( matrix ) );
        }
        if ( ( nz !== h && nz !== h + 1 ) || z === 0 ) {
            geometries.push( nzGeometry.clone().applyMatrix( matrix ) );
        }
    }
}

var geometry = THREE.BufferGeometryUtils.mergeBufferGeometries( geometries );
geometry.computeBoundingSphere();
var texture = new THREE.TextureLoader().load( 'minecraft/atlas.png' );
texture.magFilter = THREE.NearestFilter;

var mesh = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { map: texture, side: THREE.DoubleSide } ) );
scene.add( mesh );

猜你喜欢

转载自blog.csdn.net/lin5165352/article/details/83965754
今日推荐