Ajreckof - mechanically challenged portals#3
Open
ajreckof wants to merge 1 commit intoMechanicallyChallengedOrg:mainfrom
Open
Ajreckof - mechanically challenged portals#3ajreckof wants to merge 1 commit intoMechanicallyChallengedOrg:mainfrom
ajreckof wants to merge 1 commit intoMechanicallyChallengedOrg:mainfrom
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
** Warning : This is mostly an experiment and is not a functional implementation. Please use this as a learning material more than a fully featured implementation.**
** Warning : In the rest of this I will try to explain as best I can all I found about portals with as much drawings as I can but it will be mostly theoretical and not centered on the implementation as I was unable to make it work **
I mostly focused myself on the visuals of seeing through a portal and not on crossing portals or any physic interaction.
Basic Portal mechanic
The basic of a portal is to warp space between two point to make it look as if they were connected. Unfortunately we can't really warp space. So to make it look like space is warped visually we are rendering a view of the scene as if it was wrapped. So if we have this two portal and a character looking through the red portal like below.



we need to first calculate where the transform of where the portal would be if space was wrapped. The portal would placed at the location of the orange portal. The transform of the orange portal corresponds to the transform of the exit portal rotated of 180° around it's local up vector.
Then we need to express the character transform (actually it's camera) and express it in local coordinates of the entrance portal. this is obtained by multiplying the true inverse of the camera (left) to the character's transform (right). And then use this local coordinates in the warped portal coordinate to do that we simply need to multiply the warped portal transform (left) byt he local coordinates obtained before (right). This would give something like that in GDScript
exit_camera.global_transform = out_portal.global_transform.rotated_local(Vector3.UP) * ( entrance_portal.global_transform.affine_inverse() * main_camera.global_transform)Optimize transform calculation
Since we are going to update the transform everyframe as the
main_cameramight change each frame, the lesser calculation we have the better we will be especially is we plan to have more portals or to see through portals inside portals.Fortunately this
exit_camera.global_transform = out_portal.global_transform.rotated_local(Vector3.UP) * ( entrance_portal.global_transform.affine_inverse() * main_camera.global_transform)is the same as thisexit_camera.global_transform = (out_portal.global_transform.rotated_local(Vector3.UP) * (entrance_portal.global_transform.affine_inverse()) * main_camera.global_transformwhich means we can isolate the left part into a variable and only change this variable if one of the portals transform changes.Prevent camera occlusion
Most of the time the portal will be put on a wall. Unfortunately if the wall as two side the camera will most likely come on the other side of the wall which means instead of rendering what is beyond the portal it will only render the wall on the other side. To reduce this problem we have the possibility on Cameras to set a near cut-off where anything closer then this value won't be rendered. The value to be chosen will be the highest value such that everything beyond the portal will always be rendered. To calculate this value we should project all corners of the aabb enclosing the portal's mesh on the camera forward axis and take the minimum of all values. Unfortunately this isn't perfect and can still lead to case where some occlusion happens. Here below is a drawing of one of the worst case scenario. In this scenario, objects as far as
tan(fov/2)times the size of the portal can be seen. This means for a standard 75° vertical fov and a 2M high portal you should have a wall that is at least 1.5m thick which is absurd. Even if we removed near impossible asumption (camera infinitly far away) anything inside a 1m range could still be seen in some bad cases. This is why I would recommend either one to not put anything on the other side or put it in separate visibility layers. First option is the easiest but second one would give a higher flexibility on levels.Seeing through multiple portals
Seeing through a portal might seem like something we have mastered by now but unfortunately this is far from it. The hardest questions comes now : What should happen if you look through a portal and see another portal in there?
The first answer might be why not just the same thing. Let's see why this wouldn't work. Let's see what happens in the following situation where a green character looks through a red portal which goes to a blue portal.


As previously stated we should render what the world would be if it was looked upon from the orange character.
This orange character also see the red portal but from a totally new perspective. This means the right thing to show to the orange character isn't the same thing we show to the green character. Effectively the right thing to show is something as if it was seen from the purple character below.
This raises two question
Where to stop the chain
As we've seen each view of a portal needs it's own camera which means if you're facing towards a portal linked to a portal behind you, you would see an infinite number of portals (or at least a really large number of them) which would use a ton fo resources for no real gain. Therefore there is a need for a criteria to decide when do stop doing the right thing and do the previous thing which is nearly right. This can be done by three criteria :
Depth level is by far the easiest one as it only needs to register the depth and increment by one each time you make a camera to create a view. number of pixel on screen seems easy to do as using the aabb and transform but might need a little reflection on the math. The camera position error seems the hardest as it seems really hard to determine how much a rotation difference will modify the view compared to a distance difference.
How to make each camera see a different thing
Here the answer is actually quite straight forward as there is already a functionality in Godot called cull_mask. We just need to assign a cull_mask to each camera and then each view that renders for a camera should have it visibility_layers set to contain the camera personal cull_mask.
what is actually implemented here
In this repo most of what is described above as been implemented meaning placement of all cameras, how to make each camera see different things and the depth level stop criteria. Unfortunately there is one major bug which is that unfortunately it seems that all viewport are rendered simultaneously which means my implementation has black box where there should be the portal inside portals :(