Get the Mytodolist Free Android app from SlideME.

jeudi 17 juillet 2014

10/ Comment créer un paysage en 3D sur JMonkeyEngine

Pour créer un paysage 3D il faut sculpter un modèle de terrain énorme. Cela vous donne une grande liberté artistique - mais le rendu d'un tel modèle énorme peut être assez lente. Ce tutoriel explique comment créer un rendu rapide de terrains de cartes de hauteur, et comment utiliser la texture splatting pour faire bien apparaître le terrain.



Remarque: Si vous obtenez une erreur lorsque vous essayez de créer votre objet ImageBasedHeightMap, vous devrez peut-être mettre à jour le SDK, cliquez sur "Aide" / "Vérifier les mises à jour"

Code d'exemples

package jme3test.helloworld;
 
import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.renderer.Camera;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
import com.jme3.terrain.heightmap.HillHeightMap; // for exercise 2
import com.jme3.terrain.heightmap.ImageBasedHeightMap;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import java.util.ArrayList;
import java.util.List;
 
/** Sample 10 - How to create fast-rendering terrains from heightmaps,
and how to use texture splatting to make the terrain look good.  */
public class HelloTerrain extends SimpleApplication {
 
  private TerrainQuad terrain;
  Material mat_terrain;
 
  public static void main(String[] args) {
    HelloTerrain app = new HelloTerrain();
    app.start();
  }
 
  @Override
  public void simpleInitApp() {
    flyCam.setMoveSpeed(50);
 
    /** 1. Create terrain material and load four textures into it. */
    mat_terrain = new Material(assetManager, 
            "Common/MatDefs/Terrain/Terrain.j3md");
 
    /** 1.1) Add ALPHA map (for red-blue-green coded splat textures) */
    mat_terrain.setTexture("Alpha", assetManager.loadTexture(
            "Textures/Terrain/splat/alphamap.png"));
 
    /** 1.2) Add GRASS texture into the red layer (Tex1). */
    Texture grass = assetManager.loadTexture(
            "Textures/Terrain/splat/grass.jpg");
    grass.setWrap(WrapMode.Repeat);
    mat_terrain.setTexture("Tex1", grass);
    mat_terrain.setFloat("Tex1Scale", 64f);
 
    /** 1.3) Add DIRT texture into the green layer (Tex2) */
    Texture dirt = assetManager.loadTexture(
            "Textures/Terrain/splat/dirt.jpg");
    dirt.setWrap(WrapMode.Repeat);
    mat_terrain.setTexture("Tex2", dirt);
    mat_terrain.setFloat("Tex2Scale", 32f);
 
    /** 1.4) Add ROAD texture into the blue layer (Tex3) */
    Texture rock = assetManager.loadTexture(
            "Textures/Terrain/splat/road.jpg");
    rock.setWrap(WrapMode.Repeat);
    mat_terrain.setTexture("Tex3", rock);
    mat_terrain.setFloat("Tex3Scale", 128f);
 
    /** 2. Create the height map */
    AbstractHeightMap heightmap = null;
    Texture heightMapImage = assetManager.loadTexture(
            "Textures/Terrain/splat/mountains512.png");
    heightmap = new ImageBasedHeightMap(heightMapImage.getImage());
    heightmap.load();
 
    /** 3. We have prepared material and heightmap. 
     * Now we create the actual terrain:
     * 3.1) Create a TerrainQuad and name it "my terrain".
     * 3.2) A good value for terrain tiles is 64x64 -- so we supply 64+1=65.
     * 3.3) We prepared a heightmap of size 512x512 -- so we supply 512+1=513.
     * 3.4) As LOD step scale we supply Vector3f(1,1,1).
     * 3.5) We supply the prepared heightmap itself.
     */
    int patchSize = 65;
    terrain = new TerrainQuad("my terrain", patchSize, 513, heightmap.getHeightMap());
 
    /** 4. We give the terrain its material, position & scale it, and attach it. */
    terrain.setMaterial(mat_terrain);
    terrain.setLocalTranslation(0, -100, 0);
    terrain.setLocalScale(2f, 1f, 2f);
    rootNode.attachChild(terrain);
 
    /** 5. The LOD (level of detail) depends on were the camera is: */
    TerrainLodControl control = new TerrainLodControl(terrain, getCamera());
    terrain.addControl(control);
  }
}
Lorsque vous exécutez cet exemple, vous devriez voir un paysage avec des montagnes de terre, les plaines d'herbe, ainsi que quelques routes sinueuses entre les deux.

