diff --git a/doc/classes/Camera3D.xml b/doc/classes/Camera3D.xml
index 27194122d56..db8d3208626 100644
--- a/doc/classes/Camera3D.xml
+++ b/doc/classes/Camera3D.xml
@@ -202,6 +202,12 @@
The distance to the near culling boundary for this camera relative to its local Z axis. Lower values allow the camera to see objects more up close to its origin, at the cost of lower precision across the [i]entire[/i] range. Values lower than the default can lead to increased Z-fighting.
+
+ If set to a non-zero Projection, overrides the view-projection matrix passed to shaders with this custom matrix. This *only* overrides the projection passed to shaders on the GPU. The [constant PROJECTION_PERSPECTIVE], [constant PROJECTION_ORTHOGONAL] or [constant PROJECTION_FRUSTUM] values are still used for CPU-side operations such as culling, which can be used to provide a separate culling frustum.
+ To clear the overridden projection, set to [code]Projection(Vector4.ZERO, Vector4.ZERO, Vector4.ZERO, Vector4.ZERO)[/code].
+
+ [member override_projection] does not currently support stereo cameras.
+
The camera's projection mode. In [constant PROJECTION_PERSPECTIVE] mode, objects' Z distance from the camera's local space scales their perceived size.
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 3c9f0fc7aff..f727cb41f7b 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -163,6 +163,17 @@
Sets camera to use orthogonal projection, also known as orthographic projection. Objects remain the same size on the screen no matter how far away they are.
+
+
+
+
+
+ If set to a non-zero Projection, overrides the view-projection matrix passed to shaders with this custom matrix. This *only* overrides the projection passed to shaders on the GPU. The [method camera_set_frustum], [method camera_set_orthogonal] or [method camera_set_frustum] values are still used for CPU-side operations such as culling, which can be used to provide a separate culling frustum.
+ To clear the overridden projection, set to [code]Projection(Vector4.ZERO, Vector4.ZERO, Vector4.ZERO, Vector4.ZERO)[/code].
+
+ [method camera_set_override_projection] does not currently support stereo cameras.
+
+
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 6cbdf5e9350..96189a6d3a4 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -1460,7 +1460,7 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const
void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_pancake_shadows, float p_shadow_bias) {
Projection correction;
correction.set_depth_correction(p_flip_y, true, false);
- Projection projection = correction * p_render_data->cam_projection;
+ Projection projection = correction * p_render_data->view_projection[0];
//store camera into ubo
GLES3::MaterialStorage::store_camera(projection, scene_state.ubo.projection_matrix);
GLES3::MaterialStorage::store_camera(projection.inverse(), scene_state.ubo.inv_projection_matrix);
diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp
index 8515aacba7c..03d877b8076 100644
--- a/scene/3d/camera_3d.cpp
+++ b/scene/3d/camera_3d.cpp
@@ -240,6 +240,15 @@ void Camera3D::set_frustum(real_t p_size, Vector2 p_offset, real_t p_z_near, rea
update_gizmos();
}
+Projection Camera3D::get_override_projection() const {
+ return override_projection;
+}
+
+void Camera3D::set_override_projection(const Projection &p_matrix) {
+ override_projection = p_matrix;
+ RenderingServer::get_singleton()->camera_set_override_projection(camera, override_projection);
+}
+
void Camera3D::set_projection(ProjectionType p_mode) {
if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL || p_mode == PROJECTION_FRUSTUM) {
mode = p_mode;
@@ -536,6 +545,8 @@ void Camera3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_near", "near"), &Camera3D::set_near);
ClassDB::bind_method(D_METHOD("get_projection"), &Camera3D::get_projection);
ClassDB::bind_method(D_METHOD("set_projection", "mode"), &Camera3D::set_projection);
+ ClassDB::bind_method(D_METHOD("get_override_projection"), &Camera3D::get_override_projection);
+ ClassDB::bind_method(D_METHOD("set_override_projection", "projection_matrix"), &Camera3D::set_override_projection);
ClassDB::bind_method(D_METHOD("set_h_offset", "offset"), &Camera3D::set_h_offset);
ClassDB::bind_method(D_METHOD("get_h_offset"), &Camera3D::get_h_offset);
ClassDB::bind_method(D_METHOD("set_v_offset", "offset"), &Camera3D::set_v_offset);
@@ -571,6 +582,7 @@ void Camera3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "v_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_v_offset", "get_v_offset");
ADD_PROPERTY(PropertyInfo(Variant::INT, "doppler_tracking", PROPERTY_HINT_ENUM, "Disabled,Idle,Physics"), "set_doppler_tracking", "get_doppler_tracking");
ADD_PROPERTY(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal,Frustum"), "set_projection", "get_projection");
+ ADD_PROPERTY(PropertyInfo(Variant::PROJECTION, "override_projection"), "set_override_projection", "get_override_projection");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov", PROPERTY_HINT_RANGE, "1,179,0.1,degrees"), "set_fov", "get_fov");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.001,100,0.001,or_greater,suffix:m"), "set_size", "get_size");
@@ -748,6 +760,7 @@ RID Camera3D::get_pyramid_shape_rid() {
Camera3D::Camera3D() {
camera = RenderingServer::get_singleton()->camera_create();
+ override_projection.set_zero();
set_perspective(75.0, 0.05, 4000.0);
RenderingServer::get_singleton()->camera_set_cull_mask(camera, layers);
//active=false;
diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h
index dbf2ffc1dd0..2d34a26e4a4 100644
--- a/scene/3d/camera_3d.h
+++ b/scene/3d/camera_3d.h
@@ -74,6 +74,7 @@ class Camera3D : public Node3D {
real_t v_offset = 0.0;
real_t h_offset = 0.0;
KeepAspect keep_aspect = KEEP_HEIGHT;
+ Projection override_projection;
RID camera;
RID scenario_id;
@@ -133,6 +134,7 @@ class Camera3D : public Node3D {
real_t get_far() const;
real_t get_near() const;
Vector2 get_frustum_offset() const;
+ Projection get_override_projection() const;
ProjectionType get_projection() const;
@@ -141,6 +143,7 @@ class Camera3D : public Node3D {
void set_far(real_t p_far);
void set_near(real_t p_near);
void set_frustum_offset(Vector2 p_offset);
+ void set_override_projection(const Projection &p_matrix);
virtual Transform3D get_camera_transform() const;
virtual Projection get_camera_projection() const;
diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp
index dc1e64ddcc2..f06895e6c12 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp
@@ -78,7 +78,7 @@ void RenderSceneDataRD::update_ubo(RID p_uniform_buffer, RS::ViewportDebugDraw p
Projection correction;
correction.set_depth_correction(p_flip_y);
correction.add_jitter_offset(taa_jitter);
- Projection projection = correction * cam_projection;
+ Projection projection = correction * view_projection[0];
//store camera into ubo
RendererRD::MaterialStorage::store_camera(projection, ubo.projection_matrix);
@@ -261,7 +261,7 @@ void RenderSceneDataRD::update_ubo(RID p_uniform_buffer, RS::ViewportDebugDraw p
Projection prev_correction;
prev_correction.set_depth_correction(true);
prev_correction.add_jitter_offset(prev_taa_jitter);
- Projection prev_projection = prev_correction * prev_cam_projection;
+ Projection prev_projection = prev_correction * view_projection[0];
//store camera into ubo
RendererRD::MaterialStorage::store_camera(prev_projection, prev_ubo.projection_matrix);
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index b02d3def88f..61230ab1144 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -90,6 +90,15 @@ void RendererSceneCull::camera_set_frustum(RID p_camera, float p_size, Vector2 p
camera->zfar = p_z_far;
}
+void RendererSceneCull::camera_set_override_projection(RID p_camera, const Projection &p_matrix) {
+ Camera *camera = camera_owner.get_or_null(p_camera);
+ ERR_FAIL_COND(!camera);
+ Projection zero;
+ zero.set_zero();
+ camera->has_override_projection = (p_matrix != zero);
+ camera->override_projection = p_matrix;
+}
+
void RendererSceneCull::camera_set_transform(RID p_camera, const Transform3D &p_transform) {
Camera *camera = camera_owner.get_or_null(p_camera);
ERR_FAIL_NULL(camera);
@@ -2607,6 +2616,9 @@ void RendererSceneCull::render_camera(const Ref &p_render_bu
}
camera_data.set_camera(transform, projection, is_orthogonal, vaspect, jitter, camera->visible_layers);
+ if (camera->has_override_projection) {
+ camera_data.set_override_projection(camera->override_projection);
+ }
} else {
// Setup our camera for our XR interface.
// We can support multiple views here each with their own camera
diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h
index 0039d144751..4a1e80bd611 100644
--- a/servers/rendering/renderer_scene_cull.h
+++ b/servers/rendering/renderer_scene_cull.h
@@ -81,11 +81,13 @@ class RendererSceneCull : public RenderingMethod {
Vector2 offset;
uint32_t visible_layers;
bool vaspect;
+ bool has_override_projection;
RID env;
RID attributes;
RID compositor;
Transform3D transform;
+ Projection override_projection;
Camera() {
visible_layers = 0xFFFFFFFF;
@@ -96,6 +98,8 @@ class RendererSceneCull : public RenderingMethod {
size = 1.0;
offset = Vector2();
vaspect = false;
+ has_override_projection = false;
+ override_projection.set_zero();
}
};
@@ -107,6 +111,7 @@ class RendererSceneCull : public RenderingMethod {
virtual void camera_set_perspective(RID p_camera, float p_fovy_degrees, float p_z_near, float p_z_far);
virtual void camera_set_orthogonal(RID p_camera, float p_size, float p_z_near, float p_z_far);
virtual void camera_set_frustum(RID p_camera, float p_size, Vector2 p_offset, float p_z_near, float p_z_far);
+ virtual void camera_set_override_projection(RID p_camera, const Projection &p_matrix);
virtual void camera_set_transform(RID p_camera, const Transform3D &p_transform);
virtual void camera_set_cull_mask(RID p_camera, uint32_t p_layers);
virtual void camera_set_environment(RID p_camera, RID p_env);
diff --git a/servers/rendering/renderer_scene_render.cpp b/servers/rendering/renderer_scene_render.cpp
index 76c779900fb..d1290e2beb2 100644
--- a/servers/rendering/renderer_scene_render.cpp
+++ b/servers/rendering/renderer_scene_render.cpp
@@ -47,6 +47,12 @@ void RendererSceneRender::CameraData::set_camera(const Transform3D p_transform,
taa_jitter = p_taa_jitter;
}
+void RendererSceneRender::CameraData::set_override_projection(const Projection &p_projection) {
+ if (view_count == 1) {
+ view_projection[0] = p_projection;
+ }
+}
+
void RendererSceneRender::CameraData::set_multiview_camera(uint32_t p_view_count, const Transform3D *p_transforms, const Projection *p_projections, bool p_is_orthogonal, bool p_vaspect) {
ERR_FAIL_COND_MSG(p_view_count != 2, "Incorrect view count for stereoscopic view");
diff --git a/servers/rendering/renderer_scene_render.h b/servers/rendering/renderer_scene_render.h
index 719efa4df22..59b9fafcb5d 100644
--- a/servers/rendering/renderer_scene_render.h
+++ b/servers/rendering/renderer_scene_render.h
@@ -309,6 +309,7 @@ class RendererSceneRender {
Vector2 taa_jitter;
void set_camera(const Transform3D p_transform, const Projection p_projection, bool p_is_orthogonal, bool p_vaspect, const Vector2 &p_taa_jitter = Vector2(), uint32_t p_visible_layers = 0xFFFFFFFF);
+ void set_override_projection(const Projection &p_projection);
void set_multiview_camera(uint32_t p_view_count, const Transform3D *p_transforms, const Projection *p_projections, bool p_is_orthogonal, bool p_vaspect);
};
diff --git a/servers/rendering/rendering_method.h b/servers/rendering/rendering_method.h
index aa5e7d83cc8..5f903b6c94f 100644
--- a/servers/rendering/rendering_method.h
+++ b/servers/rendering/rendering_method.h
@@ -50,6 +50,7 @@ class RenderingMethod {
virtual void camera_set_perspective(RID p_camera, float p_fovy_degrees, float p_z_near, float p_z_far) = 0;
virtual void camera_set_orthogonal(RID p_camera, float p_size, float p_z_near, float p_z_far) = 0;
virtual void camera_set_frustum(RID p_camera, float p_size, Vector2 p_offset, float p_z_near, float p_z_far) = 0;
+ virtual void camera_set_override_projection(RID p_camera, const Projection &p_matrix) = 0;
virtual void camera_set_transform(RID p_camera, const Transform3D &p_transform) = 0;
virtual void camera_set_cull_mask(RID p_camera, uint32_t p_layers) = 0;
virtual void camera_set_environment(RID p_camera, RID p_env) = 0;
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index 164ec3cc09f..242728c81b2 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -582,6 +582,7 @@ class RenderingServerDefault : public RenderingServer {
FUNC4(camera_set_perspective, RID, float, float, float)
FUNC4(camera_set_orthogonal, RID, float, float, float)
FUNC5(camera_set_frustum, RID, float, Vector2, float, float)
+ FUNC2(camera_set_override_projection, RID, const Projection &)
FUNC2(camera_set_transform, RID, const Transform3D &)
FUNC2(camera_set_cull_mask, RID, uint32_t)
FUNC2(camera_set_environment, RID, RID)
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 7637d4e7da2..9401075e7ae 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -2756,6 +2756,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("camera_set_perspective", "camera", "fovy_degrees", "z_near", "z_far"), &RenderingServer::camera_set_perspective);
ClassDB::bind_method(D_METHOD("camera_set_orthogonal", "camera", "size", "z_near", "z_far"), &RenderingServer::camera_set_orthogonal);
ClassDB::bind_method(D_METHOD("camera_set_frustum", "camera", "size", "offset", "z_near", "z_far"), &RenderingServer::camera_set_frustum);
+ ClassDB::bind_method(D_METHOD("camera_set_override_projection", "camera", "matrix"), &RenderingServer::camera_set_override_projection);
ClassDB::bind_method(D_METHOD("camera_set_transform", "camera", "transform"), &RenderingServer::camera_set_transform);
ClassDB::bind_method(D_METHOD("camera_set_cull_mask", "camera", "layers"), &RenderingServer::camera_set_cull_mask);
ClassDB::bind_method(D_METHOD("camera_set_environment", "camera", "env"), &RenderingServer::camera_set_environment);
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index e15dba43536..424b8c37c4d 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -842,6 +842,7 @@ class RenderingServer : public Object {
virtual void camera_set_perspective(RID p_camera, float p_fovy_degrees, float p_z_near, float p_z_far) = 0;
virtual void camera_set_orthogonal(RID p_camera, float p_size, float p_z_near, float p_z_far) = 0;
virtual void camera_set_frustum(RID p_camera, float p_size, Vector2 p_offset, float p_z_near, float p_z_far) = 0;
+ virtual void camera_set_override_projection(RID p_camera, const Projection &p_matrix) = 0;
virtual void camera_set_transform(RID p_camera, const Transform3D &p_transform) = 0;
virtual void camera_set_cull_mask(RID p_camera, uint32_t p_layers) = 0;
virtual void camera_set_environment(RID p_camera, RID p_env) = 0;