How to make your own 3d! (3d projection)
3D tutorial (by this_name_is_taken)!
So, I was considering making a tutorial about this using the new tutorial functions for projects. But making an article sounded more interesting! If you have any comments, questions or feedback (feedback would be greatly appreciated!) feel free to let me know down below! Also, would you me to make a raytracing tutorial? Last of all, remember to press the like button if you approve of this tutorial!
Anyway, this article will be covering a method of 3d, called
3d projection. The basic idea of 3d projection is that you get a starting set of 3d points, rotate them around the camera, and then finally project the 2d points onto the screen.
This tutorial aims to:
- Teach how to make first person 3d through the method of 3d projection
- Give examples of what the code would look like.
- Give a basic understanding of the concepts and why everything works
- Give some areas for improvement (eg. shape filling)
Part 1: 3d to 2d projection.
3d to 2d projection is the formula we use to project a set of 3d points onto a 2d screen. The formula for this is outlined below (please note that FOV stands for the field of view, which is how wide your vision is. This should be set from ~100-300):
3dprojection=function(x,y,z) //X is side to side, Y is up and down, z is depth outputx=FOV*x/z //Divide both x and y by z and multiply by FOV outputy=FOV*y/z end
Basically, this works because things further away are smaller. As objects get further away the z position (depth) will be higher, and the difference between the point and the centre of the screen will become smaller and smaller. The FOV bit is basically just to adjust.
Part 2: 2d rotation.
The next step is learning 2d rotation (we will implement this into our 3d rotation later). Basically, we will use sine and cosine to do this, as they have an interesting property which allows them to rotate points in a circle. See the
unit circle for an example of this. Here is the code to rotate two 2d points by the angle 'rot'
rotate=function(x,y,rot) //Rotate two points x and y by the angle 'rot' rotx=cosd(rot)*x-sind(rot)*y roty=sind(rot)*x+cosd(rot)*y // I use cosd and sind instead of cos because I prefer to work in degrees end
Now that we know 2d rotation, how to we apply that into 3d rotation?
Part 3: 3d rotation
How we use 2d rotation to get 3d rotation is by rotating the x with the z by the x-rotation, and then the y with the z by the y-rotation. So basically this:
3drotation=function(x,y,z) rotate(z,x,camxrot) // camxrot is your x-rotation outputx=roty // Final x-value rotate(y,rotx,camyrot) // Use the updated z-value. Just to save space outputy=rotx // Final values outputz=roty
And that's our 3d rotation done!
Part 4: Drawing a line
Our last step in the rendering process is finally drawing a line. With all of the scripts already in place, it is surprisingly simple! We also have to remember to subtract the camera position as our first step. What this does is it allows the camera to move and also means that we are rotating the points
around the camera instead of
around a point. This is the difference between seeing a cube rotate around it's centre on the screen and first person 3d. So here is the code:
drawline=function(x1,y1,z1,x2,y2,z2) //Draws a line between these two 3d points //Rotation 3drotation(x1-camx,y1-camy,z1-camz) //Camx,camy and camz are our camera position X1=outputx Y1=outputy Z1=outputz //Point 2 3drotation(x2-camx,y2-camy,z2-camz) X2=outputx Y2=outputy Z2=outputz //3d to 2d projection if Z1>zclipdist or Z2>zclipdist then //Both points are behind the camera, don't render. //If only one point is, then we z-clip (covered later) 3dprojection(X1,Y1,Z1) //Get final 2d coordinates X1=outputx Y1=outputy 3dprojection(X2,Y2,Z2) X2=outputx Y2=outputy screen.drawLine(X1,Y1,X2,Y2,"rgb(0,0,255)") // Draws a line between the final points, with a blue RGB colour end end
Phew, that was a massive script! Now we only have three steps to go (bear with me here). The third last step is z-clipping.
Part 5: Z-clipping
Z-clipping is necessary when one point is behind the camera (has a negative z-value) and the other isn't. This makes lines wrap around the screen (as the x/y value swaps when the z is negative/positive). We fix this through
interpolating the x and y and between the two points using our z-clipping distance value, and setting the z position to the z-clipping distance. Basically, we change shorten the line if part of it is behind the camera to where it should be. The z-clipping distance should be anywhere from 4-10. This is the code:
zclip=function() percent=(zclipdist-Z1)/(Z2-Z1) //Decimal number from 0-1 representing how much line is behind camera if Z1<zclipdist then X1=X1+(X2-X1)*percent //Interpolate back up the line Y1=Y1+(Y2-Y1)*percent Z1=zclipdist // Set z position to the z clipping distance end if Z2<zclipdist then //Same thing but for second point X2=X1+(X2-X1)*percent //Position will be the same as it would have for Z1 Y2=Y1+(Y2-Y1)*percent Z2=zclipdist end
Now we put that into our draw line script!
drawline=function(x1,y1,z1,x2,y2,z2) //Rotation 3drotation(x1-camx,y1-camy,z1-camz) X1=outputx Y1=outputy Z1=outputz //Point 2 3drotation(x2-camx,y2-camy,z2-camz) X2=outputx Y2=outputy Z2=outputz //3d to 2d projection if Z1>zclipdist or Z2>zclipdist then zclip() //Z-clipping goes here! 3dprojection(X1,Y1,Z1) X1=outputx Y1=outputy 3dprojection(X2,Y2,Z2) X2=outputx Y2=outputy screen.drawLine(X1,Y1,X2,Y2,"rgb(0,0,255)") end end
Now that we have a successful draw line script, we can (finally) get to drawing our cube!
Part 6: Drawing our cube!
This is our second last step, drawing a cube. This is a very basic 3d shape and definitely has room for improvement. How we are going to do it is by rendering the front face, back face, and then the connecting lines. So here is the code:
cube=function() //Front face (note that cube is centred at 0,0,0) drawline(50,50,-50,-50,50,-50) drawline(50,-50,-50,-50,-50,-50) drawline(50,50,-50,50,-50,-50) drawline(-50,50,-50,-50,-50,-50) //Back face (same thing but with further z-position) drawline(50,50,50,-50,50,50) drawline(50,-50,50,-50,-50,50) drawline(50,50,50,50,-50,50) drawline(-50,50,50,-50,-50,50) // Connecting lines (change z in each line but not x and y) drawline(50,50,-50,50,50,50) drawline(-50,50,-50,-50,50,50) drawline(-50,-50,-50,-50,-50,50) drawline(50,-50,-50,50,-50,50) end
And now we are done with our code! Now we are finally at our last step, which is realtime movement! This is perhaps the best part of the whole scripts.
Part 7: Movement
Movement is relatively simple. We will use WASD for moving forward and backward, and we will use arrow keys to rotate the camera. So here is the code:
movement=function() //First we will use arrow keys and rotate the camera if keyboard.ARROW_RIGHT then //Right arrow key pressed camxrot+=4 // You can change the number '4' for faster or slower movement end if keyboard.ARROW_LEFT then camxrot-=4 end if keyboard.ARROW_UP then camyrot+=4 end if keyboard.ARROW_DOWN then camyrot-=4 end //Now we will do movement (WASD). This movement is dependent on the camera direction. //We account for this by using the rotate script from earlier! if keyboard.W then rotate(0,5,camxrot) //Rotate 5 units in the forward direction by camera x direction camx+=rotx //Change camx and z by outputs camz+=roty end if keyboard.S then rotate(0,-5,camxrot) camx+=rotx camz+=roty end if keyboard.A then rotate(5,0,camxrot) camx+=rotx camz+=roty end if keyboard.D then rotate(-5,0,camxrot) camx+=rotx camz+=roty end end
Now we are finally done with all of our scripts! All we have to do now is to put them in our init, update and draw blocks!
init=function() camx=0 //Play around with the values to check if it works! camy=0 zclipdist=5 //Can be anywhere from ~4-10 camz=-400 FOV=200 camxrot=0 camyrot=0 end update=function() movement() end draw=function() screen.clear() //Clear screen (necessary for movement, as otherwise the screen would be filled with all our different outcomes) cube() //Draws our cube end
So that's it for this tutorial! Finally! Remember, If you have any comments, questions, feedback (feedback would be greatly appreciated!),or it's not working, feel free to let me know down below!
Should I make a ray tracing tutorial? Also, down below should be some improvements that advanced coders can try!