Qu'est-ce qu'un Heightmap?



Heightmaps représente le moyen le plus efficace de représenter la un paysage accidenté en 3D. Chaque pixel du paysage n'est pas enrégistré,  à la place, on utilise une grille de valeurs d'échantillons pour décrire la hauteur du terrain à certains endroits. Les hauteurs entre les échantillons sont interpolées.

         En Java, une carte d'altitude est un réseau de flotteurs contenant des valeurs de hauteur entre 0f et 255f. Voici un exemple très simple d'un terrain créé à partir d'une carte d'altitude 5x5 = 25 valeurs de hauteur.


Choses importantes à noter:

  • Les faibles valeurs (par exemple 0 ou 50) sont des vallées.
  • Des valeurs élevées (par exemple 200, 255) sont des collines.
  • Heightmap ne définit que quelques points, et le moteur interpole le reste. L'interpolation est plus efficace que la création d'un modèle 3D avec plusieurs millions de sommets.

Lorsque l'on regarde les types de données en java pour stocker les valeurs dans un tableau de nombre flottante entre 0 et 255, la classe Image vient à l'esprit. Le stockage des valeurs de hauteur d'un terrain comme une image en niveaux de gris a un gros avantage: le résultat est un très convivial, comme une carte topographique:
  • Les faibles valeurs (par exemple 0 ou 50) sont gris foncé - ce sont les vallées.
  • Des valeurs élevées (par exemple 200, 255) sont gris clairs - ce sont des collines.

Regardez la capture d'écran suivante: En haut à gauche, vous voyez une image en niveaux de gris 128x128 (heightmap) qui a été utilisée comme base pour générer le terrain représenté. Pour que la forme de collines soit mieux visible, les sommets des montagnes doivent être de couleur blanche, les vallées brun, et les zones entre les deux, vert:


Dans un vrai jeu, vous voudrez utiliser des terrains plus complexes et plus lisses que les heightmaps simples présentés ici. Heightmaps ont généralement des tailles carrés de 512x512 ou 1024x1024, et contiennent des centaines de milliers à 1 million de valeurs de hauteur. Quelle que soit la taille, le concept est le même que celui décrit ici.

Reguardons le code d'Heightmap


La première étape de la création du relief est la carte d'altitude. Vous pouvez créer un vous-même dans toute application graphique standard. Assurez-vous qu'il a les propriétés suivantes: 

  • La taille doit être de forme carrée, et une puissance de deux. 
    • Exemples: 128x128, 256x256, 512x512, 1024x1024 
  • Le mode de couleur doit être de 255 niveaux de gris. 
  • Ne pas fournir une image en couleur, il sera interprété en niveaux de gris, avec des résultats éventuellement étranges. 
  • Enregistrer la carte comme un. Jpg ou un fichier image. Png 

Le fichier mountains512.png de fichiers que vous voyez ici est un exemple typique d'une carte d'altitude de l'image. 


Voici comment vous créez l'objet Heightmap dans votre code JME: 
  • Créez un objet de texture. 
  • Chargez votre image Heightmap préparé dans l'objet de texture. 
  • Créer un objet à partir d'un AbstractHeightmap ImageBasedHeightMap. 
  • Il nécessite une image à partir d'une texture JME. 
  • Chargez le Heightmap.

AbstractHeightMap heightmap = null;
    Texture heightMapImage = assetManager.loadTexture(
            "Textures/Terrain/splat/mountains512.png");
    heightmap = new ImageBasedHeightMap(heightMapImage.getImage());
    heightmap.load();

Qu'est ce que la texture Splatting? 



Auparavant, vous avez appris à créer un matériel pour une forme simple comme un cube. Toutes les faces du cube ont la même couleur. Vous pouvez appliquer le même matériel à un terrain alors que vous avez une grande prairie, un grand désert de roche, etc Ce n'est pas toujours ce que vous voulez. 

Texture splatting vous permet de créer un matériel personnalisé, et "peindre" les textures sur elle comme si vous avez un "pinceau". Ceci est très utile pour les terrains: Comme vous le voyez dans l'exemple ici, vous pouvez peindre une texture de l'herbe dans les vallées, une texture de saleté sur les montagnes et les routes de forme libre entre les deux.

