1<?php 2 3/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 4 5/** 6 * 3d Library 7 * 8 * PHP versions 5 9 * 10 * LICENSE: 11 * This library is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU Lesser General Public 13 * License as published by the Free Software Foundation; either 14 * version 2.1 of the License, or (at your option) any later version. 15 * 16 * This library is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 * Lesser General Public License for more details. 20 * 21 * You should have received a copy of the GNU Lesser General Public 22 * License along with this library; if not, write to the Free Software 23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 24 * 25 * @category Image 26 * @package Image_3D 27 * @author Kore Nordmann <3d@kore-nordmann.de> 28 * @copyright 1997-2005 Kore Nordmann 29 * @license http://www.gnu.org/licenses/lgpl.txt lgpl 2.1 30 * @version CVS: $Id$ 31 * @link http://pear.php.net/package/PackageName 32 * @since File available since Release 0.1.0 33 */ 34 35// {{{ Image_3D_Renderer_Raytrace 36 37/** 38 * Image_3D_Renderer_Raytrace 39 * 40 * @category Image 41 * @package Image_3D 42 * @author Kore Nordmann <3d@kore-nordmann.de> 43 * @copyright 1997-2005 Kore Nordmann 44 * @license http://www.gnu.org/licenses/lgpl.txt lgpl 2.1 45 * @version Release: @package_version@ 46 * @link http://pear.php.net/package/PackageName 47 * @since Class available since Release 0.1.0 48 */ 49class Image_3D_Renderer_Raytrace extends Image_3D_Renderer 50{ 51 52 protected $_image; 53 54 protected $_camera; 55 protected $_shadows; 56 protected $_rays; 57 protected $_depth; 58 59 public function __construct() 60 { 61 parent::__construct(); 62 63 $this->_camera = new Image_3D_Coordinate(0, 0, -100); 64 $this->_shadows = true; 65 $this->_rays = 1; 66 $this->_depth = 5; 67 } 68 69 // {{{ _calculateScreenCoordiantes() 70 71 /** 72 * Caclulate Screen Coordinates 73 * 74 * Does not do anything 75 * 76 * @param Image_3D_Point $point Point to process 77 * 78 * @return void 79 */ 80 protected function _calculateScreenCoordiantes(Image_3D_Point $point) 81 { 82 } 83 84 // }}} 85 // {{{ _sortPolygones() 86 87 /** 88 * Sort polygones 89 * 90 * Does not do anything 91 * 92 * @return void 93 */ 94 protected function _sortPolygones() 95 { 96 } 97 98 // }}} 99 // {{{ setShading() 100 101 /** 102 * Set the quality of the shading 103 * 104 * Does not do anything 105 * 106 * @return void 107 */ 108 public function setShading() 109 { 110 } 111 112 // }}} 113 // {{{ setDriver() 114 115 /** 116 * Set the driver 117 * 118 * Does not do anything 119 * 120 * @return void 121 */ 122 public function setDriver() 123 { 124 } 125 126 // }}} 127 public function setCameraPosition(Image_3D_Coordinate $position) 128 { 129 $this->_camera = $position; 130 } 131 132 public function setRaysPerPixel($rays) 133 { 134 $this->_rays = max(1, (int) $rays); 135 } 136 137 public function scanDepth($depth) 138 { 139 $this->_depth = max(1, (int) $depth); 140 } 141 142 public function enableShadows($shadows) 143 { 144 $this->_shadows = (bool) $shadows; 145 } 146 147 protected function _sendRay(Image_3D_Line $ray, $depth) 148 { 149 if ($depth <= 0) { 150 return false; 151 } 152 153 $lowest = 1000; 154 $nearest = false; 155 foreach ($this->_polygones as $nr => $polygon) { 156 $t = $polygon->distance($ray); 157 if ($t === false) { 158 continue; 159 } 160 161 if ($t < $lowest) { 162 $nearest = $nr; 163 $lowest = $t; 164 } 165 } 166 167 if ($nearest === false) { 168 return false; 169 } 170 171 // Examine cutting point 172 $cuttingPoint = clone $ray; 173 $direction = $cuttingPoint->getDirection(); 174 $cuttingPoint->add($cuttingPoint->getDirection()->multiply($lowest)); 175 176 // Create point to use for enlightenment 177 $point = new Image_3D_Point($cuttingPoint->getX(), $cuttingPoint->getY(), $cuttingPoint->getZ()); 178 $point->addVector($this->_polygones[$nearest]->getNormale()); 179 $point->addColor($this->_polygones[$nearest]->getColor()); 180 181 // Perhaps send new rays 182 $transparency = end($this->_polygones[$nearest]->getColor()->getValues()); 183 $reflection = $this->_polygones[$nearest]->getColor()->getReflection(); 184 185 186 if ($reflection > 0) { 187 // Calculate reflection vector 188 $normale = $this->_polygones[$nearest]->getNormale(); 189 $normale->unify(); 190 191 $direction = $ray->getDirection(); 192 193 $reflectionRay = new Image_3D_Line($cuttingPoint->getX(), 194 $cuttingPoint->getY(), 195 $cuttingPoint->getZ(), 196 // l - 2n (n * l) 197 $direction->sub($normale->multiply($normale->scalar($direction))->multiply(2))); 198 199 $reflectionColor = $this->_sendRay($reflectionRay, $depth - 1); 200 if ($reflectionColor === false) { 201 $reflectionColor = $this->_background; 202 } 203 $reflectionColor->mixAlpha($reflection); 204 $point->addColor($reflectionColor); 205 } 206 207 if ($transparency > 0) { 208 // Calculate colors in the back of our polygon 209 $transparencyRay = new Image_3D_Line($cuttingPoint->getX(), $cuttingPoint->getY(), $cuttingPoint->getZ(), $ray->getDirection()); 210 211 $transparencyColor = $this->_sendRay($transparencyRay, $depth - 1); 212 if ($transparencyColor === false) { 213 $transparencyColor = $this->_background; 214 } 215 $transparencyColor->mixAlpha($transparency); 216 $point->addColor($transparencyColor); 217 } 218 219 // Check lights influence for cutting point 220 $pointLights = array(); 221 foreach ($this->_lights as $light) { 222 // Check for shadow casting polygones 223 if ($this->_shadows) { 224 // Create line from point to light source 225 $lightVector = new Image_3D_Vector($light->getX(), $light->getY(), $light->getZ()); 226 $lightVector->sub($cuttingPoint); 227 228 $lightVector = new Image_3D_Line($cuttingPoint->getX(), $cuttingPoint->getY(), $cuttingPoint->getZ(), $lightVector); 229 230 // Check all polygones for possible shadows to cast 231 $modifyingPolygones = array(); 232 233 $modifyLight = false; 234 foreach ($this->_polygones as $polygon) { 235 $t = $polygon->distance($lightVector); 236 237 // $t > 1 means polygon is behind the light, but crosses the ray 238 if (($t !== false) && ($t < 1)) { 239 $transparency = end($polygon->getColor()->getValues()); 240 if ($transparency > 0) { 241 // Polygon modifies light source 242 $modifyingPolygones[] = $polygon; 243 244 $modifyLight = true; 245 } else { 246 // Does not use lightsource when non transparent polygon is in its way 247 continue 2; 248 } 249 } 250 } 251 252 // We only reach this code with no, or only transparent polygones 253 if ($modifyLight) { 254 $light = clone $light; 255 256 $lightColor = $light->getRawColor()->getValues(); 257 258 // Modify color for all polygones in the rays way to earth 259 foreach ($modifyingPolygones as $polygon) { 260 $polygonColor = $polygon->getColor()->getValues(); 261 262 $lightColor[0] *= $polygonColor[0] * (1 - $polygonColor[3]); 263 $lightColor[1] *= $polygonColor[1] * (1 - $polygonColor[3]); 264 $lightColor[2] *= $polygonColor[2] * (1 - $polygonColor[3]); 265 $lightColor[3] *= $polygonColor[3]; 266 } 267 268 $light->setColor(new Image_3D_Color($lightColor[0], $lightColor[1], $lightColor[2], $lightColor[3])); 269 } 270 } 271 272 $pointLights[] = $light; 273 } 274 275 $point->calculateColor($pointLights); 276 return $point->getColor(); 277 } 278 279 protected function _raytrace() 280 { 281 // Create basic ray ... modify direction later 282 $ray = new Image_3D_Line($this->_camera->getX(), $this->_camera->getY(), $this->_camera->getZ(), new Image_3D_Vector(0, 0, 1)); 283 284 // Colorarray for resulting image 285 $canvas = array(); 286 287 // Iterate over viewplane 288 for ($x = -$this->_size[0]; $x < $this->_size[0]; ++$x) { 289 for ($y = -$this->_size[1]; $y < $this->_size[1]; ++$y) { 290 $canvas[$x + $this->_size[0]][$y + $this->_size[1]] = array(); 291 292 // Iterate over rays for one pixel 293 $inPixelRayDiff = 1 / ($this->_rays + 1); 294 for ($i = 0; $i < $this->_rays; ++$i) { 295 for ($j = 0; $j < $this->_rays; ++$j) { 296 297 // Modify ray 298 $ray->setDirection(new Image_3D_Vector( 299 ($x + $i * $inPixelRayDiff) - $this->_camera->getX(), 300 ($y + $j * $inPixelRayDiff) - $this->_camera->getY(), 301 - $this->_camera->getZ() 302 )); 303 304 // Get color for ray 305 $color = $this->_sendRay($ray, $this->_depth); 306 if ($color !== false) { 307 $canvas[$x + $this->_size[0]][$y + $this->_size[1]][] = $color; 308 } else { 309 $canvas[$x + $this->_size[0]][$y + $this->_size[1]][] = $this->_background; 310 } 311 } 312 } 313 } 314 } 315 316 return $canvas; 317 } 318 319 protected function _getColor(Image_3D_Color $color) 320 { 321 $values = $color->getValues(); 322 323 $values[0] = (int) round($values[0] * 255); 324 $values[1] = (int) round($values[1] * 255); 325 $values[2] = (int) round($values[2] * 255); 326 $values[3] = (int) round($values[3] * 127); 327 328 if ($values[3] > 0) { 329 // Tranzparente Farbe allokieren 330 $color = imageColorExactAlpha($this->_image, $values[0], $values[1], $values[2], $values[3]); 331 if ($color === -1) { 332 // Wenn nicht Farbe neu alloziieren 333 $color = imageColorAllocateAlpha($this->_image, $values[0], $values[1], $values[2], $values[3]); 334 } 335 } else { 336 // Deckende Farbe allozieren 337 $color = imageColorExact($this->_image, $values[0], $values[1], $values[2]); 338 if ($color === -1) { 339 // Wenn nicht Farbe neu alloziieren 340 $color = imageColorAllocate($this->_image, $values[0], $values[1], $values[2]); 341 } 342 } 343 344 return $color; 345 } 346 347 // {{{ render() 348 349 /** 350 * Render the image 351 * 352 * Render the image into the metioned file 353 * 354 * @param string $file Filename 355 * 356 * @return void 357 */ 358 public function render($file) 359 { 360 // Render image... 361 $canvas = $this->_raytrace(); 362 363 // Write canvas to file 364 $this->_image = imagecreatetruecolor($this->_size[0] * 2, $this->_size[1] * 2); 365 366 $bg = $this->_getColor($this->_background); 367 imagefill($this->_image, 1, 1, $bg); 368 369 $x = 0; 370 foreach ($canvas as $row) { 371 $y = 0; 372 foreach ($row as $pixel) { 373 if (count($pixel)) { 374 $color = new Image_3D_Color(); 375 $color->merge($pixel); 376 377 imagesetpixel($this->_image, $x, $y, $this->_getColor($color)); 378 } 379 ++$y; 380 } 381 ++$x; 382 } 383 384 imagepng($this->_image, $file); 385 } 386 387 // }}} 388} 389 390// }}} 391