godot
godot copied to clipboard
Implementation of new Camera3D type: Oblique
Closes: https://github.com/godotengine/godot-proposals/issues/501 Partial solution to this proposal: https://github.com/godotengine/godot-proposals/issues/2713
This PR should not necessarily be a reason to close the proposal 2713, it is merely a straightforward implementation of oblique near plane clipping to implement a portal--or other similar--mechanic.
This PR is essentially an update to my previous PR: https://github.com/godotengine/godot/pull/64876 The main difference is that shadows were fixed.
This other PR attempts to completely solve proposal 2713 mentioned above, but apparently is having issues with shadows. Considering that its commits try to implement custom projections by adding a new camera type like with my PRs, there is a good chance the way I fixed shadows could also work there too: https://github.com/godotengine/godot/pull/84454
The solution was that along with the main Oblique camera projection a separate, normal Perspective camera projection needed to be included with the CameraData used in the RenderSceneCull::_render_scene() that was used explicitly for lighting and shadows.
However, regarding the potential question of whether one PR should be included over the other as a solution, clearly the more general solution can see a wider range of uses. However, I believe this implementation of an Oblique Near Plane camera still has merit based on the majority use case of creating a portal mechanic, etc where oblique near clipping is necessary.
This is because the interface for the Oblique camera included in this PR has been simplified so that users are only required to provide at a minimum the transform of the plane they wish to use as the near clipping plane of the camera using set_oblique_plane_from_transform().
Additionally, because all of the required math (especially the matrix math) is happening in C++, the effect is as efficient as possible, without requiring steps to first camera.get_projection, then doing matrix manipulation, then camera.set_projection all in GDScript.
Alternatively, because the Oblique Near Plane matrix manipulations "... can be applied to any projection matrix, ..." (https://terathon.com/lengyel/Lengyel-Oblique.pdf, pg. 2) it's possible that all camera types (including the other proposed custom projection camera) could benefit from a general is_oblique_near_plane boolean toggle instead of this dedicated oblique camera which only implements the algorithm for a Perspective projection.
Video 1: Current state of personal portal game mechanic https://youtu.be/A_-2nTyKVkU Notice that the portal viewports are not blocked by the surface the portal is on (the issue with using a standard perspective camera), and that shadows and lighting appear the same when looking through the portal (as evidenced by walking through the portal where a seamless transition occurs).
Video 2: Previous video showcasing dynamically the difference oblique near plane clipping makes https://youtu.be/PipVd6wRnMk?si=lCThReaKyO-smBaP Note that this older video did not properly handle shadows, and the viewport texture color and environment lighting were all wrong giving it that washed out look (both fixed in Video 1).
Image 1: Portal effect using Perspective camera
Image 2: Portal effect using Oblique camera
Image 3: Oblique camera editor interface
@Calinou There are two camera member variables: oblique_normal, and oblique_position, which are exposed in the editor for this purpose. Use the transform origin for position and transform forward vector for the normal.
@qaptoR Can you upload a testing project? I haven't been able to figure it out so far.
@Calinou I just got home from classes.
- I will post a minimum testing project asap.
- I will also test the project you posted on my system to see if everything works:
- I'm building/testing on Windows 10, Intel 10900K, Nvidia 3090.
- then work on the code revisions that have been posted so far.
@Calinou Still going to work on a minimum test project, but in the interim I figured out what was wrong with your test scene.
The path to the Camera3D in water.gd on line 12 should be:
$Water/SubViewport/Camera3D.set_oblique_plane_from_transform(global_transform)
I was unable to open the test scene until this was corrected because it is an @tool script.
From there the oblique camera works correctly for me.
@lyuma You were looking into this. https://github.com/godotengine/godot/pull/85529
@Calinou Here is a minimum portal test project. Some things to note about the scene I set up
- the BackingMesh's are invisible to the portal cameras purposefully because they're mostly there to keep tabs on where the portals are as you move around
- when you look through the portal at the other portal, the visual distortion that looks like you're painting on the portal with what else is in view is normal because we're not handling recursion
- there are visual indicators behind each portal so that when you switch the cameras to perspective mode it's obvious what the oblique camera is culling
EDIT: Fixed a quirk with the test scene where the viewport textures may appear pink when first loaded.
another important feature in addition to portals is Planar reflections, which require the ability to flip the final projection matrix (UV space), since this is necessary to preserve chirality of triangles (backface culling) in a mirrored setting.
for example, in my code, I do this by adding a flip in the projection here:
p_cam.set("override_projection", Projection(Transform3D.FLIP_X) * proj * Projection(Transform3D.FLIP_X))
and then perform the flip again in the shader to undo this:
uv = vec2(1.0 - SCREEN_UV.x, SCREEN_UV.y);
Without this flipping, it is quite complicated to implement mirrors: there will need to be some way to communicate that culling is inverted (In Unity users do this with GL.invertCulling = true but inverting UV space is IMHO far simpler and doesn't require changing the renderer.
Just thinking aloud, but this is a problem I ran into, though it was easily solved in my case since I was using the PR with a customizable projection matrix.
An oblique projection is a parallel projection.
Here is a picture from Wikipedia:
It is commonly used in technical drawing. But it has also seen use in video game art, as most 2D RPGs use some kind of oblique projection (where you see the top face and the front face on full size). A notable case is EarthBound that uses a cabinet projection.
And thus such projection could be useful for anybody trying to mimic the look of those games with a 3D scenes (consider for example the trick used for The Legend of Zelda: A Link Between Worlds).
But it seems you are not doing that. Instead it seems that what you are doing is an oblique frustum projection. Please call it what it is. PROJECTION_OBLIQUE is wrong.
I've updated the PR to reflect the alternative approach to applying the oblique algorithm.
Now, there is a flag use_oblique_frustum which is currently only available for the perspective and orthogonal cameras because as far I am aware the more general algorithm brought to my attention by @lyuma only works for those two projections.
This new approach also reduces the amount of duplicated code that was used for the oblique camera previously.
I've also updated the test project I posted before with a new scene meant to test oblique algorithm on orthogonal cameras. Some notes about the project in general
- for both scenes the portal viewport paths need to be reset when the scene is loaded:
PortalSide[AB]>mesh>material>shader_parameter/TextureAlbedo>viewport_path - the
orthogonal_oblique.tscnportal camera frustums seem to be affected in unreliable ways. The boxes/prisms in the scene are placed such that if they are moved further/closer from the position you find them in when the scene loads will result in them being culled by the far plane.- on the good side, the near plane does appear to be culling appropriate to the oblique plane
EDIT:
I think I found the issue. I'll fix it when I get home from class. I'm referencing the wrong index at
projection.cpp:line108 where it should be [0][3] instead of [0][1].
EDIT:
Okay, the columns reference is fixed.
Hi there! I'm currently working on a project that needs portals and mirrors, and I'm in dire need of this feature. I see it's been completely written up and working well, which is awesome! Since it's been a few months since this was finished, I'm wondering: are there's any blocks on merging? I'm quite new to contributing to open-source projects, but if there's anything I can dedicate time to so this feature gets integrated as soon as possible, I'd love to know what needs to be done.
@Ribiveer I'm not here to say whether anything can be done sooner or later to merge, I'm new to this process as well, despite having made several PRs over the years, they all end up in stasis and I suspect that is either because a) i don't push the godot team enough to remind them of my features, or b) because my PRs are predominantly new features there is an opposing force to them being merged which is that strive for stability in the core engine which new features tend to disrupt leading to slow integration of an already long backlog.
what I would recommend is getting comfortable building from source, and merging different branches/prs into a single working engine that you call your own. this is what I do, and it means that you can have all the features that you want that are stuck in stasis.
obviously I hope that my work gets included in the engine core eventually and I can be counted officially as a contributor, but I also want the current features of the engine to be robust and bug free, so I am fine with the godot team focusing their effort there.
I am also grateful that you appreciate my work, and I hope that if you do choose to use it still in the interim that it serves well enough for the purpose you intend.
@qaptoR Thank you for your thoughtful reply! I think I've been delaying the inevitable, getting comfortable building from source is indeed the best strategy here. In the meantime I hope for you that your PR's get approved! I've looked through your repository and they seem like useful additions indeed.
I've copied this pull request's commit in my fork, and have been testing it today.
It's working extremely well! It surprised me that the given coordinates were in world space. While this perfectly serves most purposes this feature will used for (most of the time it will be used to align the clipping plane with an object in the world), an argument could also be made that, since all other camera parameters are local to the camera, this one should be as well. Personally though, this is perfect for my (and I assume most people's) needs!
It doesn't seem to play nicely with the automatic LOD system, however. Looking at the clipping plane from straight on, Godot assumes a normal distance for meshes. But when looking at the clipping plane at an angle, Godot seems to assume more and more radical distances, causing objects to assume a lower LOD than they should. I looked around and found issue #81995 and #76436 that might be related, but further investigation would be necessary for implementation in Godot.
For my own purposes, this PR perfectly suits my needs, since I am not planning on using Godot's built-in LOD system. For integration into the engine there's either a fix needed for working with the LOD system, or it might actually work itself out when the LOD system gets worked out.
That, or it might have already been worked out in the master branch, and my using of 4.2's stable branch messes something up. Keeping open that possibility!
Thank you so much for making this PR!
Now that 4.3 is out, there is another chance for this feature.
It seems everything from prior reviews has been addressed. So I hope it goes smoothly.
Thank you @qaptoR for this contribution! I am also experimenting with portals and was initially disappointed that Godot doesn't support "oblique projection", only to discover that your PR adds this feature! :heart: I compiled the engine from source using your branch and it works exactly like expected! I didn't check all the shadow and LOD issues mentioned above, but for my limited experiment, this is exactly what I need :smiley:
I hope that your PR will be merged soon and Godot will start supporting this awesome feature out-of-the-box :wink:
This feature would be very helpful for water reflection as you can go through water like a portal even though your position doesn't change. Especially if it works for compatibility renderer as we don't have access to screen_space reflection.
Awesome looking PR! Really looking forward to its merge. Fixes this awful artefact with planar reflections. Pink geometry should be clipped by the planar reflection camera which should be possible with an oblique camera.