Sign in to follow this  
erickmeister

What is wrong with my raycaster?

Recommended Posts

Hello, After reading some raycasting tutorials I decided an attempt to make one. Starting out I started with something small like only displaying a small room with no wall/object collision. Just rendering and walking. I got this program to work but the room looks so blocky and I can never walk right up to a wall without the program crashing. Can someone please look at my code and correct me? I am programming this in C++ and using the Allegro library. Parts of interest: Maybe something wrong in castRays(); Parts of interest: Maybe something wrong in drawScene();
Module 1: GLOBALS.H

typedef unsigned short USHORT;

const USHORT RAYS_TO_CAST = 640;
const USHORT SCREEN_WIDTH = 640;
const USHORT SCREEN_HEIGHT = 480;
const USHORT HALF_SCREEN_HEIGHT = 240;
const USHORT GRID_SIZE = 64;
const USHORT WALL = 1;
const float FOV = 60.0f;

int map[9][9] =   {{ 1, 1, 1, 1, 1, 1, 1, 1, 1 },
                   { 1, 0, 0, 0, 0, 0, 0, 0, 1 },
                   { 1, 0, 0, 0, 0, 0, 0, 0, 1 },
                   { 1, 0, 0, 0, 0, 0, 0, 0, 1 },
                   { 1, 0, 0, 0, 0, 0, 0, 0, 1 },
                   { 1, 0, 0, 0, 0, 0, 0, 0, 1 },
                   { 1, 0, 0, 0, 0, 0, 0, 0, 1 },
                   { 1, 0, 0, 0, 0, 0, 0, 0, 1 },
                   { 1, 1, 1, 1, 1, 1, 1, 1, 1 }};
Now for the last file... SOURCE CODE: CAST.CPP
#include <allegro.h>
#include "globals.h"

BITMAP *buffer;
float rayAngle[RAYS_TO_CAST]; // hold all angles to cast with
int rayLength[RAYS_TO_CAST]; // later use this to scale walls

float players_direction = 90.0f; // facing north
int players_x = 128;
int players_y = 128;

// prototypes
void calculateAngles();
void castRays();
void drawScene();

int main(void)
{
  // setup
  allegro_init();
  install_keyboard();
  set_color_depth(16);
  set_gfx_mode(GFX_AUTODETECT, 640, 480, 0, 0);
  buffer = create_bitmap(640, 480);
  // end of setup

  //main loop
  while(!key[KEY_ESC])
  {
    if(key[KEY_UP])
    {
      players_y--;
    }
    if(key[KEY_DOWN])
    {
      players_y++;
    }
    if(key[KEY_RIGHT])
    {
      players_direction += 1.0f;
    }
    if(key[KEY_LEFT])
    {
      players_direction -= 1.0f;
    }

    calculateAngles();
    castRays();
    drawScene();

    acquire_screen();
    blit(buffer, screen, 0, 0, 0, 0, 640, 480);
    release_screen();
    clear_bitmap(buffer);

    // reset the rayLength array
    for(int i=0;i<RAYS_TO_CAST;i++)
      rayLength[i] = 0;
  }

  // clean up
  destroy_bitmap(buffer);
  // end of clean up

  return(0);
}
END_OF_MAIN();

void calculateAngles(void)
{
  for(int i=0;i<RAYS_TO_CAST;i++)
  {
    rayAngle[i] = players_direction + ((i * (FOV / 640.0f)) - (FOV / 2.0f));

    if(rayAngle[i] < 0.0f)
      rayAngle[i] += 360.0f;
    if(rayAngle[i] >=360.0f)
      rayAngle[i] -= 360.0f;
  }
}
END_OF_FUNCTION(calculateAngles(void));

void castRays(void)
{
  int ray_x;
  int ray_y;

  for(int i=0;i<SCREEN_WIDTH;i++)
  {
    ray_x = players_x;
    ray_y = players_y;

    while(map[ray_x/GRID_SIZE][ray_y/GRID_SIZE] != WALL)
    {
      ray_x += int(GRID_SIZE/4 * cos(rayAngle[i]));
      ray_y -= int(GRID_SIZE/4 * sin(rayAngle[i]));

      rayLength[i]++;
    }
  }
}
END_OF_FUNCTION(castRays(void));

