How to take a screen and turn it into a pseudo 3d

not exactly sure how to come at this so bear with me here.

imagine that you turned the screen of a game into a painting canvas and you wanted to view the canvas at an angle, or that you wanted to take control of a camera, viewing the game, and then adjust its angle so that it could extend the game out into the distance so that you could fit more objects, which normally would not be viewable when top down.



how would i begin learning how to do this with gms? its not true 3d because im not interested in walking around or jumping over any objects (the gameplay has no z-coordinate to take into consideration) but i just want to be able to view it from an angle so i can see stuff that normally would be off screen.

thank you.
 
D

dannyjenn

Guest
The basic idea is that you take your top-down room and you slice it up into several 1-pixel tall horizontal strips. You then adjust the y coordinate of each strip such as to place it between the horizon line and the bottom of the screen, while also scaling up the width for the lower strips.

If you don't understand what I'm saying, I could try making an example project for you to look at.

This would probably best be done using a shader... you can do it without a shader but there's a lot of drawing involved so it might not run very smoothly. (Unfortunately, I don't know enough about shaders to do anything like this yet.)
 
yes this is new territory for me as well @dannyjenn and i worry about how labour intensive it might be.

right now im hoping their might be a tutorial on this somewhere because i have everything i need on screen i just want to give the players a bit more room to see what is coming.

my best guess was that i could somehow use a surface to treat the entire room as if it is an image and then scale it but im just pulling stuff out of the air and dont know the practical code to do it.

thank you
 
D

dannyjenn

Guest
Interesting. I'll have to try that out. I'm thinking it probably won't work though, due to the warning it gives:
if you use this function on a sprite, you will get different results and may experience texture "shearing" due to the way that a sprite is constructed from a quad of primitives.
(I'm guessing that means you'll probably get a seam along the diagonal...)

edit - Yeah, I just tested it and there's a seam.
But come to think of it, check the Marketplace. I think somebody once made an improved version of the draw_sprite_pos() function which does not have a seam. That might be something worth looking into.
 
Last edited by a moderator:
ok that draw sprite by keevee looks like a very good start.

now the million dollar question is if their is a way to take a snapshot of a room in an efficient manner.

i know it is possible to take a screenshot of a room because i have seen it done for pause buttons (prior to disactivating everything you would take a shot and then throw it up behind the pause buttons) but i dont know if that can be done 30 times per second...

https://forum.yoyogames.com/index.php?threads/draw-a-screenshot.18389/

Code:
sprite_create_from_surface
@RangerX any insight would be much appreciated if their is an effective way to use this to create the side view of a snooker table without grinding the fps to a halt.

thank you
 
D

dannyjenn

Guest
If your room has static backgrounds, you wouldn't need to take a screenshot of the room every step. You'd just need to take the screenshot one time, when the room starts up.
 
By "real 3D", I mean just tilt the camera and switch from an orthographic to a perspective projection. You can use d3d_set_projection_ext to do both those things. If you keep -z as up, then to stop textures from being flipped, use a negative aspect ratio [Why? using built-in gamemaker functions to go from orthographic to perspective switches from right-handed to left-handed coordinate systems].

Example: camera located at (0,384,-200), looking mainly toward +x (2000,384,0)
Code:
d3d_set_projection_ext(
    0, 384, -200,
    2000, 384, 0,
    0, 0, -1, 
    40, -1024/768, 1, 10000
);
The only other aspect of this would be to stop gamemaker from trying to cull certain things it thinks are outside of the view. Yoyo, if you're reading, there should be an option to turn off this behavoir. One way to get around it, that works in GMS1 at least, is to set up a view that you won't actually draw anything with, but which has a view area large enough (and correctly positioned) to encompass all the stuff you want to be shown in your perspective projection. That way, you make gamemaker believe everything is within the "2d view", and thus it wont try to cull them.
 
Last edited:
D

dannyjenn

Guest
Yeah, I'd suggest just using real 3D.

