J’ai commencé cette année un projet de moteur de jeu, écrit en Kotlin. Le moteur de jeu se base sur la technologie du Raymarching, et l’approche utilisée permet des rendus et des calculs sur des objets définis en 3 ou 4 dimensions1.
Objet | Nombre de dimensions | Paramètres |
---|---|---|
Cube | 3 | taille sur les axes x, y, z |
Hyper cube | 4 | taille sur les axes x, y, z, w |
Hyper sphère | 4 | rayon |
J’ai implémenté un algorithme de raymarching, qui utilise une scène décrite par des SDFs2.
Voici une implémentation simple en GLSL, permettant d’envoyer un rayon :
float castRay(vec4 rayOrigin, vec3 rayDirection) {
float epsilon = 0.001;
float maxDistance = 100.0;
float distance = 0.0;
for (int i = 0; i < 1000; i++) {
vec3 position = rayOrigin + rayDirection * distance;
float objectDistance = map(position);
distance += objectDistance;
if (objectDistance < epsilon) {
return distance;
}
else if(distance >= maxDistance) {
return -1;
}
}
return -1;
}
A chaque itération, la fonction effectue plusieurs étapes :
1. Calcul de la nouvelle position temporaire, en fonction de la
direction du rayon et de la précédente distance,
2. Détermine la nouvelle distance à l’aide de la fonction
map(position)
, qui renvoie la distance entre la position
donnée et la scène,
3. Vérifie si la distance est inférieure à un epsilon donné, si oui, on
considère q’un objet et touché et on renvoie la distance calculée,
4. Vérifie si on n’excède pas une limite de distance.
La fonction renvoie -1 si le rayon n’intersecte avec aucun objet.
L’algorithme de raymarching est exécuté sur chaque pixel de l’écran,
chaque pixel est associé à une position et une direction dans la scène.
On peut convertir la position p du pixel (qui est dans un
espace normalisé entre 0 et 1 sur les axes x et y) en
direction d avec la relation suivante :
On utilise les constantes aspect ratio et fov qui sont respectivement le rapport largeur/hauteur de l’écran, et l’angle du champ de vision de la caméra.
Possédant des fonctions permettant de mesurer la distance entre un
point et un objet, il est facile de déterminer si deux objets sont en
collision et y répondre.
Voici un exemple de code présent dans le moteur permettant de savoir si
deux objets sont effectivement en collision :
fun detect() {
for (i in objectsPairs) {
val data = collisionData(i.first, i.second)
if (data.distance <= 0) {
// Si la distance est négative, les objets sont en collision
.add(data)
collidingObjetcs}
}
}
Afin de repousser deux objets entre eux dans la direction n,
on reflète les vecteurs normaux a et b
résultant de la collision en utilisant la relation suivante :