Le SDK du moteur jmonkeyengine est livré avec le plugin TerrainEditor. Utiliser ce plugin pour sculpter le terrain avec la souris, et sauvegarder le résultat comme Heightmap. Vous pouvez peindre des textures sur le terrain et le plugin enregistre les textures de floc résultant comme alphamap (s). Les paragraphes suivants décrivent le processus manuel pour vous. Vous pouvez choisir de créer le terrain à la main, ou utiliser le plugin TerrainEditor.

Les textures Splat sont basés sur la définition de matériel Terrain.j3md. Si vous ouvrez le fichier Terrain.j3md, et regardez dans la section Paramètres de matériel, vous voyez que vous avez plusieurs couches de texture à peindre sur: Tex1, TEX2, Tex3, etc 

Avant de pouvoir commencer à peindre, vous devez prendre quelques décisions: 

  • Choisissez trois Les textures. Par exemple grass.jpg, dirt.jpg, et road.jpg. 




  • Vous gravez trois couches de texture en utilisant trois couleurs: rouge, bleu et vert. Vous décidez arbitrairement que:
    • Rouge est l'herbe - rouge est la couche Tex1, alors mettez la texture de l'herbe dans Tex1. 
    • Le vert est la saleté - vert est la couche Tex2, alors mettez la texture de la saleté dans Tex2. 
    • Le bleu est la route - bleu est une couche Tex3, alors mettez les routes texture en Tex3. 

Maintenant vous commencez à peindre la texture: 
  • Faites une copie de vos terrains Heightmap, mountains512.png. Vous voulez comme une référence pour la forme du paysage. 
  • Nommez la copie alphamap.png. 
  • Ouvrir alphamap.png dans un éditeur graphique et changer le mode d'image image couleur. 
    • Peindre les vallées noir rouge - ce sera l'herbe. 
    • Peindre le vert des collines blanches - ce sera la saleté des montagnes. 
    • Peindre des lignes bleues où vous voulez les routes.
  • Le résultat final devrait ressembler à ceci:

                 =>    

En regardant le code Texturing 


