today I'm bringing a subject about Pseudo 3D and perspective.
I was checking the video #1 Java Classical 3D Rendering Tutorial : Creating 3D World where he used a method for render pseudo-3D ceil and floor. I tried to find some tutorial or the name of the method that he used but I didn't find. I saw the algorithm but it is not clear to understand. I started to search about perspective graphics (vanishing points, horizon...) but the unique thing that I got was static drawing. I wanna apply an illusion moving putting the camera inside the plan and moving it. Under follow an example about the perspective floor and ceiling that I wanna make.
This is just an image, but my first question is: "I realy can make a movement of the camera in this ambient, like rotation and move x, and y axis?". I tried to make 2 vanishing point in a canvas, creating lines for each degree of 15º, and I got a perspective illusion, but i couldn't find a way to make the rotation, or the movement. In that video I saw the pixels creating 2 dimensions using just the colors green and blue, but I wanna make this using lines, to understand how it works.
There isn't a place that is teaching step by step how to make the perspective with movements. I didn't find. I checked the videos of 3D game maker in Java and the Markus Person creating the game called by "Prelude of the chambered" using the method of the video, but I didn't find an explanation for this king of rendering.
Lets supose I have to create a plan using a grid. how is the logic that I have to apply in the lines to create the movement? I realy wanna understand the logic to make this kind of pseudo-3D, without using frameworks or thing like that. Thanks for help me! I will wait for your answer.
I checked something about MODE 7 of SNES. This is a good way for make it I think. I have just to understand how it works, and how to make the rotation.
** Note: I don't what to use raycasting for it. Raycasting I'll use to create the walls.
I found the method that was used to create the perspective view in old games. Check my tutorial here: http://programandocoisas.blogspot.com.br/2017/09/mode-7.html. The method's name is MODE 7. I made a tutorial to help wo wants to implement and understand it. The formula to make the mode 7 on a texture is:
Z you can use to create the depth. This variable is just an incremented variable on the Y coord. After get the _X and _Y new coords, just use these coords to get the pixel in the texture that will be mapped, and insert this pixel in X Y coord in the render view.
Here is the pseudo-code: Basically, the pseudo code is it:
Here is the code:
This is the result:
Interesting problem. I did not resist and code it for fun so here some insights... Well there are 2 basic approaches for this. One is raster fake and second is Vector based. I will describe the latter as you can do much more with it.
Vector approach
This approach is not faking anything it really is 3D. The rest depends on the rendering you want to use this for... For now I assume you can render 2D lines. All the code chunks are in C++.
Transformations
You need vector math to transform points between world and camera space and back again. In 3D graphics are usually 4x4 homogenuous transform matrices used for this and many programing APIs support them natively. I will base my math on OpenGL matrix layout which determine the order of multiplication used. For more info I strongly recommend to read this:
As I use a lot from it. The linked answers there are also useful especially the 3D graphics pipeline and Full pseudo inverse matrix. The Answer itself is basic knowledge needed for 3D rendering in a nutshell (low level without the need for any lib apart of the rendering stuff).
There are also libs for this like GLM so if you want you can use any linear algebra supporting 4x4 matrices and 4D vectors instead of my code.
So lets have two
4x4
matrices one (camera
) representing our camera coordinate system and second (icamera
) which is its inverse. Now if we want to transform between world and screen space we simply do this:where
P(x,y,z,1)
is point in camera coordinate system andQ(x,y,z,1)
is the same point in global world coordinate system.Perspective
This is done simply by dividing
P
by itsz
coordinate. That will scale objects around(0,0)
so the more far object is the smaller will be. If we add some screen resolution and axis correction we can use this:so point
0,0
is center of screen. Thexs2,ys2
is half of resolution of the screen andznear
is focal length of the projection. SoXY
plane rectangle with screen resolution and center at(0,0,znear)
will cover the screen exactly.Rendering 3D line
We can use any primitives for rendering. I chose line as it is very simple and can achieve much. So what we want is to render 3D line using 2D line rendering API (of any kind). I am VCL based so I chose VCL/GDI
Canvas
which should be very similar to yourCanvas
.So as input we got two 3D points in global world coordinate system. In order to render it with 2D line we need to convert the 3D position to 2D screen space. That is done by
matrix*vector
multiplication.From that we obtain two 3D points but in camera coordinate system. Now we need to clip the line by our view area (Frustrum). We can ignore
x,y
axises as 2D line api usually does that for us anyway. So the only thing left is clipz
axis. Frustrum inz
axis is defined byznear
andzfar
. Wherezfar
is our max visibility distance from camera focal point. So if our line is fully before or after ourz-range
we ignore it and do not render. If it is inside we render it. If it crossesznear
orzfar
we cut the outside part off (by linear interpolation of thex,y
coordinates).Now we just apply perspective on both points and render 2D line using their
x,y
coordinates.My code for this looks like this:
Rendering
XZ
planeWe can visualize the ground and sky planes using our 3D line as grid of squares. So we just create
for
loops rendering thex
-axis aligned lines andy
-axis aligned lines covering some square of somesize
around some origin positionO
. The lines should be somestep
far between each other equal to grid cell size.The origin position
O
should be near our frustrun center. If it would be constant then we could walk out of the plane edges so it woul dnot cover the whole (half)screen. We can use our camera position and add0.5*(zfar+znear)*camera_z_axis
to it. To maintain the illusion of movement we need to align theO
tostep
size. We can exploitfloor
,round
or integer cast for this.The resulting plane code looks like this:
Now if I put all this together in small VCL/GDI/Canvas application I got this:
Here the Form header file (you do not really need it unless you are reconstructin my VCL app)
The VCL app is just single Form with single timer (
100ms
) on it and no other VCL components. Thebmp
is just my backbuffer bitmap to avoid flickering. The keyboard events are just to enable turning and movement (with numpad8,9,4,1
).Here preview of above code:
Now if you want to add the whiteout visibility limiter that is done by Fog or Volumetric fog. You simply interpolate between rendered color and White based on parameter
t
:where
z
is pixel coordinate in camera space so:But to apply this in here we would need to encode the 2D line rasterizer or have 2D line api with per vertex color (like OpenGL). Another option is to fake it by blending in a Fog image which is fully solid near center line and fully transparent on the top and bottom edges.