Skip to content

Instantly share code, notes, and snippets.

@usrlocalben
Created September 6, 2012 23:50
Show Gist options
  • Save usrlocalben/3661517 to your computer and use it in GitHub Desktop.
Save usrlocalben/3661517 to your computer and use it in GitHub Desktop.
raymarch in php
<?php
// oh boy, lets raymarch in php.
function LERP( $a, $b, $t ) { return (1.0-$t)*$a + $t*$b; }
function CLAMP($a,$x,$y){ return ($a<$x?$x:($a>$y?$y:$a)); }
function CLAMP01($a) { return CLAMP($a,0.0,1.0); }
class vec3 {
public $x,$y,$z;
public function __construct($a,$b,$c) { $this->x = $a; $this->y = $b; $this->z = $c; }
//public function __construct($a){ $x = $y = $z = $w = $a; }
//public function __construct(){ $x=$y=$z=$w=0;}
public function add_vec3(vec3 $v) { return new vec3( $this->x + $v->x ,$this->y + $v->y ,$this->z + $v->z ); }
public function sub_vec3(vec3 $v) { return new vec3( $this->x - $v->x ,$this->y - $v->y ,$this->z - $v->z ); }
public function mul_vec3(vec3 $v) { return new vec3( $this->x * $v->x ,$this->y * $v->y ,$this->z * $v->z ); }
public function div_vec3(vec3 $v) { return new vec3( $this->x / $v->x ,$this->y / $v->y ,$this->z / $v->z ); }
public function add_num($v) { return new vec3( $this->x + $v ,$this->y + $v ,$this->z + $v ); }
public function sub_num($v) { return new vec3( $this->x - $v ,$this->y - $v ,$this->z - $v ); }
public function mul_num($v) { return new vec3( $this->x * $v ,$this->y * $v ,$this->z * $v ); }
public function div_num($v) { return new vec3( $this->x / $v ,$this->y / $v ,$this->z / $v ); }
public function add($v) { if ( $v instanceof vec3 ) return $this->add_vec3($v); return $this->add_num($v); }
public function sub($v) { if ( $v instanceof vec3 ) return $this->sub_vec3($v); return $this->sub_num($v); }
public function mul($v) { if ( $v instanceof vec3 ) return $this->mul_vec3($v); return $this->mul_num($v); }
public function div($v) { if ( $v instanceof vec3 ) return $this->div_vec3($v); return $this->div_num($v); }
public function dot(vec3 $v) { return $this->x*$v->x + $this->y*$v->y + $this->z*$v->z; }
public function length() { return sqrt($this->dot($this)); }
public function normalized() { return $this->div_num( $this->length() ); }
public function dump(){
printf("(%11.4f,%11.4f,%11.4f)\n",$this->x,$this->y, $this->z);
}
public static function lerp(vec3 $a, vec3 $b, $t ) {
return new vec3(
LERP($a->x, $b->x, $t)
,LERP($a->y, $b->y, $t)
,LERP($a->z, $b->z, $t) );
}
public static function nlerp(vec3 $a, vec3 $b, $t ) {
return static::lerp($a,$b,$t)->normalized();
}
public static function saturate(vec3 $a) {
return new vec3( CLAMP01($a->x), CLAMP01($a->y), CLAMP01($a->z) );
}
public function truecolor32() {
// printf("%04x %04x %04x %04x\n", $this->x *255.0, $this->y*255.0, $this->z*255.0, $this->w*255.0 );
return pack( 'CCCC', 0, $this->z*255.0, $this->y*255.0, $this->x*255.0 );
}
public function truecolor24() {
// printf("%04x %04x %04x %04x\n", $this->x *255.0, $this->y*255.0, $this->z*255.0, $this->w*255.0 );
return pack( 'CCC', $this->z*255.0, $this->y*255.0, $this->x*255.0 );
}
}//vec3
$colorbuffer = array();
// a simple light for the scene
$lightdir = new vec3(-1,-1,-1);
$lightdir = $lightdir->normalized();
for ( $py=0; $py<256; $py++ ) {
for ( $px=0; $px<256; $px++ ) {
// build a ray through the screen at the pixel to sample
$sp = new vec3( $px/128-1, -($py/128-1), 0 );
$p = new vec3( 0, 0, 10 );
$raydir = $sp->sub($p)->normalized();
// march! advance along the ray until we get within
// "epsilon" (0.01) of the field, or until z is past
// a limit
// $steps = 0;
while ( $p->z > -500 ) {
$dist = myfield($p);
if ( abs($dist)<0.01 ) break;
$p = $p->add( $raydir->mul($dist) );
//$steps++;
}
// if z is past our limit, we never hit the field
if ( $p->z <= -500 ) {
$colorbuffer[$py][$px] = new vec3(0,0,0);
} else {
// make a few samples to build a normal
// by numerical differentiation
// from http://blog.hvidtfeldts.net/index.php/2011/08/distance-estimated-3d-fractals-ii-lighting-and-coloring/
$xdir = new vec3(0.0001,0,0);
$ydir = new vec3(0,0.0001,0);
$zdir = new vec3(0,0,0.0001);
$n = new vec3(
myfield($p->add($xdir))-myfield($p->sub($xdir))
,myfield($p->add($ydir))-myfield($p->sub($ydir))
,myfield($p->add($zdir))-myfield($p->sub($zdir)) );
$n = $n->normalized();
// very basic lighting
$color = CLAMP01(-$n->dot($lightdir));
// store the pixel
$colorbuffer[$py][$px] = new vec3($color,$color,$color);
}
}
echo '.';//progress...
}
$tga = tga_createheader(256,256);
$tgadata = '';
for ( $py=255; $py>=0; $py-- ) {
for ( $px=0; $px<256; $px++ ) {
$tgadata .= $colorbuffer[$py][$px]->truecolor24();
}
}
file_put_contents('raymarched.tga',$tga.$tgadata);
echo "\nwrote colorbuffer to raymarched.tga\n";
die;
function myfield( vec3 $p )
{
$spherepos = new vec3(0,2,-100);
$spherepos2 = new vec3(-2,-0,-100);
$planepos = new vec3(0,-10,-100);
$planedir = new vec3(0,1,0);
return min(
sdSphere( $p->sub($spherepos), 3.0 )
,sdSphere( $p->sub($spherepos2), 5.0 )
,sdPlane( $p->sub($planepos), $planedir )
);
}
// some primitives, from iq's pages
function sdPlane( vec3 $p, vec3 $n )
{
return $p->dot($n);
}
function sdSphere( vec3 $p, $r )
{
return $p->length()-$r;
}
/*
* create a tga file header
* see http://paulbourke.net/dataformats/tga/
*/
function tga_createheader( $width, $height )
{
$a = '';
$a .= pack('CC',0,0);
$a .= pack('C',2); // uncompressed RGB
$a .= pack('CC',0,0);
$a .= pack('CC',0,0);
$a .= pack('C',0);
$a .= pack('CC',0,0); // x origin
$a .= pack('CC',0,0); // y origin
$a .= pack('v',$width );
$a .= pack('v',$height );
$a .= pack('C',24); // 24 bit bitmap
$a .= pack('C',0);
return $a;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment