3 votes

terrain lisse à partir de la carte des hauteurs trois js

J'essaie actuellement de créer des lisse terrain utilisant le PlaneBufferGeometry de three.js à partir d'une carte de hauteur que j'ai obtenue de Google Images :

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

mais le résultat est un peu haché

(Désolé, c'est ma première question et, de toute évidence, j'ai besoin d'une réputation de 10 pour poster des images, sinon je le ferais mais voici une chose encore meilleure : une Démonstration en direct ! clic gauche + glisser pour faire pivoter, défiler pour zoomer)

Je veux, comme je l'ai dit, un terrain lisse, alors est-ce que je fais quelque chose de mal ou est-ce que c'est juste le résultat et je dois le lisser après d'une manière ou d'une autre ?

Voici également mon code :

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

1voto

Matey Points 952

Le résultat est irrégulier car la carte de hauteur a une faible profondeur de couleur. J'ai pris la liberté de colorer une partie de la carte de hauteur (Paint bucket dans Photoshop, tolérance 0, non continu) pour que vous puissiez voir par vous-même la taille des zones qui ont la même valeur de couleur, c'est-à-dire la même hauteur.

Les zones de la même couleur créeront un plateau dans votre terrain. C'est pourquoi il y a des plateaux et des marches prononcées dans votre terrain.

Colored height map

Ce que vous pouvez faire, c'est lisser les valeurs Z de la géométrie ou utiliser une carte de hauteur qui utilise 16 ou 32 bits pour les informations de hauteur. La carte de hauteur actuelle n'utilise que 8 bits, soit 256 valeurs.

0voto

Martin Schuhfuß Points 677

Pour lisser un peu les choses, vous pourriez échantillonner plus d'un pixel de la carte de hauteur. Actuellement, les indices de sommet correspondent directement à la position du pixel dans le tableau de données. Et vous mettez juste à jour la valeur z de l'image.

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

Au lieu de cela, vous pourriez faire des choses comme ça :

  • obtenir des échantillons multiples avec certains décalages le long des axes x/y
  • calculer une valeur moyenne (pondérée) à partir des échantillons

De cette façon, vous obtiendrez un certain lissage aux limites des zones de même hauteur.

La deuxième option est d'utiliser quelque chose comme un noyau de flou (le flou gaussien est horriblement cher, mais quelque chose comme un flou de boîte rapide pourrait fonctionner pour vous).

Comme la résolution est très limitée du fait de l'utilisation d'un seul octet, vous devez d'abord convertir cette image en float32 :

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

Les données sont maintenant dans un format qui permet une résolution numérique bien plus élevée, nous pouvons donc les lisser. Vous pouvez soit ajuster quelque chose comme le StackBlur pour le cas d'utilisation de float32, utilisez ndarrays y ndarray-gaussian-filter ou mettez en place quelque chose de simple vous-même. L'idée de base est de trouver une valeur moyenne pour toutes les valeurs dans ces plateaux uniformément colorés.

J'espère que cela vous aidera, bonne chance :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X