void drawScene(void)
{
  int sliceSize;

  for(int i=0;i<SCREEN_WIDTH;i++)
  {
    sliceSize = 300/rayLength[i];

    vline(buffer, i, sliceSize, (HALF_SCREEN_HEIGHT / 2) -sliceSize, makecol(255, 255, 255));
  }
}
END_OF_FUNCTION(drawScene(void));

Share this post


Link to post
Share on other sites
i think the reason of image being blocky is probabley this


while(map[ray_x/GRID_SIZE][ray_y/GRID_SIZE] != WALL)
{
ray_x += int(GRID_SIZE/4 * cos(rayAngle[i]));
ray_y -= int(GRID_SIZE/4 * sin(rayAngle[i]));

rayLength[i]++;
}



why do you say rayLength[i]++?the correct way of finding distance should be like once you get out from the while loop ray_x and ray_y represents the intersection point and

distance = abs((ray_y - player_y)/sin(player's facing angle))

another thing is remember coordination starts counting from 0.so you need to -1 or +1 on your ray_x and ray_y.thats probably why is crashing.

Share this post


Link to post
Share on other sites
Thanks a lot for the reply, it helped me a lot. I was not sure how to check for wall distance. I changed the castRays() function to the following...


void castRays(Player & ref)
{
int ray_x;
int ray_y;

for(int i=0;i<SCREEN_WIDTH;i++)
{
ray_x = ref.posX;
ray_y = ref.posY;

while(map[ray_x/GRID_SIZE][ray_y/GRID_SIZE] != WALL)
{
ray_x += int(GRID_SIZE * cos(angle[i]*M_PI/180.0f));

ray_y += int(GRID_SIZE * sin(angle[i]*M_PI/180.0f));
}
wall_distance[i] = abs((ray_y - ref.posY) / sin(ref.facing_direction));
}
}
END_OF_FUNCTION(castRays(Player & ref));


I was forgetting to convert degrees into radians when passing the angles into cos/sin. The program does not look blocky at all now only walls look distorted. Do you still see errors in my code? By the way, I changed my screen dimensions from 640X480 to 320X200

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:

The program does not look blocky at all now only walls look distorted


Distorted in what way? Do you mean the walls seem to curve the further they are from the center of the screen?

Check out the pictures on this page to see if thats the same problem you are having. If it is, the solution is on that page also.

Alan

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
Quote:

The program does not look blocky at all now only walls look distorted


Distorted in what way? Do you mean the walls seem to curve the further they are from the center of the screen?

Check out the pictures on this page to see if thats the same problem you are having. If it is, the solution is on that page also.

Alan


Fishbowl... You need to multiply by 1 / cosine to fix this

http://www.iol.ie/~kellyswd/Patrick/raycaster/
Source code

Also it's better to build a raycaster based on the tan function, as it result in significantly less calulatuions.

Share this post


Link to post
Share on other sites
Well fishbowl is one of the problems. But I think I still have an error when casting ray_y and ray_x. I'm constantly adding to the variables but don't I add, subtract, etc depending on the angle? Or do I always just add to the rays? I will try to explain what happens when I run the program. The first view looks ok but when I start to move forward, back, or turn it looks like I have other walls in the map when I don't. Hard to explain i'm sorry :/ It seems like the map is as follows...

111111111
100010001
101100001
100000011
100000001
100100010.... etc

Like my casting is not right. I'm sure i'm moving around right. When I turn I add 1.0f or subtract 1.0f from my facing_direction variable and when I walk forward or back i'm incrementing ++ or decrementing -- my posY variable. I don't think anything is wrong with the way i'm drawing to the screen but in case there is something wrong I'll post the drawScene() function which is not large at all.


void drawScene(void)
{
int sliceSize;

for(int i=0;i<SCREEN_WIDTH;i++)
{
sliceSize = wall_distance[i] / 6;
vline(buffer, i, HALF_SCREEN_HEIGHT-(sliceSize/2), sliceSize, makecol(255, 255, 255));
}
}
END_OF_FUNCTION(drawScene(void));


I noticed that the larger the number I use for / 6 the more blocky everything seems so I was playing around with values 4 and 6 for this literal constant. Thanks for the help on fishbowl, I was also wondering about that.

Erick

Share this post


Link to post
Share on other sites
Quote:
Original post by erickmeister
I'm constantly adding to the variables but don't I add, subtract, etc depending on the angle? Or do I always just add to the rays?


You get the sign from the result of sine or cosine functions, so you always add. Did you see the source of my caster

[SOURCE]
//Ray starting position = player starting position
rayx = playerx;
rayy = playery;

//Calculate ray step x, ray step y.
//Need to multiply by 1 / cos( fix ) to get rid of fishbowl.
//Remove it and see what happens.
stepx = ( Math.cos( getAngle( angle ) ) * ( 1.0 / Math.cos( getAngle( fix ) ) ) ) / 8;
stepy = ( Math.sin( getAngle( angle ) ) * ( 1.0 / Math.cos( getAngle( fix ) ) ) ) / 8;

//Set these to minimum value
wall = 0;
distance = 1; //Do *NOT* set to 0 or you might get /0 error.

//Loopp until wall found
while( wall == 0 )
{
rayx += stepx; //Ray traverses map in x by stepx.
rayy += stepy; //Ray traverses map in x by stepy.
wall = map[ (int)rayx ][ (int)rayy ]; //Have we hit a wall.
distance++; //Increment distance for each step.
}
[/SOURCE]
[/source]

Quote:
Original post by erickmeister
I will try to explain what happens when I run the program. The first view looks ok but when I start to move forward, back, or turn it looks like I have other walls in the map when I don't..


Are you stopping the cast when you hit a wall?

EDIT - Are you clearing the screen properly? It happens sometimes that you forget the simple stuff.

Maybe a screenshot would help.

Quote:
Original post by erickmeister

void drawScene(void)
{
int sliceSize;

for(int i=0;i<SCREEN_WIDTH;i++)
{
sliceSize = wall_distance[i] / 6;
vline(buffer, i, HALF_SCREEN_HEIGHT-(sliceSize/2), sliceSize, makecol(255, 255, 255));
}
}
END_OF_FUNCTION(drawScene(void));



I'm not sure what graphics library (homebrew?) you are using but it looks right. You shouln't need to modify the slice size if you do it right, the way I do in in my JAVA raycaster is:

height = 1024 / distance;

distance is just incremented every step, thus I fake the height of the wall.

Share this post


Link to post
Share on other sites
I appreciate all the help from the forum, to answer the question:

>EDIT - Are you clearing the screen properly? It happens sometimes that you forget the simple stuff.

I think I'm doing it correctly, as for the distance array, I zero it each main loop iteration like this...


// reset wall_distance
for(int i=0;i<SCREEN_WIDTH;i++)
wall_distance[i] = 0;


I also start it off equal to zero.

When I'm drawing to the screen i'm drawing each vertical line on a bitmap (buffer) then I blit the buffer to the screen. I clear the buffer after each loop iteration like this...

// show buffer to the screen
acquire_screen();
blit(buffer, screen, 0, 0, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
release_screen();
clear_bitmap(buffer);


The clear_bitmap takes care of that part. By the way, i'm using Allegro for my graphics library and coding pure C++.

When checking for my left and right movements i'm manipulating my facing_direction in this way...


if(key[KEY_RIGHT])
{
Hero.facing_direction += 1.0f;
}
if(key[KEY_LEFT])
{
Hero.facing_direction -= 1.0f;
}


Does that look about right? The FOV is 60.0f * so I would not add or subtract by 0.1f right? I think this is ok.

The problem i'm having is something small, when I get home i'm going to look over your source and brief back over the tutorials to see if there is something I missed. Do you see a problem in my degree to radian converting?


ray_x += int(GRID_SIZE * cos(angle[i]*M_PI/180.0f));
ray_y += int(GRID_SIZE * sin(angle[i]*M_PI/180.0f));


This is just an idea I have to what might be one error in my casting. I start the vector length off at GRID_SIZE which is equal to 64. In the loop I check for walls in this way:

while(map[ray_x/GRID_SIZE][ray_y/GRID_SIZE] != WALL)
{
}


Of course it finds what grid i'm in but before I cast the first ray should i make sure exactly how far I am from the grid intersections? Then cast by GRID_SIZE? Sorry for so many questions.

Erick

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this