I'm currently working on a project about 3D rendering, and I'm trying to make simplistic program that can display a simple 3D room (static shading, no player movement, only rotation) with pygame
So far I've worked through the theory:
- Start with a list of coordinates for the X and Z of each "Node"
- Nodes are kept in an order which forms a closed loop, so that a pair of nodes will form either side of a wall
- The height of the wall is determined when it is rendered, being relative to distance from the camera
- Walls are rendered using painter's algorithm, so closer objects are drawn on top of further ones
- For shading "fake contrast", which brightens/darkens walls based on the gradient between it's two nodes
While it seems simple enough, the process behind translating the 3D coordinates into 2D points on the screen is proving the difficult for me to understand.
Googling this topic has so far only yeilded these equations:
screenX = (worldX/worldZ)
screenY = (worldY/worldZ)
Which seem flawed to me, as you would get a divide by zero error if any Z coordinate is 0.
So if anyone could help explain this, I'd be really greatful.
Well the
is not the whole stuff that is just the perspective division by
z
and it is not meant for DOOM or Wolfenstein techniques.Well in Doom there is only single angle of viewing (you can turn left/right but cannot look up/down only duck or jump which is not the same). So we need to know our player position and direction
px,py,pz,pangle
. Thez
is needed only if you want to implement also z axis movement/looking...If you are looking in a straight line (Red) all the object that cross that line in the 3D are projected to single x coordinate in the player screen...
So if we are looking at some direction (red) any object/point crossing/touching this red line will be place at the center of screen (in
x
axis). What is left from it will be rendered on the left and similarly whats on right will be rendered on the right too...With perspective we need to define how large viewing angle we got...
This limits our view so any point touches the green line will be projected on the edge of view (in
x
axis). From this we can compute screenx
coordinatesx
of any point(x,y,z)
directly:where
screen_size_x
is resolution of our view area and point ang is angle of pointx,y,z
relative to originpx,py,pz
. You can compute it like this:but if you truly do a DOOM ray-casting then you already got this angle.
Now we need to compute the screen
y
coordinatesy
which is dependent on the distance from player and wall size. We can exploit triangle similarity.so:
Where focal length is the distance at which wall with 100% height will cover exactly the whole screen in
y
axis. As you can see we dividing by distance which might be zero. Such state must be avoided so you need to make sure your rays will be evaluated at the next cell if standing directly on cell boundary. Also we need to select the focal length so square wall will be projected as square.Here a piece of code from mine Doom engine (putted all together):
where:
Do not forget to use perpendicular distances (multiplied by
cos(a)
as I did) otherwise serious fish-eye effect will occur. For more info see: