1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

Development PHP "raycaster" 3D renderer

Discussion in 'Software' started by NiHiLiST, 6 Jul 2006.

  1. NiHiLiST

    NiHiLiST New-born car whore

    Joined:
    18 Aug 2001
    Posts:
    3,987
    Likes Received:
    6
    I mentioned recently that I was looking for an interesting PHP project. This evening I remembered an idea I had a while ago to make a 3D renderer in PHP (and if anyone's tempted to ask - "because I can" ;)). I did a bit of reading on old 3D engines, as I didn't want something modern and slow, and found out that the "raycaster" rendering used in Wolfenstein 3D is ridiculously easy to implement. A few hours later and it's up and running :eeek: I've not run any proper speed tests yet, but the images load up pretty much instantaneously. I have a couple of ideas of what this could be used for, so watch this space ;)

    Sample render:
    [​IMG]

    Works off axis too of course:
    [​IMG]

    And on the vertical:
    [​IMG]

    The code is currently just over 300 lines long. It could be compressed down a lot (maybe even to half it's current size) just to show how stupidly simple things are with PHP. It will bulk out marginally once I get all of the functions in place for setting up the render size, camera position, angle, FOV etc as they are currently all just variables set in __construct. Even the map is just being randomly generated and thrown in.

    Next real things to do will be to expand the texture mapping somewhat (at the moment it's just 1 for each of walls, floor and ceiling) and get it showing sprites. Then maybe HDR and anisotropic filtering :worried: :hehe:
     
    Last edited: 6 Jul 2006
  2. JazzXP

    JazzXP Eh! Steve

    Joined:
    30 Apr 2002
    Posts:
    1,669
    Likes Received:
    13
    Wow, that's awesome. That could be used for some great stuff. 3d website, HTML based RPG, stuff like that!
     
  3. hitman012

    hitman012 Minimodder

    Joined:
    6 May 2005
    Posts:
    4,877
    Likes Received:
    19
    Wow - very nice work! Reminds me of the 3D Maze screensaver that you used to get with Win95 :D
     
  4. Nath

    Nath Your appeal has already been filed.

    Joined:
    28 Dec 2003
    Posts:
    2,409
    Likes Received:
    1
    Awesome, can't wait to see what you get up to with this! :D
     
  5. NiHiLiST

    NiHiLiST New-born car whore

    Joined:
    18 Aug 2001
    Posts:
    3,987
    Likes Received:
    6
    Thanks, Jazz. I did consider it could be used to add some visual depth to those HTML-based RPG games. Would be pretty easy to do, but until I've run some load tests I couldn't say how it would affect the server, it might get pretty intensive if the site's getting a lot of hits.

    One of the big overheads at the moment is that the textures have to be loaded from disk each time, and there are a lot of sin/cos/tan calculations. It's currently using lookup tables for those, but again, it's having to either generate or load those each time the script runs so some persistent memory or suchlike may need to be looked at :D

    Of course if you wanted this kind of thing on a large site it would make more sense to just code the renderer in C or something and have your scripts execute the program when necessary. PHP's not bad for things like this, but it's far from being the fastest.
     
  6. NiHiLiST

    NiHiLiST New-born car whore

    Joined:
    18 Aug 2001
    Posts:
    3,987
    Likes Received:
    6
    Eeee, 2 more replies whilst I was writing my last one :D Thanks hitman, Nath.
     
  7. RTT

    RTT #parp

    Joined:
    12 Mar 2001
    Posts:
    14,120
    Likes Received:
    74
    Surely you're loading .dlls and just calling functions from within the DLL using PHP?

    I'm not denying that it's cool - it is - but if i'm right then PHP isn't doing the fun stuff ;) :D

    Please tell me I'm 110% wrong!
     
  8. NiHiLiST

    NiHiLiST New-born car whore

    Joined:
    18 Aug 2001
    Posts:
    3,987
    Likes Received:
    6
    No I'm not, RTT, that would be silly :D The only thing I'm using is mathematical functions, binary operators and the GD library. No extensions, no DLLs, just classic software rendering.
     
  9. RTT

    RTT #parp

    Joined:
    12 Mar 2001
    Posts:
    14,120
    Likes Received:
    74
    Cor blimey :D Color me impressed!
     
  10. NiHiLiST

    NiHiLiST New-born car whore

    Joined:
    18 Aug 2001
    Posts:
    3,987
    Likes Received:
    6
    Here are the functions used RTT ;):
    require_once :wallbash:
    mt_rand
    sin
    cos
    tan
    pi
    sqrt
    pow
    imagecreatetruecolor
    imagecreatefrompng
    imagecopy
    imagedestroy
    imagepng
    imagecolorat
    imagesetpixel
    header

    Plus of course the functions in the class I've written.
     
    Last edited: 8 Jul 2006
  11. RotoSequence

    RotoSequence Lazy Lurker

    Joined:
    6 Jan 2004
    Posts:
    4,588
    Likes Received:
    7
    Add sprites! :D
     
  12. NiHiLiST

    NiHiLiST New-born car whore

    Joined:
    18 Aug 2001
    Posts:
    3,987
    Likes Received:
    6
    Right now I'm going to bed, but maybe tonight (Friday) I'll get a chance to, if I'm not out pimping of course :hip:
     
  13. RTT

    RTT #parp

    Joined:
    12 Mar 2001
    Posts:
    14,120
    Likes Received:
    74
    require_once aint a function ;)

    Hehe... can't wait for updates on this. Some source wouldn't go amiss either :D /curious :D
     
  14. Fusen

    Fusen What's a Dremel?

    Joined:
    17 Jan 2004
    Posts:
    351
    Likes Received:
    0
    some source and a demo would be awesome ;D
     
  15. FredsFriend

    FredsFriend What's a Dremel?

    Joined:
    20 Jul 2005
    Posts:
    486
    Likes Received:
    0
    This is teh funkey, seconded on the intrest ub the source it would be interesting to look through.
     
  16. NiHiLiST

    NiHiLiST New-born car whore

    Joined:
    18 Aug 2001
    Posts:
    3,987
    Likes Received:
    6
    True, I was tired - functions and language constructs blur into one when it's bedtime ;)
     
  17. NiHiLiST

    NiHiLiST New-born car whore

    Joined:
    18 Aug 2001
    Posts:
    3,987
    Likes Received:
    6
    Code for those who were asking:
    PHP:
        public function render() {
            
            
    // find which map block the camera is in
            
            
    $cameraBlockX $this->camX >> 6;
            
    $cameraBlockY $this->camY >> 6;

            
    $offset 0;

            
    // clamp angle for lookup tables

            
    if ($this->camAngle 0)        $this->camAngle += pi() * 2;
            if (
    $this->camAngle pi() * 2$this->camAngle -= pi() * 2;

            
    $angle $this->camAngle $this->camFov 0.5;
            if (
    $angle pi() * 2$angle -= pi() * 2;
            
            
    $width $this->width;

            
    $this->camPCentre $this->height $this->camRoll;
                
            
    $ang = ($this->camAngle $this->divisor) | 0;
            
            
    $invertZ 64 $this->camZ;

            
    // loop through each vertical strip

            
    while ($width-- > 0) {

                
    // set nearest face to practical infinity

                
    $nearestFace 4294967296;

                
    $angleLookup = ($angle $this->divisor);

                
    // find oriented face aligned to X axis of map

                
    $x $cameraBlockX;
                
    $y $cameraBlockY;

                if (
    $this->sin[$angleLookup] < 0) {

                    while (
    $y > -&& $x > -&& $x $this->mapWidth) {
                        
    $absY $y << 6;
                        
    $absX $this->camX + ($absY $this->camY) / $this->tan[$angleLookup];
                        
    $x    $absX >> 6;

                        
    // check if there is a block here

                        
    if ($this->map[$x][--$y]) {
                            
    $nearestFace pow($absX $this->camX2) + pow($absY $this->camY2);
                            
    $offset $absX 63;
                            break;
                        }
                    }

                } else {

                    while (
    $y++ < $this->mapHeight && $x > -&& $x $this->mapWidth) {
                        
    $absY $y << 6;
                        
    $absX $this->camX + ($absY $this->camY) / $this->tan[$angleLookup];
                        
    $x    $absX >> 6;
                        
                        
    // check if there is a block here
                        
                        
    if ($this->map[$x][$y]) {
                            
    $nearestFace pow($absX $this->camX2) + pow($absY $this->camY2);
                            
    $offset 64 $absX 63;
                            break;
                        }
                    }

                }

                
    // find closest face oriented to Y axis of map

                
    $x $cameraBlockX;
                
    $y $cameraBlockY;

                if (
    $this->cos[$angleLookup] < 0) {

                    while (
    $x > -&& $y > -&& $y $this->mapHeight) {
                        
    $absX $x << 6;
                        
    $absY $this->camY + ($absX $this->camX) * $this->tan[$angleLookup];
                        
    $y    $absY >> 6;
                        
                        
    // check if there is a block here

                        
    if ($this->map[--$x][$y]) {
                            
    $distance pow($absX $this->camX2) + pow($absY $this->camY2);

                            if (
    $distance $nearestFace) {
                                
    $nearestFace $distance;
                                
    $offset 64 $absY 63;
                            }
                            break;
                        }
                    }

                } else {

                    while (
    $x++ < $this->mapWidth && $y > -&& $y $this->mapHeight) {
                        
    $absX $x << 6;
                        
    $absY $this->camY + ($absX $this->camX) * $this->tan[$angleLookup];
                        
    $y    $absY >> 6;
                        
                        
    // check if there is a block here
                        
                        
    if ($this->map[$x][$y]) {
                            
    $distance pow($absX $this->camX2) + pow($absY $this->camY2);

                            if (
    $distance $nearestFace) {
                                
    $nearestFace $distance;
                                
    $offset $absY 63;
                            }
                            break;
                        }
                    }

                }

                
    // check whether the current strip is looking left or right of centre angle

                
    if ($angleLookup $ang)    {
                    
    $distort $this->camDistance $this->cos[7200 $angleLookup $ang];
                } else {
                    
    $distort $this->camDistance $this->cos[$angleLookup $ang];
                }

                
    $wallHeight $distort sqrt($nearestFace);

                
    $distanceFloor   $invertZ $distort;
                
    $distanceCeiling $this->camZ $distort;

                
    $floorLevel = (int) ($this->camPCentre $wallHeight $this->camZ 0.5);
                
    $wallLevel  = (int) ($this->camPCentre $wallHeight $invertZ);

                
    $height $this->height;

                
    // draw floor

                
    while (--$height $floorLevel && $height >= 0) {
                    
    $distance $distanceFloor / ($height $this->camPCentre);
                    
    imagesetpixel($this->canvas$width$heightimagecolorat($this->texFloor, ($this->camX $this->cos[$angleLookup] * $distance) & 63, ($this->camY $this->sin[$angleLookup] * $distance) & 63));
                }

                
    // draw wall

                
    while (--$height $wallLevel && $height >= 0) {
                    
    imagesetpixel($this->canvas$width$heightimagecolorat($this->texWall$offset, ($height $wallLevel) / $wallHeight));
                }

                
    // draw ceiling

                
    while (--$height > -1) {
                    
    $distance $distanceCeiling / ($this->camPCentre $height);
                    
    imagesetpixel($this->canvas$width$heightimagecolorat($this->texCeil, ($this->camX $this->cos[$angleLookup] * $distance) & 63, ($this->camY $this->sin[$angleLookup] * $distance) & 63));
                }

                
    $angle -= $this->camPerRayAngle;
                
                if (
    $angle 0$angle += pi() * 2;
            }
        }
    The rest of the class needs some work before it's really presentable ;)
     
  18. Hwulex

    Hwulex What's a Dremel?

    Joined:
    1 Feb 2002
    Posts:
    4,007
    Likes Received:
    1
    Wow, very impressive. I wouldn't even know where to begin with soemthing like this.

    Damn I hate sucking at math. :sigh:
     
  19. korhojoa

    korhojoa durr

    Joined:
    31 Oct 2003
    Posts:
    162
    Likes Received:
    0
    Whoa, VERY Nice!
    (Oh yes, and you got dugg.)
     
  20. Rexxie

    Rexxie What's a Dremel?

    Joined:
    18 Nov 2002
    Posts:
    198
    Likes Received:
    0
    That's pretty awesome. Nice work! :)

    Oh and yeah, you got dugg :D
     

Share This Page