Moteur 3d

Description

C'est un... moteur 3D !
Il fait un seul rendu bien évidemment et utilise les mêmes techniques de rendu software que les vrais moteurs. Pour l'instant, il affiche les arêtes d'un cube (mais on peut modifier et mettre autre chose qu'un cube) selon la position de la caméra que l'on peut définir en modifiant la source

J'ai fait cela pour prouver qu'il n'était pas si compliqué de faire un moteur 3D, même en php
Il utilise GD. J'ai fait ça pour me faciliter la vie... après on peut aussi faire un .bmp avec et ne pas utiliser GD

Le backbuffer ne fonctionne pas correctement, j'ai mis la partie qui utiliser le backbuffer en commentaire

Source / Exemple :


$width = 320;
$height = 240;

$clipping = 500;

$wireframe = false; // mode fil de fer

header("Content-type: image/jpeg");

function drawtriangle($im, $backbuffer, $pt1, $pt2, $pt3, $width, $height, $wireframe) {
	if ($pt1[1] > $pt2[1]) { $tmp = $pt2; $pt2 = $pt1; $pt1 = $tmp; }
	if ($pt1[1] > $pt3[1]) { $tmp = $pt3; $pt3 = $pt1; $pt1 = $tmp; }
	if ($pt2[1] > $pt3[1]) { $tmp = $pt3; $pt3 = $pt2; $pt2 = $tmp; }
	if ($pt1[1] > $pt2[1]) { $tmp = $pt2; $pt2 = $pt1; $pt1 = $tmp; }
	if ($pt1[1] > $pt3[1]) { $tmp = $pt3; $pt3 = $pt1; $pt1 = $tmp; }
	if ($pt2[1] > $pt3[1]) { $tmp = $pt3; $pt3 = $pt2; $pt2 = $tmp; }
	
	for ($i = 0; $i <= ($pt3[1] - $pt1[1]); ++$i) {
		if ($i + $pt1[1] > $height || $i + $pt1[1] < 0)
			continue;
	
		$num1 = $pt1[0] + $i * ($pt3[0] - $pt1[0]) / ($pt3[1] - $pt1[1]);
		$num1z = $pt1[2] + $i * ($pt3[2] - $pt1[2]) / ($pt3[1] - $pt1[1]);
		$num1r = $pt1[3] + $i * ($pt3[3] - $pt1[3]) / ($pt3[1] - $pt1[1]);
		$num1g = $pt1[4] + $i * ($pt3[4] - $pt1[4]) / ($pt3[1] - $pt1[1]);
		$num1b = $pt1[5] + $i * ($pt3[5] - $pt1[5]) / ($pt3[1] - $pt1[1]);
		
		if ($i > ($pt2[1] - $pt1[1])) {
			$num2 = $pt2[0] + ($pt3[0] - $pt2[0]) * ($i - ($pt2[1] - $pt1[1])) / ($pt3[1] - $pt2[1]);
			$num2z = $pt2[2] + ($pt3[2] - $pt2[2]) * ($i - ($pt2[1] - $pt1[1])) / ($pt3[1] - $pt2[1]);
			$num2r = $pt2[3] + ($pt3[3] - $pt2[3]) * ($i - ($pt2[1] - $pt1[1])) / ($pt3[1] - $pt2[1]);
			$num2g = $pt2[4] + ($pt3[4] - $pt2[4]) * ($i - ($pt2[1] - $pt1[1])) / ($pt3[1] - $pt2[1]);
			$num2b = $pt2[5] + ($pt3[5] - $pt2[5]) * ($i - ($pt2[1] - $pt1[1])) / ($pt3[1] - $pt2[1]);
		} else {
			$num2 = $pt1[0] + ($pt2[0] - $pt1[0]) * $i / ($pt2[1] - $pt1[1]);
			$num2z = $pt1[2] + ($pt2[2] - $pt1[2]) * $i / ($pt2[1] - $pt1[1]);
			$num2r = $pt1[3] + ($pt2[3] - $pt1[3]) * $i / ($pt2[1] - $pt1[1]);
			$num2g = $pt1[4] + ($pt2[4] - $pt1[4]) * $i / ($pt2[1] - $pt1[1]);
			$num2b = $pt1[5] + ($pt2[5] - $pt1[5]) * $i / ($pt2[1] - $pt1[1]);
		}
			
		if ($num1 > $num2) {
			$tmp = $num2;
			$num2 = $num1;
			$num1 = $tmp;
			
			$tmp = $num2z;
			$num2z = $num1z;
			$num1z = $tmp;
			
			$tmp = $num2r;
			$num2r = $num1r;
			$num1r = $tmp;
			
			$tmp = $num2g;
			$num2g = $num1g;
			$num1g = $tmp;
			
			$tmp = $num2b;
			$num2b = $num1b;
			$num1b = $tmp;
		}
		
		if ($num1 == $num2) $num2 = $num1 + 1;
		for ($j = $num1; $j <= $num2; $j += $wireframe?($num2 - $num1):1) {
			if ($j > $width || $j < 0) continue;
		
			$zcoord = $num1z + ($num2z - $num1z) * (($j - $num1) / ($num2 - $num1));
			/*if ($zcoord > ($backbuffer[$j + ($i + $pt1[1]) * $width]))
				continue;
			$backbuffer[$j + ($i + $pt1[1]) * $width] = $zcoord;*/
			
			$red = $num1r + ($num2r - $num1r) * (($j - $num1) / ($num2 - $num1));
			$green = $num1g + ($num2g - $num1g) * (($j - $num1) / ($num2 - $num1));
			$blue = $num1b + ($num2b - $num1b) * (($j - $num1) / ($num2 - $num1));
			
			imagesetpixel($im, $j, $i + $pt1[1], imagecolorallocate($im, $red, $green, $blue));
		}
	}
	
	return $backbuffer;
}

class mesh {
	var $points;
	var $triangle1;
	var $triangle2;
	var $triangle3;
	
	var $scale;
	var $translation;
	
	function render($matrix, $width, $height, $im, $backbuffer, $wireframe) {
		$matrix2 = multiply(multiply($matrix, $this->translation), $this->scale);
	
		for ($i = 0; $i < sizeof($this->triangle1); ++$i) {
			if (!isset($this->triangle1[$i])) continue;
			if (!isset($this->triangle2[$i])) continue;
			if (!isset($this->triangle3[$i])) continue;
			
			$pt1 = $this->points[$this->triangle1[$i]];
			$pt2 = $this->points[$this->triangle2[$i]];
			$pt3 = $this->points[$this->triangle3[$i]];
			
			$tmp1 = $pt1->calculate($matrix2);
			$tmp2 = $pt2->calculate($matrix2);
			$tmp3 = $pt3->calculate($matrix2);
			
			$tmp1[0] = ($tmp1[0] + 1) * $width / 2;
			$tmp1[1] = ($tmp1[1] + 1) * $height / 2;
			$tmp2[0] = ($tmp2[0] + 1) * $width / 2;
			$tmp2[1] = ($tmp2[1] + 1) * $height / 2;
			$tmp3[0] = ($tmp3[0] + 1) * $width / 2;
			$tmp3[1] = ($tmp3[1] + 1) * $height / 2;
			
			$backbuffer = drawtriangle($im, $backbuffer, $tmp1, $tmp2, $tmp3, $width, $height, $wireframe);
		}
	}
	
	function mesh() {
		$this->points = array();
		$this->triangle1 = array();
		$this->triangle2 = array();
		$this->triangle3 = array();
		
		$this->scale = new matrix();
		$this->scale->loadIdentity();
		$this->translation = new matrix();
		$this->translation->loadIdentity();
	}
};

class point {
	var $x;
	var $y;
	var $z;
	
	var $cr;
	var $cg;
	var $cb;
	
	function point($x = 0, $y = 0, $z = 0, $r = 255, $g = 255, $b = 255) {
		$this->x = $x;
		$this->y = $y;
		$this->z = $z;
		$this->cr = $r;
		$this->cg = $g;
		$this->cb = $b;
	}
	
	function calculate($matrix) {
		$retour = array(0, 0, 0, 255, 255, 255);
		$retour[0] = $this->x * $matrix->values[0] + $this->y * $matrix->values[1] + $this->z * $matrix->values[2] + $matrix->values[3];
		$retour[1] = $this->x * $matrix->values[4] + $this->y * $matrix->values[5] + $this->z * $matrix->values[6] + $matrix->values[7];
		$retour[2] = $this->x * $matrix->values[8] + $this->y * $matrix->values[9] + $this->z * $matrix->values[10] + $matrix->values[11];
		$retour[3] = $this->cr;
		$retour[4] = $this->cg;
		$retour[5] = $this->cb;
		return $retour;
	}
};

class matrix {
	var $values;
	
	function get($x, $y) {
		return $y + $x * 4;
	}
	
	function loadIdentity() {
		$this->values = array(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
	}
	
	function projection($angle, $ratio, $zNear, $zFar) {
		$angle = $angle;
		$yScale = atan($angle / 2);
		
		$this->loadIdentity();
		$this->values[0] = $ratio / $yScale;
		$this->values[5] = $yScale;
		$this->values[10] = $zFar / ($zFar - $zNear);
		$this->values[11] = -$zNear * $zFar / ($zFar - $zNear);
		$this->values[14] = 1;
		$this->values[15] = 0;
	}
	
	function lookat($eye, $at, $up) {
		$zaxis = new point();
		$zaxis->x = $eye->x - $at->x; $zaxis->y = $eye->y - $at->y; $zaxis->z = $eye->z - $at->z;
		$dist = sqrt($zaxis->x * $zaxis->x + $zaxis->y * $zaxis->y + $zaxis->z * $zaxis->z);
		$zaxis->x /= $dist; $zaxis->y /= $dist; $zaxis->z /= $dist;
		
		$xaxis = new point();
		$xaxis->x = $up->y * $zaxis->z - $up->z * $zaxis->y;
		$xaxis->y = $up->z * $zaxis->x - $up->x * $zaxis->z;
		$xaxis->z = $up->x * $zaxis->y - $up->y * $zaxis->x;
		$dist = sqrt($xaxis->x * $xaxis->x + $xaxis->y * $xaxis->y + $xaxis->z * $xaxis->z);
		$xaxis->x /= $dist; $xaxis->y /= $dist; $xaxis->z /= $dist;
		
		$yaxis = new point();
		$yaxis->x = $zaxis->y * $xaxis->z - $zaxis->z * $xaxis->y;
		$yaxis->y = $zaxis->z * $xaxis->x - $zaxis->x * $xaxis->z;
		$yaxis->z = $zaxis->x * $xaxis->y - $zaxis->y * $xaxis->x;
		
		$dot1 = $xaxis->x * $eye->x + $xaxis->y * $eye->y + $xaxis->z * $eye->z;
		$dot2 = $yaxis->x * $eye->x + $yaxis->y * $eye->y + $yaxis->z * $eye->z;
		$dot3 = $zaxis->x * $eye->x + $zaxis->y * $eye->y + $zaxis->z * $eye->z;

		$this->loadIdentity();
		$this->values[0] = $xaxis->x;
		$this->values[1] = $xaxis->y;
		$this->values[2] = $xaxis->z;
		$this->values[3] = -$dot1;
		$this->values[4] = $yaxis->x;
		$this->values[5] = $yaxis->y;
		$this->values[6] = $yaxis->z;
		$this->values[7] = -$dot2;
		$this->values[8] = $zaxis->x;
		$this->values[9] = $zaxis->y;
		$this->values[10] = $zaxis->z;
		$this->values[11] = -$dot3;
		$this->values[15] = 1;
	}
	
	function translate($x, $y, $z) {
		$this->loadIdentity();
		$this->values[3] = $x;
		$this->values[7] = $y;
		$this->values[11] = $z;
	}
	
	function scale($x, $y, $z) {
		$this->loadIdentity();
		$this->values[0] = $x;
		$this->values[5] = $y;
		$this->values[10] = $z;
	}
	
	function matrix() {
		$this->values = array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
	}
};

function multiply($matrix1, $matrix2) {
	$retour = new matrix();
		
	for ($i = 0; $i < 4; ++$i)
		for ($j = 0; $j < 4; ++$j)
			for ($k = 0; $k < 4; ++$k)
				$retour->values[$retour->get($i, $j)] += $matrix1->values[$matrix1->get($i, $k)] * $matrix2->values[$matrix2->get($k, $j)];
	return $retour;
}

$im = imagecreatetruecolor($width, $height);

$fond = imagecolorallocate($im, 0, 0, 0);
$blanc = imagecolorallocate($im, 255, 255, 255);

$backbuffer = array($width * $height);
for ($i = 0; $i < $width * $height; ++$i)
	$backbuffer[$i] = $clipping;

$projection = new matrix();
$projection->projection(45, $width / $height, 1, $clipping);

$view = new matrix();
$view->lookat(new point(2.5, 0.7, 1.3), new point(0, 0, 0), new point(0, 1, 0));

$matrice = multiply($projection, $view);

$cube = new mesh();

$taille = 0.5;
$cube->points[0] = new point($taille, $taille, $taille, 255, 0, 0);
$cube->points[1] = new point(-$taille, $taille, $taille, 255, 128, 128);
$cube->points[2] = new point(-$taille, -$taille, $taille, 200, 200, 0);
$cube->points[3] = new point($taille, -$taille, $taille, 255, 255, 60);
$cube->points[4] = new point($taille, -$taille, -$taille, 128, 128, 0);
$cube->points[5] = new point($taille, $taille, -$taille, 200, 0, 0);
$cube->points[6] = new point(-$taille, $taille, -$taille, 255, 45, 45);
$cube->points[7] = new point(-$taille, -$taille, -$taille, 255, 255, 128);

$cube->triangle1[0] = 0; $cube->triangle2[0] = 1; $cube->triangle3[0] = 2;
$cube->triangle1[1] = 0; $cube->triangle2[1] = 2; $cube->triangle3[1] = 3;
$cube->triangle1[2] = 4; $cube->triangle2[2] = 5; $cube->triangle3[2] = 7;
$cube->triangle1[3] = 5; $cube->triangle2[3] = 6; $cube->triangle3[3] = 7;
$cube->triangle1[4] = 0; $cube->triangle2[4] = 3; $cube->triangle3[4] = 4;
$cube->triangle1[5] = 0; $cube->triangle2[5] = 4; $cube->triangle3[5] = 5;
$cube->triangle1[6] = 1; $cube->triangle2[6] = 2; $cube->triangle3[6] = 7;
$cube->triangle1[7] = 1; $cube->triangle2[7] = 6; $cube->triangle3[7] = 7;
$cube->triangle1[8] = 0; $cube->triangle2[8] = 1; $cube->triangle3[8] = 6;
$cube->triangle1[9] = 0; $cube->triangle2[9] = 5; $cube->triangle3[9] = 6;
$cube->triangle1[10] = 2; $cube->triangle2[10] = 3; $cube->triangle3[10] = 7;
$cube->triangle1[11] = 3; $cube->triangle2[11] = 4; $cube->triangle3[11] = 7;

$cube->scale->scale(0.5, 0.5, 0.5);
$cube->translation->translate(0.2, 0.5, 0.3);

$cube->render($matrice, $width, $height, $im, $backbuffer, $wireframe);

imagejpeg($im);

Conclusion :


J'utilise les mêmes matrices que DirectX

Voici le site qui contient les explications :
http://aaprog.free.fr/?page=php/examples/3dengine1
http://aaprog.free.fr/?page=php/examples/3dengine2

Si un jour je supprime le site je mettrai ces explications dans une page html dans le zip
Le zip, qui d'ailleurs contient plusieurs fichiers .php
Le dernier est celui qui a le plus grand numéro (ils s'appelent tous moteur3d#.php où # est un nombre)
J'ai mis les anciennes versions tout simplement pour que vous arriviez à comprendre au fur et à mesure

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.