Comme d'habitude, vous créez un objet matériel. Baser sur la définition du matériel Terrain.j3md qui est inclus dans le cadre jME3.
Material mat_terrain = new Material(assetManager, "Common/MatDefs/Terrain/Terrain.j3md");
Chargez les quatre textures dans ce matériel. La première, Alpha, est l'alphamap que vous venez de créer.
mat_terrain.setTexture("Alpha",
    assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
Les trois autres textures sont les couches dont vous avez déjà décidé de la peinture: l'herbe, la saleté, et la route. Vous créez des objets textures et charger les trois textures comme d'habitude. Notez que vous affectez les couches de textures respectives (Tex1, TEX2, et Tex3) à l'intérieur du matériel!

  /** 1.2) Add GRASS texture into the red layer (Tex1). */
    Texture grass = assetManager.loadTexture(
            "Textures/Terrain/splat/grass.jpg");
    grass.setWrap(WrapMode.Repeat);
    mat_terrain.setTexture("Tex1", grass);
    mat_terrain.setFloat("Tex1Scale", 64f);
 
    /** 1.3) Add DIRT texture into the green layer (Tex2) */
    Texture dirt = assetManager.loadTexture(
            "Textures/Terrain/splat/dirt.jpg");
    dirt.setWrap(WrapMode.Repeat);
    mat_terrain.setTexture("Tex2", dirt);
    mat_terrain.setFloat("Tex2Scale", 32f);
 
    /** 1.4) Add ROAD texture into the blue layer (Tex3) */
    Texture rock = assetManager.loadTexture(
            "Textures/Terrain/splat/road.jpg");
    rock.setWrap(WrapMode.Repeat);
    mat_terrain.setTexture("Tex3", rock);
    mat_terrain.setFloat("Tex3Scale", 128f);
Les échelles individuelles de texture (par exemple mat_terrain.setFloat ("Tex3Scale", 128f);) dépendent de la taille des textures que vous utilisez. 

Vous pouvez dire que vous avez choisi une échelle trop petite si, par exemple, vos tuiles de la route apparaissent comme de minuscules grains de sable. 
Vous pouvez dire que vous avez choisi une trop grande échelle si, par exemple, les brins d'herbe ressemblent à des brindilles. 
Utilisez setWrap (WrapMode.Repeat) pour faire la petite texture remplir la vaste zone. Si la répétition est trop visible, essayez d'ajuster la valeur * Echelle de Tex respectif. 

Qu'est-ce qu'un terrain? 


En interne, le maillage du terrain généré est divisé en carreaux et blocs. Il s'agit d'une optimisation afin de rendre l'abattage plus facile. Vous n'avez pas besoin de trop s'inquiéter à propos de "tuiles et les blocs", il suffit d'utiliser les valeurs recommandées pour l'instant - 64 c'est un bon début. 

Supposons que vous souhaitez générer un terrain de 512x512. Vous avez déjà créé l'objet Heightmap. Voici les étapes que vous effectuez chaque fois que vous créez un nouveau terrain. 

Créer un TerrainQuad avec les arguments suivants: 

  • Indiquez un nom: par exemple mon terrain. 
  • Spécifiez la taille de tuile: Vous voulez un terrain carrellé de taille 64x64, si vous fournissez 64 +1 = 65. 
    • En général, 64 est une bonne valeur de départ pour les carreaux de terrain. 
  • Spécifiez la taille de bloc: Depuis que vous avez préparé un Heightmap de taille 512x512, vous fournissez 512 +1 = 513. 
    • Si vous fournissez une taille de bloc de 2x la taille de la carte d'altitude (1024 +1 = 1025), vous obtiendrez un étendu, plus large, un terrain plat. 
    • Si vous fournissez une taille de bloc de 1/2 la taille de la carte d'altitude (256 +1 = 257), vous obtenez un terrain plus petit, plus détaillée. 
  • Fournir l'objet de Heightmap 512x512 que vous avez créé. 

En regardant le code Terrain 

Voici le code:
terrain = new TerrainQuad(
  "my terrain",               // name
  65,                         // tile size
  513,                        // block size
  heightmap.getHeightMap());  // heightmap
Vous avez créer l'objet terrain
  • N'oubliez pas d'appliquer le matériel créé

terrain.setMaterial(mat_terrain);
  • N'oubliez pas d'attachez le terrain au noeud racide, rootNode

rootNode.attachChild(terrain);
Si nécessaire, faite l'échelle et la translation de l'objet terrain, comme n'importe quel autre territoire. 
Astuce: Terrain.j3md est la définition d'un matériel sans ombre, de sorte que vous n'avez pas besoin d'une source de lumière. Vous pouvez également utiliser TerrainLighting.j3md plus une lumière, si vous voulez un terrain ombragé. 

Quel est LOD (niveau de détail)? 

JME3 comprend une optimisation qui permet de régler le niveau de détail (LOD) du rendu du terrain selon que le terrain soit vue de près ou de loin par la caméra.

 TerrainLodControl control = new TerrainLodControl(terrain, getCamera());
    terrain.addControl(control);
Les parties proche du terrain sont rendus en détail. Les parties de terrain qui sont plus loin ne sont pas clairement visibles ainsi JME3 améliore les performances en les rendant moins détaillé. De cette façon vous pouvez vous permettre de charger d'énormes terrains sans peine causée par des détails invisibles. 

Conclusion 


Vous avez appris les techniques permettant de créer des terrains et qui sont plus efficaces que le chargement de modèle géant. Vous savez comment générer aléatoirement ou créer des heightmaps à la main. Vous pouvez ajouter un contrôle LOD pour charger de grands terrains plus rapidement. Vous êtes conscient que vous pouvez combiner ce que vous avez appris sur la détection de collision pour rendre le terrain solide pour un joueur physique. Vous êtes également capable de texturer un terrain "comme un patron" à l'aide de matériaux en couches et la texture splatting. Vous êtes conscient que le SDK du moteur jmonkeyengine fournit un TerrainEditor qui aide à la plupart de ces tâches manuelles. 

Voulez-vous écouter vos joueurs dirent "aïe!" quand ils se cognent dans un mur ou quand il tombe d'une colline? Continuer en apprenant comment jouer un son dans un jeu.


<< Précédent                                                           Sommaire                Suivant>>    

1 commentaire:

  1. Très interessant. Juste une petite remarque concernant la traduction "float" = "réel simple précision" et non "flotteur"
    Par contre je n'arrive pas à comprendre où sont les fichiers terrain.j3md et les fichiers textures.

    RépondreSupprimer