diff --git a/core/math/projection.cpp b/core/math/projection.cpp index e0b8dcf815d0..d90a8ae7b584 100644 --- a/core/math/projection.cpp +++ b/core/math/projection.cpp @@ -934,6 +934,28 @@ void Projection::add_jitter_offset(const Vector2 &p_offset) { columns[3][1] += p_offset.y; } +void Projection::project_origin_and_ray(const Vector2 &p_screen_pos, const Vector2 &p_screen_size, Vector3 &r_origin, Vector3 &r_ray) const { + // Use full matrix. + Projection inv_cm = inverse(); + + // Clip-Space point in near plane. + Vector4 clip_near( + (2.0 * p_screen_pos.x) / p_screen_size.width - 1.0, + 1.0 - (2.0 * p_screen_pos.y) / p_screen_size.height, + -1.0, // Near plane in OpenGL clip space. + 1.0); + + Vector4 view_near = inv_cm.xform(clip_near); + r_origin = Vector3(view_near.x, view_near.y, view_near.z) / view_near.w; + + Vector4 clip_adv = clip_near; + clip_adv.z = 1.0; + + Vector4 view_adv = inv_cm.xform(clip_adv); + Vector3 point_adv = Vector3(view_adv.x, view_adv.y, view_adv.z) / view_adv.w; + r_ray = (point_adv - r_origin).normalized(); +} + Projection::operator Transform3D() const { Transform3D tr; const real_t *m = &columns[0][0]; diff --git a/core/math/projection.h b/core/math/projection.h index 87985c2c6f1e..f5f15f124d8c 100644 --- a/core/math/projection.h +++ b/core/math/projection.h @@ -116,6 +116,9 @@ struct [[nodiscard]] Projection { Vector2 get_viewport_half_extents() const; Vector2 get_far_plane_half_extents() const; + // Used for generic ray picking in any matrix. + void project_origin_and_ray(const Vector2 &p_screen_pos, const Vector2 &p_screen_size, Vector3 &r_origin, Vector3 &r_ray) const; + void invert(); Projection inverse() const; diff --git a/doc/classes/Camera3D.xml b/doc/classes/Camera3D.xml index d7ef4320ce0c..f1e427606a16 100644 --- a/doc/classes/Camera3D.xml +++ b/doc/classes/Camera3D.xml @@ -173,6 +173,10 @@ If [code]true[/code], the ancestor [Viewport] is currently using this camera. If multiple cameras are in the scene, one will always be made current. For example, if two [Camera3D] nodes are present in the scene and only one is current, setting one camera's [member current] to [code]false[/code] will cause the other camera to be made current. + + The camera's [Projection]. This can be changed from the default to apply custom projection matrix. + [b]Note:[/b] Only effective if [member projection] is [constant PROJECTION_CUSTOM]. + If not [constant DOPPLER_TRACKING_DISABLED], this camera will simulate the [url=https://en.wikipedia.org/wiki/Doppler_effect]Doppler effect[/url] for objects changed in particular [code]_process[/code] methods. [b]Note:[/b] The Doppler effect will only be heard on [AudioStreamPlayer3D]s if [member AudioStreamPlayer3D.doppler_tracking] is not set to [constant AudioStreamPlayer3D.DOPPLER_TRACKING_DISABLED]. @@ -224,6 +228,9 @@ Frustum projection. This mode allows adjusting [member frustum_offset] to create "tilted frustum" effects. + + Custom projection. This mode allows using [member custom_projection]. + Preserves the horizontal aspect ratio; also known as Vert- scaling. This is usually the best option for projects running in portrait mode, as taller aspect ratios will benefit from a wider vertical FOV. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 60fa9fa159bd..0a9e2c3a8879 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -134,6 +134,14 @@ Sets the cull mask associated with this camera. The cull mask describes which 3D layers are rendered by this camera. Equivalent to [member Camera3D.cull_mask]. + + + + + + Sets camera to use custom projection. + + diff --git a/editor/scene/3d/gizmos/camera_3d_gizmo_plugin.cpp b/editor/scene/3d/gizmos/camera_3d_gizmo_plugin.cpp index 61bbf5f602a8..6279f6ca1015 100644 --- a/editor/scene/3d/gizmos/camera_3d_gizmo_plugin.cpp +++ b/editor/scene/3d/gizmos/camera_3d_gizmo_plugin.cpp @@ -250,6 +250,36 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { Vector3 tup(0, up.y + hsize / 2, side.z); ADD_TRIANGLE(tup + offset, side + up + offset, nside + up + offset); } break; + + case Camera3D::PROJECTION_CUSTOM: { + Vector3 points[8] = { + Vector3(0.5f, 0.5f, 0.0f), + Vector3(0.5f, -0.5f, 0.0f), + Vector3(-0.5f, 0.5f, 0.0f), + Vector3(-0.5f, -0.5f, 0.0f), + Vector3(0.5f, 0.5f, -1.0f), + Vector3(0.5f, -0.5f, -1.0f), + Vector3(-0.5f, 0.5f, -1.0f), + Vector3(-0.5f, -0.5f, -1.0f) + }; + + Projection proj = camera->get_camera_projection(); + + for (int i = 0; i < 8; i++) { + points[i] = proj.xform(points[i]); + } + + ADD_QUAD(points[0], points[1], points[5], points[4]); + ADD_QUAD(points[2], points[3], points[7], points[6]); + ADD_QUAD(points[0], points[2], points[6], points[4]); + ADD_QUAD(points[1], points[3], points[7], points[5]); + + Vector3 top_left_to_top_right = points[0] - points[2]; + Vector3 up = Vector3(0, 0, 0.2).cross(top_left_to_top_right); + ADD_TRIANGLE(points[2] + top_left_to_top_right * 0.4, + points[2] + top_left_to_top_right * 0.6, + points[2] + top_left_to_top_right * 0.5 + up); + } break; } #undef ADD_TRIANGLE diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 148466aa17d1..abae6469925b 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -104,7 +104,6 @@ void Camera3D::_update_camera_mode() { switch (mode) { case PROJECTION_PERSPECTIVE: { set_perspective(fov, _near, _far); - } break; case PROJECTION_ORTHOGONAL: { set_orthogonal(size, _near, _far); @@ -112,6 +111,9 @@ void Camera3D::_update_camera_mode() { case PROJECTION_FRUSTUM: { set_frustum(size, frustum_offset, _near, _far); } break; + case PROJECTION_CUSTOM: { + set_custom(c_proj); + } break; } fti_notify_node_changed(false); } @@ -131,6 +133,18 @@ void Camera3D::_validate_property(PropertyInfo &p_property) const { p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } + } else if (p_property.name == "custom_projection") { + if (mode != PROJECTION_CUSTOM) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + } else if (p_property.name == "near") { + if (mode == PROJECTION_CUSTOM) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + } else if (p_property.name == "far") { + if (mode == PROJECTION_CUSTOM) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } } if (attributes.is_valid()) { @@ -279,6 +293,9 @@ Projection Camera3D::_get_camera_projection(real_t p_near) const { case PROJECTION_FRUSTUM: { cm.set_frustum(size, viewport_size.aspect(), frustum_offset, p_near, _far); } break; + case PROJECTION_CUSTOM: { + cm = c_proj; + } break; } return cm; @@ -337,8 +354,44 @@ void Camera3D::set_frustum(real_t p_size, Vector2 p_offset, real_t p_z_near, rea update_gizmos(); } +void Camera3D::set_custom(Projection p_proj) { + if (!force_change && c_proj == p_proj) { + return; + } + + c_proj = p_proj; + mode = PROJECTION_CUSTOM; + force_change = false; + + Vector planes = c_proj.get_projection_planes(Transform3D()); + const Projection::Planes intersections[8][3] = { + { Projection::PLANE_FAR, Projection::PLANE_LEFT, Projection::PLANE_TOP }, + { Projection::PLANE_FAR, Projection::PLANE_LEFT, Projection::PLANE_BOTTOM }, + { Projection::PLANE_FAR, Projection::PLANE_RIGHT, Projection::PLANE_TOP }, + { Projection::PLANE_FAR, Projection::PLANE_RIGHT, Projection::PLANE_BOTTOM }, + { Projection::PLANE_NEAR, Projection::PLANE_LEFT, Projection::PLANE_TOP }, + { Projection::PLANE_NEAR, Projection::PLANE_LEFT, Projection::PLANE_BOTTOM }, + { Projection::PLANE_NEAR, Projection::PLANE_RIGHT, Projection::PLANE_TOP }, + { Projection::PLANE_NEAR, Projection::PLANE_RIGHT, Projection::PLANE_BOTTOM }, + }; + + for (int i = 0; i < 8; i++) { + Plane a = planes[intersections[i][0]]; + Plane b = planes[intersections[i][1]]; + Plane c = planes[intersections[i][2]]; + ERR_FAIL_COND_MSG(!a.intersect_3(b, c), "Camera3D custom projection has non-intersecting planes; skipping update."); + } + + RenderingServer::get_singleton()->camera_set_custom(camera, c_proj); + update_gizmos(); +} + void Camera3D::set_projection(ProjectionType p_mode) { - if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL || p_mode == PROJECTION_FRUSTUM) { + if (p_mode == PROJECTION_CUSTOM && mode != PROJECTION_CUSTOM && is_inside_tree()) { + c_proj = get_camera_projection(); + } + + if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL || p_mode == PROJECTION_FRUSTUM || p_mode == PROJECTION_CUSTOM) { mode = p_mode; _update_camera_mode(); notify_property_list_changed(); @@ -404,6 +457,9 @@ Vector3 Camera3D::project_local_ray_normal(const Point2 &p_pos) const { if (mode == PROJECTION_ORTHOGONAL) { ray = Vector3(0, 0, -1); + } else if (mode == PROJECTION_CUSTOM) { + Vector3 from; + _get_camera_projection(_near).project_origin_and_ray(p_pos, viewport_size, from, ray); } else { Projection cm = _get_camera_projection(_near); Vector2 screen_he = cm.get_viewport_half_extents(); @@ -420,6 +476,8 @@ Vector3 Camera3D::project_ray_origin(const Point2 &p_pos) const { Vector2 cpos = get_viewport()->get_camera_coords(p_pos); ERR_FAIL_COND_V(viewport_size.y == 0, Vector3()); + Vector3 origin; + if (mode == PROJECTION_ORTHOGONAL) { Vector2 pos = cpos / viewport_size; real_t vsize, hsize; @@ -436,10 +494,18 @@ Vector3 Camera3D::project_ray_origin(const Point2 &p_pos) const { ray.y = (1.0 - pos.y) * (vsize)-vsize / 2; ray.z = -_near; ray = get_camera_transform().xform(ray); - return ray; + origin = ray; + } else if (mode == PROJECTION_CUSTOM) { + Vector3 from; + Vector3 ray; + _get_camera_projection(_near).project_origin_and_ray(p_pos, viewport_size, from, ray); + + origin = get_camera_transform().xform(from); } else { - return get_camera_transform().origin; - }; + origin = get_camera_transform().origin; + } + + return origin; } bool Camera3D::is_position_behind(const Vector3 &p_pos) const { @@ -623,6 +689,7 @@ void Camera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_perspective", "fov", "z_near", "z_far"), &Camera3D::set_perspective); ClassDB::bind_method(D_METHOD("set_orthogonal", "size", "z_near", "z_far"), &Camera3D::set_orthogonal); ClassDB::bind_method(D_METHOD("set_frustum", "size", "offset", "z_near", "z_far"), &Camera3D::set_frustum); + ClassDB::bind_method(D_METHOD("set_custom_projection", "p_proj"), &Camera3D::set_custom_projection); ClassDB::bind_method(D_METHOD("make_current"), &Camera3D::make_current); ClassDB::bind_method(D_METHOD("clear_current", "enable_next"), &Camera3D::clear_current, DEFVAL(true)); ClassDB::bind_method(D_METHOD("set_current", "enabled"), &Camera3D::set_current); @@ -633,6 +700,7 @@ void Camera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_frustum_offset"), &Camera3D::get_frustum_offset); ClassDB::bind_method(D_METHOD("get_size"), &Camera3D::get_size); ClassDB::bind_method(D_METHOD("get_far"), &Camera3D::get_far); + ClassDB::bind_method(D_METHOD("get_custom_projection"), &Camera3D::get_custom_projection); ClassDB::bind_method(D_METHOD("get_near"), &Camera3D::get_near); ClassDB::bind_method(D_METHOD("set_fov", "fov"), &Camera3D::set_fov); ClassDB::bind_method(D_METHOD("set_frustum_offset", "offset"), &Camera3D::set_frustum_offset); @@ -677,17 +745,19 @@ void Camera3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "h_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_h_offset", "get_h_offset"); 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::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal,Frustum,Custom"), "set_projection", "get_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"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frustum_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_frustum_offset", "get_frustum_offset"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "near", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater,exp,suffix:m"), "set_near", "get_near"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "far", PROPERTY_HINT_RANGE, "0.01,4000,0.01,or_greater,exp,suffix:m"), "set_far", "get_far"); + ADD_PROPERTY(PropertyInfo(Variant::PROJECTION, "custom_projection"), "set_custom_projection", "get_custom_projection"); BIND_ENUM_CONSTANT(PROJECTION_PERSPECTIVE); BIND_ENUM_CONSTANT(PROJECTION_ORTHOGONAL); BIND_ENUM_CONSTANT(PROJECTION_FRUSTUM); + BIND_ENUM_CONSTANT(PROJECTION_CUSTOM); BIND_ENUM_CONSTANT(KEEP_WIDTH); BIND_ENUM_CONSTANT(KEEP_HEIGHT); @@ -717,6 +787,10 @@ real_t Camera3D::get_far() const { return _far; } +Projection Camera3D::get_custom_projection() const { + return c_proj; +} + Camera3D::ProjectionType Camera3D::get_projection() const { return mode; } @@ -748,6 +822,11 @@ void Camera3D::set_far(real_t p_far) { _update_camera_mode(); } +void Camera3D::set_custom_projection(Projection p_proj) { + c_proj = p_proj; + _update_camera_mode(); +} + void Camera3D::set_cull_mask(uint32_t p_layers) { layers = p_layers; RenderingServer::get_singleton()->camera_set_cull_mask(camera, layers); diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index 7fd88f412ed1..25b8e54476a5 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -44,7 +44,8 @@ class Camera3D : public Node3D { enum ProjectionType { PROJECTION_PERSPECTIVE, PROJECTION_ORTHOGONAL, - PROJECTION_FRUSTUM + PROJECTION_FRUSTUM, + PROJECTION_CUSTOM }; enum KeepAspect { @@ -74,6 +75,7 @@ class Camera3D : public Node3D { real_t v_offset = 0.0; real_t h_offset = 0.0; KeepAspect keep_aspect = KEEP_HEIGHT; + Projection c_proj; RID camera; RID scenario_id; @@ -139,7 +141,9 @@ class Camera3D : public Node3D { void set_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far); void set_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far); void set_frustum(real_t p_size, Vector2 p_offset, real_t p_z_near, real_t p_z_far); + void set_custom(Projection p_proj); void set_projection(Camera3D::ProjectionType p_mode); + void set_custom_projection(Projection p_proj); void make_current(); void clear_current(bool p_enable_next = true); @@ -151,6 +155,7 @@ class Camera3D : public Node3D { real_t get_fov() const; real_t get_size() const; real_t get_far() const; + Projection get_custom_projection() const; real_t get_near() const; Vector2 get_frustum_offset() const; diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index a7f2ff4025cd..24ebd4bc31b5 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -106,6 +106,13 @@ void RendererSceneCull::camera_set_frustum(RID p_camera, float p_size, Vector2 p camera->zfar = p_z_far; } +void RendererSceneCull::camera_set_custom(RID p_camera, Projection p_proj) { + Camera *camera = camera_owner.get_or_null(p_camera); + ERR_FAIL_NULL(camera); + camera->type = Camera::CUSTOM; + camera->c_proj = p_proj; +} + 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); @@ -2653,6 +2660,10 @@ void RendererSceneCull::render_camera(const Ref &p_render_bu camera->vaspect); is_frustum = true; } break; + case Camera::CUSTOM: { + projection = camera->c_proj; + is_orthogonal = projection.is_orthogonal(); + } break; } camera_data.set_camera(transform, projection, is_orthogonal, is_frustum, vaspect, jitter, taa_frame_count, camera->visible_layers); diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h index b195fa520a6d..dc2e47d345b6 100644 --- a/servers/rendering/renderer_scene_cull.h +++ b/servers/rendering/renderer_scene_cull.h @@ -74,7 +74,8 @@ class RendererSceneCull : public RenderingMethod { enum Type { PERSPECTIVE, ORTHOGONAL, - FRUSTUM + FRUSTUM, + CUSTOM, }; Type type; float fov; @@ -87,6 +88,7 @@ class RendererSceneCull : public RenderingMethod { RID attributes; RID compositor; + Projection c_proj; Transform3D transform; Camera() { @@ -109,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_custom(RID p_camera, Projection p_proj); 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/rendering_method.h b/servers/rendering/rendering_method.h index 5c933d13d082..69f977f2e822 100644 --- a/servers/rendering/rendering_method.h +++ b/servers/rendering/rendering_method.h @@ -49,6 +49,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_custom(RID p_camera, Projection p_proj) = 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.cpp b/servers/rendering/rendering_server.cpp index 4ca52a59ce59..e2fd97a238d2 100644 --- a/servers/rendering/rendering_server.cpp +++ b/servers/rendering/rendering_server.cpp @@ -2813,6 +2813,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_custom", "camera", "p_proj"), &RenderingServer::camera_set_custom); 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/rendering_server.h b/servers/rendering/rendering_server.h index 32f7bf0b6427..52bf6e439fd2 100644 --- a/servers/rendering/rendering_server.h +++ b/servers/rendering/rendering_server.h @@ -915,6 +915,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_custom(RID p_camera, Projection p_proj) = 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 49b8f2752c42..76e453d7c572 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -672,6 +672,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_custom, RID, Projection) FUNC2(camera_set_transform, RID, const Transform3D &) FUNC2(camera_set_cull_mask, RID, uint32_t) FUNC2(camera_set_environment, RID, RID)