Jump to content
Search In
  • More options...
Find results that contain...
Find results in...
Anarkavre

The Definitive Guide to Visplanes

Recommended Posts

Visplanes have always seemed mysterious, so I planned to write an article explaining them that was simple to understand. I went through all the related code and everything can be categorized into three stages: Generation, Mapping, and Rendering. I will explain the code for these three stages with simple explanations and visuals. Just a warning, parts of this require knowledge of algebra, geometry, and trigonometry, so brush up on those for a full understanding. So with that, let’s delve into the mysterious visplanes.

 

Generation

 

Each frame R_ClearPlanes is called in R_RenderPlayerView to reset data related to clipping, visplane generation, and texture mapping. I will come back to the part related to texture mapping in a later section. R_FindPlane is called in R_Subsector for both the floor and the ceiling. This checks to see if a visplane already exists with the same height, pic, (different for floor and ceiling) and light level of the subsector. If it cannot find one, it creates a new one with the height, pic, and light level of the subsector. R_CheckPlane is called in R_StoreWallRange for both the floor and ceiling if they are visible. This checks if the plane selected earlier can be merged with the area it wants to render. If there is no vertical divide, the area is merged with the visplane. If it cannot be merged, a new visplane is created. As the linesegs are rendered in R_RenderSegLoop, the top and bottom of the of visplane are marked for both the floor and ceiling if they are visible.

 

Mapping

 

R_DrawPlanes is called in R_RenderPlayerView to draw all visplanes that were generated. If the visplane is for a sky, it will draw the sky as columns like the walls and the sprites since it does not have to be projected the same way as floors and ceilings. If the visplane is for a floor or ceiling, it will convert the columns into spans of pixels to be drawn.

 

Figure 1 - Floor as columns

 

w7bJ8jg.png

 

Figure 2 - Floor as spans

 

2e8EIvG.png

 

It does it this way because the strip of the floor or ceiling is at a constant depth and it helps save on perspective calculations that can be done once and used for the entire span. R_MakeSpans is called in R_DrawPlanes for each visplane drawn. It performs the conversion from columns to spans. When a span is ready to be drawn, R_MapPlane is called in R_MakeSpans. It performs the necessary perspective calculations for the span and caches them so they can be reused. The mathematics behind this is simple algebra, geometry, and trigonometry. We can find the distance to the strip of floor or ceiling using similar triangles and some known variables. We know the distance of the floor or ceiling from the player's view, the distance of the projection plane y the span is on from the center y of the projection plane, and the distance from the player to the projection plane.

 

The distance from the player to the projection plane can be easily calculated. The player field of view is 90° which makes it a 45°–45°–90° triangle. The projection plane would be along the hypotenuse of this triangle.

 

Figure 3 - Field of view and projection plane

 

WWXHFep.png

 

We can find the distance from the player to the projection plane using trigonometry.

 

projectionplanedistance = (viewwidth / 2) / tan(90 / 2) = viewwidth / 2

 

The distance can then be calculated.

 

distance = planeheight * projectionplanedistance / dy

 

The step used for texture mapping can also be calculated. We know that the distance to the projection plane is equal to half of the width of the projection plane. This also applies for the distance to the strip of floor or ceiling.

 

step =  distance / projectionplanedistance

 

We need to know the step in the x and y directions of world space. We can find these using trigonometry.

 

xstep = step * cos(viewangle - 90)

ystep = -step * sin(viewangle - 90)

 

The last thing we need before rendering can happen is the point in world space where the strip of floor or ceiling begins. This can be found using trigonometry and some known variables. We know the player view x, player view y, the player view angle, the view angle of the projection plane x where the span starts, and the distance to the strip of floor or ceiling.

 

x = viewx + (distance / cos(xviewangle) * cos(viewangle + xviewangle)

y = -viewy - (distance / cos(xviewangle) * sin(viewangle + xviewangle)

 

Rendering

 

R_DrawSpan or R_DrawSpanLow is called in R_MapPlane depending on the graphic detail mode set. It draws the span by stepping over the strip of floor or ceiling in texture space and sampling the texture at each discrete point.

 

Figure 4 - Sampling points

 

V1oBCGD.png

 

Figure 5 - Sampling points zoomed

 

XB4IwoR.png

 

That is all there is to visplanes. They aren't very mysterious after all. I wrote a little demo that lets you watch a floor be drawn. You can also turn, move, and strafe. You can think of it as being a room with walls infinitely far away, so the visplane starts at the horizon which is the center y of the projection plane. The demo isn't exactly how Doom works, but it uses all the same mathematics that I explained.

 

I hope you enjoyed this article and learned something from it. If I made any mistakes or errors, please let me know so that I may make corrections.

Edited by Anarkavre

Share this post


Link to post

I have not seen it, but I will read it. I was curious how they worked for use in my ray-casting engine. Once I learned everything, I thought the knowledge may benefit others. I was planning on writing this article for a few months. I had the figures, notes, and a rough draft, but I only wrote the full article today. I wasn't aware of anything that went in depth and I wanted to try to explain it as simple as possible.

Edited by Anarkavre

Share this post


Link to post

@Anarkavre Nice, straight-forward description, and interesting post! Thanks for taking the time to write this.

Share this post


Link to post

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
×