从高度图三个js平滑地形

时间:2017-09-25 18:39:12

标签: javascript three.js

我目前正尝试使用来自Google图片的高度贴图中的three.js的PlaneBufferGeometry创建一些平滑地形:

https://forums.unrealengine.com/filedata/fetch?id=1192062&d=1471726925

但结果有点不稳定..

(对不起,这是我的第一个问题,显然我需要10个声望来发布图片,否则我会...但是这里更好一点:live demo!左键单击+拖动旋转,滚动到缩放)

我想,就像我说的那样,一个平滑的地形,所以我做错了什么,或者这只是结果,我之后需要以某种方式使其平滑?

这是我的代码:

const IMAGE_SRC = 'terrain2.png';
const SIZE_AMPLIFIER = 5;
const HEIGHT_AMPLIFIER = 10;

var WIDTH;
var HEIGHT;


var container = jQuery('#wrapper');
var scene, camera, renderer, controls;
var data, plane;

image();
// init();

function image() {
    var image = new Image();
    image.src = IMAGE_SRC;
    image.onload = function() {
        WIDTH = image.width;
        HEIGHT = image.height;

        var canvas = document.createElement('canvas');
        canvas.width = WIDTH;
        canvas.height = HEIGHT;
        var context = canvas.getContext('2d');


        console.log('image loaded');
        context.drawImage(image, 0, 0);
        data = context.getImageData(0, 0, WIDTH, HEIGHT).data;

        console.log(data);

        init();
    }
}

function init() {

    // initialize camera
    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, .1, 100000);
    camera.position.set(0, 1000, 0);

    // initialize scene
    scene = new THREE.Scene();

    // initialize directional light (sun)
    var sun = new THREE.DirectionalLight(0xFFFFFF, 1.0);
    sun.position.set(300, 400, 300);
    sun.distance = 1000;
    scene.add(sun);

    var frame = new THREE.SpotLightHelper(sun);
    scene.add(frame);

    // initialize renderer
    renderer = new THREE.WebGLRenderer();
    renderer.setClearColor(0x000000);
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    container.append(renderer.domElement);

    // initialize controls
    controls = new THREE.OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true;
    controls.dampingFactor = .05;
    controls.rotateSpeed = .1;

    // initialize plane
    plane = new THREE.PlaneBufferGeometry(WIDTH * SIZE_AMPLIFIER, HEIGHT * SIZE_AMPLIFIER, WIDTH - 1, HEIGHT - 1);
    plane.castShadow = true;
    plane.receiveShadow = true;

    var vertices = plane.attributes.position.array;
    // apply height map to vertices of plane
    for(i=0, j=2; i < data.length; i += 4, j += 3) {
        vertices[j] = data[i] * HEIGHT_AMPLIFIER;
    }

    var material = new THREE.MeshPhongMaterial({color: 0xFFFFFF, side: THREE.DoubleSide, shading: THREE.FlatShading});

    var mesh = new THREE.Mesh(plane, material);
    mesh.rotation.x = - Math.PI / 2;
    mesh.matrixAutoUpdate  = false;
    mesh.updateMatrix();

    plane.computeFaceNormals();
    plane.computeVertexNormals();

    scene.add(mesh);

    animate();
}

function animate() {
    requestAnimationFrame(animate);

    renderer.render(scene, camera);
    controls.update();
}

2 个答案:

答案 0 :(得分:1)

结果是锯齿状,因为高度贴图的颜色深度较低。我冒昧地为高度贴图的一部分着色(Photoshop中的油漆桶,0公差,非连续),这样你就可以自己看到具有相同颜色值的区域有多大,即相同的高度。

相同颜色的区域将在您的地形中形成高原。这就是为什么你的地形有高原和尖锐的台阶。

Colored height map

您可以做的是平滑几何的Z值或使用高度图,该高度图使用16位或事件32位作为高度信息。当前高度图仅使用8位,即256个值。

答案 1 :(得分:0)

你可以做的一件事就是从高度图中采样不仅仅是一个像素。现在,顶点索引直接对应于数据阵列中的像素位置。您只需更新图像中的z值即可。

for(i=0, j=2; i < data.length; i += 4, j += 3) {
  vertices[j] = data[i] * HEIGHT_AMPLIFIER;
}

相反,你可以这样做:

  • 获取具有沿x / y轴的某些偏移的多个样本
  • 计算样本的(加权)平均值

这样你就可以在相同高度的区域的边界处得到一些平滑。

第二种选择是使用类似模糊内核的东西(高斯模糊非常昂贵,但也许像快速的盒子模糊对你有用)。

由于您只使用一个字节,因此分辨率非常有限,您应首先将该图像转换为float32:

const highResData = new Float32Array(data.length / 4);
for (let i = 0; i < highResData.length; i++) {
  highResData[i] = data[4 * i] / 255;
}

现在数据采用的格式允许更高的数值分辨率,因此我们现在可以平滑。你可以为float32用例调整一些like the StackBlur,使用ndarraysndarray-gaussian-filter或者自己实现一些简单的东西。基本思路是找到均匀着色平台中所有值的平均值。

希望有所帮助,祝你好运:)