I. Introduction▲
Cette série explique comment générer de manière procédurale des continents dans Unity.
Vous pouvez retrouver les autres épisodes de cette série dans le sommaire dédié.
II. Vidéo▲
Unity - Génération procédurale de terrain - Correction des trous
III. Résumé▲
Les morceaux de terrain ayant un niveau de détail différent ne s'alignent pas parfaitement. Par conséquent, le terrain peut avoir des trous. Ce dernier chapitre de la série apporte une solution à ce problème.
III-A. Implémentation▲
Pour régler les soucis, les morceaux de terrain auront leur bordure avec le niveau de détail maximal.
Toutefois, cette solution rend visible la bordure lorsque les morceaux de terrains sont du plus bas niveau de détail. En effet, les bordures auront un niveau de détail bien plus grand que le reste de la surface. Par contre, c'est négligeable car cela ne sera visible que sur des morceaux très loin du joueur.
Pour implémenter cette correction, des sommets supplémentaires seront générés, en bordure du modèle (comme cela l'a été pour les normales). Le premier changement consiste à modifier le calcul du nombre de sommets par ligne :
public
int
numVertsPerLine {
get
{
return
supportedChunkSizes [(
useFlatShading) ?
flatshadedChunkSizeIndex :
chunkSizeIndex]
+
5
;
}
}
Ensuite, il est nécessaire de modifier la fonction MeshGenerator::GenerateTerrainChunk() pour modifier la génération des sommets. La première étape est de clarifier le code en supprimant les variables borderedSize, meshSize, meshSizeSimplified et verticesPerLine. Ces variables sont remplacées par :
int
numVertsPerLine =
meshSettings.
numVertsPerLine;
La variable meshSimplificationIncrement est renommée skipIncrement, IsBorderVertex est renommée isOutOfMeshVertex et borderVertexIndex devient outOfMeshVertexIndex.
Ensuite, il faut trouver une formule pour savoir si le sommet est à ignorer ou non :
bool
isSkippedVertex =
x >
2
&&
x <
numVertsPerLine -
3
&&
y >
2
&&
y <
numVertsPerLine -
3
&&
((
x -
2
) %
skipIncrement !=
0
||
(
y -
2
) %
skipIncrement !=
0
);
D'origine, l'incrément utilisé pour les boucles de parcours des sommets est calculé grâce au niveau de détail. Maintenant, l'incrément est de 1..
Ensuite, il faut déterminer un procédé pour savoir quel sommet fait partie de quel sous- ensemble. De plus, il faut déterminer s'il y a besoin de construire un triangle pour tel ou tel sommet :
bool
isOutOfMeshVertex =
y ==
0
||
y ==
numVertsPerLine -
1
||
x ==
0
||
x ==
numVertsPerLine -
1
;
bool
isMeshEdgeVertex =
(
y ==
1
||
y ==
numVertsPerLine -
2
||
x ==
1
||
x ==
numVertsPerLine -
2
) &&
!
isOutOfMeshVertex;
bool
isMainVertex =
(
x -
2
) %
skipIncrement ==
0
&&
(
y -
2
) %
skipIncrement ==
0
&&
!
isOutOfMeshVertex &&
!
isMeshEdgeVertex;
bool
isEdgeConnectionVertex =
(
y ==
2
||
y ==
numVertsPerLine -
3
||
x ==
2
||
x ==
numVertsPerLine -
3
) &&
!
isOutOfMeshVertex &&
!
isMeshEdgeVertex &&
!
isMainVertex;
bool
createTriangle =
x <
numVertsPerLine -
1
&&
y <
numVertsPerLine -
1
&&
(!
isEdgeConnectionVertex ||
(
x !=
2
&&
y !=
2
));
L'utilisation d'un brouillon est plus que recommandé pour résoudre un tel problème et trouver les bonnes formules.
Évidemment, il va falloir revoir la méthode pour prendre les bons sommets pour construire les triangles :
int
currentIncrement =
(
isMainVertex &&
x !=
numVertsPerLine -
3
&&
y !=
numVertsPerLine -
3
) ?
skipIncrement :
1
;
int
a =
vertexIndicesMap [
x,
y];
int
b =
vertexIndicesMap [
x +
currentIncrement,
y];
int
c =
vertexIndicesMap [
x,
y +
currentIncrement];
int
d =
vertexIndicesMap [
x +
currentIncrement,
y +
currentIncrement];
meshData.
AddTriangle (
a,
d,
c);
meshData.
AddTriangle (
d,
a,
b);
En outre, il faut revoir la création des tableaux contenant les sommets et les triangles :
public
MeshData
(
int
numVertsPerLine,
int
skipIncrement,
bool
useFlatShading) {
this
.
useFlatShading =
useFlatShading;
int
numMeshEdgeVertices =
(
numVertsPerLine -
2
) *
4
-
4
;
int
numEdgeConnectionVertices =
(
skipIncrement -
1
) *
(
numVertsPerLine -
5
) /
skipIncrement *
4
;
int
numMainVerticesPerLine =
(
numVertsPerLine -
5
) /
skipIncrement +
1
;
int
numMainVertices =
numMainVerticesPerLine *
numMainVerticesPerLine;
vertices =
new
Vector3[
numMeshEdgeVertices +
numEdgeConnectionVertices +
numMainVertices];
uvs =
new
Vector2[
vertices.
Length];
int
numMeshEdgeTriangles =
8
*
(
numVertsPerLine -
4
);
int
numMainTriangles =
(
numMainVerticesPerLine -
1
) *
(
numMainVerticesPerLine -
1
) *
2
;
triangles =
new
int
[(
numMeshEdgeTriangles +
numMainTriangles) *
3
];
outOfMeshVertices =
new
Vector3[
numVertsPerLine *
4
-
4
];
outOfMeshTriangles =
new
int
[
24
*
(
numVertsPerLine-
2
)];
}
Pour que la connexion soit correcte entre les niveaux de détail, il faut repositionner les sommets à l'intersection des deux niveaux de détail :
if
(
isEdgeConnectionVertex) {
bool
isVertical =
x ==
2
||
x ==
numVertsPerLine -
3
;
int
dstToMainVertexA =
((
isVertical)?
y -
2
:
x-
2
) %
skipIncrement;
int
dstToMainVertexB =
skipIncrement -
dstToMainVertexA;
float
dstPercentFromAToB =
dstToMainVertexA /
(
float
)skipIncrement;
float
heightMainVertexA =
heightMap [(
isVertical) ?
x :
x -
dstToMainVertexA,
(
isVertical) ?
y -
dstToMainVertexA :
y];
float
heightMainVertexB =
heightMap [(
isVertical) ?
x :
x +
dstToMainVertexB,
(
isVertical) ?
y +
dstToMainVertexB :
y];
height =
heightMainVertexA *
(
1
-
dstPercentFromAToB) +
heightMainVertexB *
dstPercentFromAToB;
}
IV. Code source▲
Vous pouvez consulter le code de ce chapitre sur le GitHub de Sebastian Lague.
V. Commenter▲
Vous pouvez commenter et donner vos avis dans la discussion associée sur le forum.