I took a shot at the fake 3D, and mine looks fairly good so far... but it was a lot of work setting it up, and it's still far from perfect, and still needs additional code (which I haven't written yet) to reposition and scale all the sprites (right now it only scales the tilemap). A lot of things need to be done a certain way with my implementation, so it's is not very flexible... and I don't think the performance is all that great.
Screen Shot 2019-02-16 at 8.37.53 PM.png Screen Shot 2019-02-16 at 8.38.15 PM.png
If you're interested in seeing my project folder, let me know and I'll tidy it up and upload it. Otherwise I won't bother.

Real 3D would probably be a lot easier. One of these days I'll need to look into 3D (never tried it before).
 
@dannyjenn

Sure if you would be willing to share how you did this i would be more than curious.

ok for now im going to tinker with the caerma perspective to see if i can get it working.

thank you.
 
@flyingsaucerinvasion

i tried putting your code in and didnt notice any change, undoubtly because i am using it wrong.

how would i correctly set it up so that i could take everything on screen at one time and then tilt it?

thank you.

______________________________
just discovered that depth is highly relevant to getting that code to work, now to figure out how to get the effect i want.

still dont know what i am doing but ill tinker for a bit.

thank you
 
@JML did you sort it out? It should be pretty straightforward. Just set the projection and then draw stuff. Don't forget what I said about gamemaker needing to believe what you're drawing is inside of the 2d view. If that becomes a problem, just set the 2d view (view, not port) to encompass your whole world. It won't have any bearing on how 3d stuff draws except it will stop gamemaker from auto-culling things.
 
@flyingsaucerinvasion

im making some serious headway and im actually quite amazed and glad how simple this is to do but im having some proportion issues.

imagine a variable representing the length of a "canvas" (ie everything i want drawn on the screen). what im trying to figure out right now is how to freely manipulate both the zfrom (the height of the camera) and the canvas length but still maintain an optimized prospective of the canvas.

i do know that the aspect argument would be a proportion between the canvas length and room_width and i likely always want the camera pointed at the exact center of the canvas (the "to" functions") but i cant figure out how to adjust the angle argument and the y from argument so that the bottom left and right of the canvas always stay in place while the upper left and right adjust the x distance between them while always staying at the top of the screen.

Code:
aaa = canvas length
bbb = canvas length / room_width
ccc = camera height

rrr   = view angle
sss = yfrom

d3d_set_projection_ext(
    room_width/2 , sss, ccc,
    room_width/2, room_height/2, 0,
    0, 0, -1,
    rrr, bbb, 1, 10000
);
any idea how i could adjust the view angle and the yfrom so that, regardless of the canvas length and the camera height, everything stays on screen?

thank you.

 
EDIT: The diagram has been fixed, because it contained an error before, and updated to better explain the math.

There's a few different ways to interpret your question, because at this late hour, I found the language a little confusing. I'll give one answer for now, and you tell me if it is suitable.
upload_2019-2-19_16-7-37.png
Assumes a known world width (w), and camera fov and pitch and aspect ratio

In this case, the angle "pitch" is measured from the z axis.

What we're calculating here is...

(v) the distance of the camera from the x/y plane. Remember, if your camera's position is at a negative depth (because up is -z), you'll use -v.

The following vlues are for when you want to position the camera such that a certain point in the world is drawn exactly at the bottom of the view port.

(h) is the horizontal displacement from the location of the world you want to be at the bottom of the screen. If you are looking toward positive x then use -h.
(h2) is the distance from the camera's position in the x/y plane that the camera's look-at target should be located at. If you are looking toward positive x, then use +h2.

Example of usage:

In this example, the camera is pointing generally in the +x direction. A portion of the world along the y axis, equal to length of "w" will fit exactly within the bottom of the view port. And the camera is situtated such that the 3d world position (x,w/2,0) is drawn exactly at the bottom of the view port.
Code:
var _d = w/(2*aspect*dsin(fov/2));
v = dcos(pitch - fov/2)*_d;
h = dsin(pitch - fov/2)*_d;
h2 = dtan(pitch)*v;
d3d_set_projection_ext(
    x - h, w/2 , -v,         //cam pos
    x - h + h2, w/2, 0,   //look at pos
    0, 0, -1,              //up vector (can get away with using 0,0,-1 in most cases)
    fov, -aspect, 1, 10000
);
 
Last edited:
@flyingsaucerinvasion

ok that was a huge amount of information, i still think much of it went over my head but i think i got it working.

thank you.

few more questions.

you mentioned about auto-culling any object that goes off screen and to adjust the 2d view, do you mean by using these functions? and how would i set them up correctly?
view_hview
view_wview

i have figured out how to put objects behind the canvas (for instance to put in a background) and i can put sprites and debug text up no problem, but if i want to put a sprite in front of the canvas depth wise (but not on the canvas), how would i got about doing that? having clouds drifting by would be nice.

last two questions.

if i wanted to make a pseudo 3d object, like a cube, that could rotate around and look like a cube but would not actually flip over, how would that be done?

and if you have an object that is on the canvas, like lets say a tree, but you want to make it looks like it is standing up, how would that be done?

thank you.
 
If you want to do more in 3d, you'll want to look into drawing models, or vertex buffers. And you'll want to learn about matrices and shaders.

You've got a lot of questions in your previous post, some of which I didn't completely understand. How about tackling the questions one at a time? Which one are you most interested in looking into first?
 
@flyingsaucerinvasion

sorry about the deluge of questions, im writing and coding as i learn.

i have figured out a good enough solution for the canvas problem, thank you.

as for the culling problem, although i dont know how to fix it, i did find a way to make a fake canvas, drop it on top of the real one, and if i work within the fake one all is well.

ok so here are the three questions i am struggling with right now.

1)how can i put a sprite in between the camera and everything the camera is seeing? i know that if i plug debug code into the camera object before the d3d prospect code is ran that the debug stays in place but if i try to put something in after, nothing shows up.

