三维高度图中平滑地形的实现方法(基于three.js)

3

我目前正在尝试使用来自Google Images的高度图,使用three.js的PlaneBufferGeometry创建一些光滑的地形:

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

但结果有点崎岖不平。

(抱歉,这是我的第一个问题,显然我需要10个声望才能发布图片,否则我会...但这里有更好的东西:实时演示!左键单击+拖动以旋转,滚动以缩放)

我想要像我说的那样一个光滑的地形,所以我做错了什么还是这只是结果,我需要之后进行某种平滑处理?

此外,这是我的代码:

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

正如Matey所回答的那样,使用您的方法可以获得256个灰度值的更好结果。但是这已足以获得良好的分辨率,而您的模型目前是一个多边形怪物。使用更复杂的方法,您可以用较少的多边形获得更好的结果。提示:DCEL数据结构:https://en.wikipedia.org/wiki/Doubly_connected_edge_list,Delaunay三角剖分:https://en.wikipedia.org/wiki/Delaunay_triangulation...祝你好运 :) - user1501157
2个回答

1
结果不平滑是因为高度图的颜色深度较低。我自行对高度图进行了着色(在Photoshop中使用油漆桶工具,容差为0,非连续),这样您就可以看到具有相同颜色值(即相同高度)的区域有多大了。
相同颜色的区域会在地形中形成高原。这就是为什么您的地形会有高原和陡峭的台阶。

Colored height map

你可以将几何体的Z值平滑处理,或者使用一个高度图,该高度图利用16位甚至32位的高度信息。当前的高度图仅使用8位,即256个值。

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 情况调整 类似 StackBlur 的内容,使用ndarraysndarray-gaussian-filter 或者自己实现一些简单的东西。基本思路是找到这些均匀着色平台上所有值的平均值。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接