2) if i have a sprite on the canvas, how do i adjust for distortion so that it can appear to look like its standing up?

3) and where can i begin learning how to draw quasi 3d objects like cubes, that will rotate around when the square underneath it is rotated but no not need to flip over.

thank you for all your help.
 
1)how can i put a sprite in between the camera and everything the camera is seeing? i know that if i plug debug code into the camera object before the d3d prospect code is ran that the debug stays in place but if i try to put something in after, nothing shows up.
Do you mean gui stuff? You can either draw in the gui layer or else switch back to an orthographic projection using d3d_set_projection_ortho().

2) if i have a sprite on the canvas, how do i adjust for distortion so that it can appear to look like its standing up?
I think what you're talking about here is what is called z-axis billboarding. There are three different ways you could do this. a) use d3d_draw_wall. b) use a matrix to rotate and position a regular sprite. c) use a z-axis billboarding shader. I'll usually use a shader because it's most straightforward for me. Which one(s) would you like to try?

3) and where can i begin learning how to draw quasi 3d objects like cubes, that will rotate around when the square underneath it is rotated but no not need to flip over.
One of the best places to start is to look in the manual. You can look into the d3d_draw functions, d3d_model functions, or go stright for the jugular with the vertex_buffer functions.

NOTES: I previously advised you to flip the aspect ratio in order to get textures to be correctly oriented when looking at the world from a negative position along the z axis. One downside of this is that vertex winding orders will be reversed, which will impact backface culling. So a better thing to do would probably be to use a positive aspect ratio, and just look at the world from a +z position. So why did I say to set the camera at a -z position in the first place? Under certain circumstances that could help maintain consistency with the way gamemaker draws in 2d. However after thinking about this some more, that probably wont matter. So it would probably be better just to look at the world from a +z position.

Another thing you'll want to be aware of is that if you use d3d_start, (which gamemaker will force you to do if you want to use the zbuffer or to turn on backface culling), then you should be aware that instance's "depth" will become the z component of their position. Now, if you don't want your instances to float above or below the ground, then their depth either needs to be zero, or else you need to use one of the draw_sprite functions, or else you'll need to draw your sprites as part of a 3d model, a vertex buffer, or using d3d_draw_wall/floor.
 
Ok it looks like im going to be diving in and learning about vertex buffers, i already have some experience with doing the 2d version, if i can figure out how to initialize them i think i can go from there.

thank you very much for helping me begin to understand how these functions work, i got a ton of reading to do. :)
 
Top