diff --git a/core/math/frustum.cpp b/core/math/frustum.cpp new file mode 100644 index 000000000000..8e090412d7cc --- /dev/null +++ b/core/math/frustum.cpp @@ -0,0 +1,249 @@ +/**************************************************************************/ +/* frustum.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "frustum.h" + +#include "core/math/projection.h" +#include "core/math/transform_3d.h" +#include "core/math/vector2.h" + +void Frustum::set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov) { + if (p_flip_fov) { + p_fovy_degrees = get_fovy(p_fovy_degrees, 1.0 / p_aspect); + } + + // Inspired by https://iquilezles.org/articles/frustum/ + real_t an = Math::deg_to_rad(p_fovy_degrees / 2); + real_t si = sin(an); + real_t co = cos(an); + real_t mag = Math::sqrt(co * co + si * si * p_aspect * p_aspect); + + planes[Projection::PLANE_NEAR] = Plane(Vector3(0, 0, 1), -p_z_near); + planes[Projection::PLANE_FAR] = Plane(Vector3(0, 0, -1), p_z_far); + planes[Projection::PLANE_LEFT] = Plane(Vector3(-co, 0, si * p_aspect) / mag, 0); + planes[Projection::PLANE_TOP] = Plane(Vector3(0, co, si), 0); + planes[Projection::PLANE_RIGHT] = Plane(Vector3(co, 0, si * p_aspect) / mag, 0); + planes[Projection::PLANE_BOTTOM] = Plane(Vector3(0, -co, si), 0); +} + +void Frustum::set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist) { + if (p_flip_fov) { + p_fovy_degrees = get_fovy(p_fovy_degrees, 1.0 / p_aspect); + } + + real_t left, right, modeltranslation, ymax, xmax, frustumshift; + + ymax = p_z_near * tan(Math::deg_to_rad(p_fovy_degrees / 2.0)); + xmax = ymax * p_aspect; + frustumshift = (p_intraocular_dist / 2.0) * p_z_near / p_convergence_dist; + + switch (p_eye) { + case 1: { // left eye + left = -xmax + frustumshift; + right = xmax + frustumshift; + modeltranslation = p_intraocular_dist / 2.0; + } break; + case 2: { // right eye + left = -xmax - frustumshift; + right = xmax - frustumshift; + modeltranslation = -p_intraocular_dist / 2.0; + } break; + default: { // mono, should give the same result as set_perspective(p_fovy_degrees,p_aspect,p_z_near,p_z_far,p_flip_fov) + left = -xmax; + right = xmax; + modeltranslation = 0.0; + } break; + } + + set_frustum(left, right, -ymax, ymax, p_z_near, p_z_far); + + // translate matrix by (modeltranslation, 0.0, 0.0) + planes[Projection::PLANE_LEFT].d = -modeltranslation * cos(atan2(left, p_z_near)); + planes[Projection::PLANE_RIGHT].d = modeltranslation * cos(atan2(right, p_z_near)); +} + +void Frustum::set_for_hmd(int p_eye, real_t p_aspect, real_t p_intraocular_dist, real_t p_display_width, real_t p_display_to_lens, real_t p_oversample, real_t p_z_near, real_t p_z_far) { + // we first calculate our base frustum on our values without taking our lens magnification into account. + real_t f1 = (p_intraocular_dist * 0.5) / p_display_to_lens; + real_t f2 = ((p_display_width - p_intraocular_dist) * 0.5) / p_display_to_lens; + real_t f3 = (p_display_width / 4.0) / p_display_to_lens; + + // now we apply our oversample factor to increase our FOV. how much we oversample is always a balance we strike between performance and how much + // we're willing to sacrifice in FOV. + real_t add = ((f1 + f2) * (p_oversample - 1.0)) / 2.0; + f1 += add; + f2 += add; + f3 *= p_oversample; + + // always apply KEEP_WIDTH aspect ratio + f3 /= p_aspect; + + switch (p_eye) { + case 1: { // left eye + set_frustum(-f2 * p_z_near, f1 * p_z_near, -f3 * p_z_near, f3 * p_z_near, p_z_near, p_z_far); + } break; + case 2: { // right eye + set_frustum(-f1 * p_z_near, f2 * p_z_near, -f3 * p_z_near, f3 * p_z_near, p_z_near, p_z_far); + } break; + default: { // mono, does not apply here! + } break; + } +} + +void Frustum::set_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar) { + planes[Projection::PLANE_NEAR] = Plane(Vector3(0, 0, 1), -p_znear); + planes[Projection::PLANE_FAR] = Plane(Vector3(0, 0, -1), p_zfar); + planes[Projection::PLANE_LEFT] = Plane(Vector3(-1, 0, 0), -p_left); + planes[Projection::PLANE_TOP] = Plane(Vector3(0, 1, 0), p_top); + planes[Projection::PLANE_RIGHT] = Plane(Vector3(1, 0, 0), p_right); + planes[Projection::PLANE_BOTTOM] = Plane(Vector3(0, -1, 0), -p_bottom); +} + +void Frustum::set_orthogonal(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov) { + if (!p_flip_fov) { + p_size *= p_aspect; + } + + set_orthogonal(-p_size / 2, +p_size / 2, -p_size / p_aspect / 2, +p_size / p_aspect / 2, p_znear, p_zfar); +} + +void Frustum::set_frustum(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov) { + if (!p_flip_fov) { + p_size *= p_aspect; + } + + set_frustum(-p_size / 2 + p_offset.x, +p_size / 2 + p_offset.x, -p_size / p_aspect / 2 + p_offset.y, +p_size / p_aspect / 2 + p_offset.y, p_near, p_far); +} + +void Frustum::set_frustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far) { + ERR_FAIL_COND(p_right <= p_left); + ERR_FAIL_COND(p_top <= p_bottom); + ERR_FAIL_COND(p_far <= p_near); + + real_t left = atan2(p_left, p_near); + real_t top = atan2(p_top, p_near); + real_t right = atan2(p_right, p_near); + real_t bottom = atan2(p_bottom, p_near); + + planes[Projection::PLANE_NEAR] = Plane(Vector3(0, 0, 1), -p_near); + planes[Projection::PLANE_FAR] = Plane(Vector3(0, 0, -1), p_far); + planes[Projection::PLANE_LEFT] = Plane(Vector3(-cos(left), 0, -sin(left)), 0); + planes[Projection::PLANE_TOP] = Plane(Vector3(0, cos(top), sin(top)), 0); + planes[Projection::PLANE_RIGHT] = Plane(Vector3(cos(right), 0, sin(right)), 0); + planes[Projection::PLANE_BOTTOM] = Plane(Vector3(0, -cos(bottom), -sin(bottom)), 0); +} + +Vector Frustum::get_projection_planes(const Transform3D &p_transform) const { + Vector result; + Basis tr_inverse = p_transform.basis.inverse().transposed(); + for (int i = 0; i < 6; i++) { + result.push_back(p_transform.xform_fast(planes[i], tr_inverse)); + } + return result; +} + +bool Frustum::is_orthogonal() const { + return (planes[Projection::PLANE_LEFT].normal / Math::abs(planes[Projection::PLANE_LEFT].normal.x)).is_equal_approx(Vector3(-1, 0, 0)) && + (planes[Projection::PLANE_RIGHT].normal / Math::abs(planes[Projection::PLANE_RIGHT].normal.x)).is_equal_approx(Vector3(1, 0, 0)) && + (planes[Projection::PLANE_TOP].normal / Math::abs(planes[Projection::PLANE_TOP].normal.y)).is_equal_approx(Vector3(0, 1, 0)) && + (planes[Projection::PLANE_BOTTOM].normal / Math::abs(planes[Projection::PLANE_BOTTOM].normal.y)).is_equal_approx(Vector3(0, -1, 0)); +} + +Vector2 Frustum::get_viewport_half_extents() const { + Vector3 res; + planes[Projection::PLANE_NEAR].normalized().intersect_3(planes[Projection::PLANE_RIGHT].normalized(), planes[Projection::PLANE_TOP].normalized(), &res); + return Vector2(res.x, res.y); +} + +Vector2 Frustum::get_far_plane_half_extents() const { + Vector3 res; + planes[Projection::PLANE_FAR].normalized().intersect_3(planes[Projection::PLANE_RIGHT].normalized(), planes[Projection::PLANE_TOP].normalized(), &res); + return Vector2(res.x, res.y); +} + +bool Frustum::get_endpoints(const Transform3D &p_transform, Vector3 *p_8points) const { + 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++) { + Vector3 point; + Plane a = planes[intersections[i][0]]; + Plane b = planes[intersections[i][1]]; + Plane c = planes[intersections[i][2]]; + bool res = a.intersect_3(b, c, &point); + ERR_FAIL_COND_V(!res, false); + p_8points[i] = p_transform.xform(point); + } + + return true; +} + +real_t Frustum::get_lod_multiplier() const { + if (is_orthogonal()) { + return get_viewport_half_extents().x; + } else { + const real_t zn = get_z_near(); + const real_t width = get_viewport_half_extents().x * 2.0f; + return 1.0f / (zn / width); + } + + // Usage is lod_size / (lod_distance * multiplier) < threshold +} + +real_t Frustum::get_fov() const { + return Math::rad_to_deg(Math::acos(Math::abs(planes[Projection::PLANE_LEFT].normalized().normal.x))) + Math::rad_to_deg(Math::acos(Math::abs(planes[Projection::PLANE_RIGHT].normalized().normal.x))); +} + +real_t Frustum::get_z_near() const { + return -planes[Projection::PLANE_NEAR].normalized().d; +} + +real_t Frustum::get_z_far() const { + return planes[Projection::PLANE_FAR].normalized().d; +} + +real_t Frustum::get_aspect() const { + Vector2 vp_he = get_viewport_half_extents(); + return vp_he.x / vp_he.y; +} + +Frustum::Frustum(const Vector &p_planes) { + for (int i = 0; i < p_planes.size(); i++) { + planes[i] = p_planes[i]; + } +} \ No newline at end of file diff --git a/core/math/frustum.h b/core/math/frustum.h new file mode 100644 index 000000000000..3154703884b0 --- /dev/null +++ b/core/math/frustum.h @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* frustum.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef FRUSTUM_H +#define FRUSTUM_H + +#include "core/math/plane.h" + +struct Transform3D; + +struct Frustum { + /* + Planes are stored in the order defined in struct Projection : + + enum Projection::Planes { + PLANE_NEAR, + PLANE_FAR, + PLANE_LEFT, + PLANE_TOP, + PLANE_RIGHT, + PLANE_BOTTOM + }; + */ + + Plane planes[6]; + + _FORCE_INLINE_ Plane &operator[](int p_index) { return planes[p_index]; } + _FORCE_INLINE_ const Plane &operator[](int p_index) const { return planes[p_index]; } + + void set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov = false); + void set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist); + void set_for_hmd(int p_eye, real_t p_aspect, real_t p_intraocular_dist, real_t p_display_width, real_t p_display_to_lens, real_t p_oversample, real_t p_z_near, real_t p_z_far); + void set_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar); + void set_orthogonal(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov = false); + void set_frustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far); + void set_frustum(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov = false); + + Vector get_projection_planes(const Transform3D &p_transform) const; + + bool is_orthogonal() const; + bool get_endpoints(const Transform3D &p_transform, Vector3 *p_8points) const; + Vector2 get_viewport_half_extents() const; + Vector2 get_far_plane_half_extents() const; + real_t get_lod_multiplier() const; + real_t get_fov() const; + real_t get_z_near() const; + real_t get_z_far() const; + real_t get_aspect() const; + + static real_t get_fovy(real_t p_fovx, real_t p_aspect) { + return Math::rad_to_deg(Math::atan(p_aspect * Math::tan(Math::deg_to_rad(p_fovx) * 0.5)) * 2.0); + } + + Frustum() = default; + Frustum(const Vector &p_planes); +}; + +#endif // #define FRUSTUM_H \ No newline at end of file diff --git a/core/math/projection.cpp b/core/math/projection.cpp index 20638826a6e5..23035ea77ede 100644 --- a/core/math/projection.cpp +++ b/core/math/projection.cpp @@ -93,13 +93,23 @@ Vector4 Projection::xform_inv(const Vector4 &p_vec4) const { columns[3][0] * p_vec4.x + columns[3][1] * p_vec4.y + columns[3][2] * p_vec4.z + columns[3][3] * p_vec4.w); } -void Projection::adjust_perspective_znear(real_t p_new_znear) { - real_t zfar = get_z_far(); - real_t znear = p_new_znear; +void Projection::adjust_perspective_znear_zfar(real_t p_new_znear, real_t p_new_zfar) { + real_t deltaZ = p_new_zfar - p_new_znear; + columns[2][2] = -(p_new_zfar + p_new_znear) / deltaZ; + columns[3][2] = -2 * p_new_znear * p_new_zfar / deltaZ; +} + +void Projection::adjust_perspective_fov(real_t p_new_fovy_degrees) { + real_t sine, cotangent, aspect; + real_t radians = Math::deg_to_rad(p_new_fovy_degrees / 2.0); + + sine = Math::sin(radians); + + cotangent = Math::cos(radians) / sine; + aspect = columns[1][1] / columns[0][0]; - real_t deltaZ = zfar - znear; - columns[2][2] = -(zfar + znear) / deltaZ; - columns[3][2] = -2 * znear * zfar / deltaZ; + columns[0][0] = cotangent / aspect; + columns[1][1] = cotangent; } Projection Projection::create_depth_correction(bool p_flip_y) { @@ -164,7 +174,14 @@ Projection Projection::create_fit_aabb(const AABB &p_aabb) { Projection Projection::perspective_znear_adjusted(real_t p_new_znear) const { Projection proj = *this; - proj.adjust_perspective_znear(p_new_znear); + real_t zfar = proj.get_z_far(); + proj.adjust_perspective_znear_zfar(p_new_znear, zfar); + return proj; +} + +Projection Projection::perspective_fov_adjusted(real_t p_new_fovy_degrees) const { + Projection proj = *this; + proj.adjust_perspective_fov(p_new_fovy_degrees); return proj; } diff --git a/core/math/projection.h b/core/math/projection.h index 5af43561c0c6..33c947079f80 100644 --- a/core/math/projection.h +++ b/core/math/projection.h @@ -79,7 +79,8 @@ struct [[nodiscard]] Projection { void set_orthogonal(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov = false); void set_frustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far); void set_frustum(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov = false); - void adjust_perspective_znear(real_t p_new_znear); + void adjust_perspective_znear_zfar(real_t p_new_znear, real_t p_new_zfar); + void adjust_perspective_fov(real_t p_new_fovy_degrees); static Projection create_depth_correction(bool p_flip_y); static Projection create_light_atlas_rect(const Rect2 &p_rect); @@ -92,6 +93,7 @@ struct [[nodiscard]] Projection { static Projection create_frustum_aspect(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov = false); static Projection create_fit_aabb(const AABB &p_aabb); Projection perspective_znear_adjusted(real_t p_new_znear) const; + Projection perspective_fov_adjusted(real_t p_new_fovy_degrees) const; Plane get_projection_plane(Planes p_plane) const; Projection flipped_y() const; Projection jitter_offseted(const Vector2 &p_offset) const; diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 17bdcbcad34b..f18fe6c788c2 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -610,7 +610,7 @@ void RasterizerSceneGLES3::_update_dirty_skys() { dirty_sky_list = nullptr; } -void RasterizerSceneGLES3::_setup_sky(const RenderDataGLES3 *p_render_data, const PagedArray &p_lights, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size) { +void RasterizerSceneGLES3::_setup_sky(const RenderDataGLES3 *p_render_data, const PagedArray &p_lights, const Transform3D &p_transform, const Size2i p_screen_size) { GLES3::LightStorage *light_storage = GLES3::LightStorage::get_singleton(); GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); ERR_FAIL_COND(p_render_data->environment.is_null()); @@ -821,16 +821,11 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, ERR_FAIL_NULL(shader_data); // Camera - Projection camera; + Projection camera = p_projection; - if (environment_get_sky_custom_fov(p_env)) { - float near_plane = p_projection.get_z_near(); - float far_plane = p_projection.get_z_far(); - float aspect = p_projection.get_aspect(); - - camera.set_perspective(environment_get_sky_custom_fov(p_env), aspect, near_plane, far_plane); - } else { - camera = p_projection; + float custom_fov = environment_get_sky_custom_fov(p_env); + if (custom_fov > 0) { + camera.adjust_perspective_fov(custom_fov); } Projection correction; @@ -871,7 +866,7 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, glDrawArrays(GL_TRIANGLES, 0, 3); } -void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_sky_energy_multiplier) { +void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Transform3D &p_transform, float p_sky_energy_multiplier) { GLES3::CubemapFilter *cubemap_filter = GLES3::CubemapFilter::get_singleton(); GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); ERR_FAIL_COND(p_env.is_null()); @@ -1238,11 +1233,11 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const } Plane near_plane; - if (p_render_data->cam_orthogonal) { + if (p_render_data->cam_is_orthogonal) { near_plane = Plane(-p_render_data->cam_transform.basis.get_column(Vector3::AXIS_Z), p_render_data->cam_transform.origin); - near_plane.d += p_render_data->cam_projection.get_z_near(); + near_plane.d += p_render_data->z_near; } - float z_max = p_render_data->cam_projection.get_z_far() - p_render_data->cam_projection.get_z_near(); + float z_max = p_render_data->z_far - p_render_data->z_near; RenderList *rl = &render_list[p_render_list]; @@ -1262,7 +1257,7 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const GeometryInstanceGLES3 *inst = static_cast((*p_render_data->instances)[i]); Vector3 center = inst->transform.origin; - if (p_render_data->cam_orthogonal) { + if (p_render_data->cam_is_orthogonal) { if (inst->use_aabb_center) { center = inst->transformed_aabb.get_support(-near_plane.normal); } @@ -1368,7 +1363,7 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const float lod_distance = 0.0; - if (p_render_data->cam_orthogonal) { + if (p_render_data->cam_is_orthogonal) { lod_distance = 1.0; } else { Vector3 aabb_min = inst->transformed_aabb.position; @@ -1986,7 +1981,7 @@ void RasterizerSceneGLES3::_render_shadows(const RenderDataGLES3 *p_render_data, LocalVector shadows; LocalVector directional_shadows; - float lod_distance_multiplier = p_render_data->cam_projection.get_lod_multiplier(); + float lod_distance_multiplier = p_render_data->cam_frustum.get_lod_multiplier(); // Put lights into buckets for omni (cube shadows), directional, and spot. { @@ -2156,6 +2151,7 @@ void RasterizerSceneGLES3::_render_shadow_pass(RID p_light, RID p_shadow_atlas, RenderDataGLES3 render_data; render_data.cam_projection = light_projection; + render_data.cam_frustum = light_projection.get_projection_planes(Transform3D()); render_data.cam_transform = light_transform; render_data.inv_cam_transform = light_transform.affine_inverse(); render_data.z_far = zfar; // Only used by OmniLights. @@ -2261,8 +2257,9 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ render_data.cam_transform = p_camera_data->main_transform; render_data.inv_cam_transform = render_data.cam_transform.affine_inverse(); render_data.cam_projection = p_camera_data->main_projection; - render_data.cam_orthogonal = p_camera_data->is_orthogonal; - render_data.cam_frustum = p_camera_data->is_frustum; + render_data.cam_frustum = p_camera_data->main_frustum; + render_data.cam_is_orthogonal = p_camera_data->is_orthogonal; + render_data.cam_is_frustum = p_camera_data->is_frustum; render_data.camera_visible_layers = p_camera_data->visible_layers; render_data.main_cam_transform = p_camera_data->main_transform; @@ -2272,8 +2269,8 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ render_data.view_projection[v] = p_camera_data->view_projection[v]; } - render_data.z_near = p_camera_data->main_projection.get_z_near(); - render_data.z_far = p_camera_data->main_projection.get_z_far(); + render_data.z_near = p_camera_data->main_frustum.get_z_near(); + render_data.z_far = p_camera_data->main_frustum.get_z_far(); render_data.instances = &p_instances; render_data.lights = &p_lights; @@ -2285,7 +2282,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ render_data.reflection_probe_pass = p_reflection_probe_pass; // this should be the same for all cameras.. - render_data.lod_distance_multiplier = p_camera_data->main_projection.get_lod_multiplier(); + render_data.lod_distance_multiplier = p_camera_data->main_frustum.get_lod_multiplier(); if (rt != nullptr && rt->color_type == GL_UNSIGNED_INT_2_10_10_10_REV && glow_enabled) { // As our output is in sRGB and we're using 10bit color space, we can fake a little HDR to do glow... @@ -2448,20 +2445,13 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ // setup sky if used for ambient, reflections, or background if (draw_sky || draw_sky_fog_only || sky_reflections || sky_ambient) { RENDER_TIMESTAMP("Setup Sky"); - Projection projection = render_data.cam_projection; - if (is_reflection_probe) { - Projection correction; - correction.set_depth_correction(true, true, false); - projection = correction * render_data.cam_projection; - } - sky_energy_multiplier *= bg_energy_multiplier; - _setup_sky(&render_data, *render_data.lights, projection, render_data.cam_transform, screen_size); + _setup_sky(&render_data, *render_data.lights, render_data.cam_transform, screen_size); if (environment_get_sky(render_data.environment).is_valid()) { if (sky_reflections || sky_ambient) { - _update_sky_radiance(render_data.environment, projection, render_data.cam_transform, sky_energy_multiplier); + _update_sky_radiance(render_data.environment, render_data.cam_transform, sky_energy_multiplier); } } else { // do not try to draw sky if invalid @@ -2629,7 +2619,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ Projection correction; correction.columns[1][1] = -1.0; projection = correction * render_data.cam_projection; - } else if (render_data.cam_frustum) { + } else if (render_data.cam_is_frustum) { // Sky is drawn upside down, the frustum offset doesn't know the image is upside down so needs a flip. projection[2].y = -projection[2].y; } @@ -3593,7 +3583,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } } -void RasterizerSceneGLES3::render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) { +void RasterizerSceneGLES3::render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, const Frustum &p_cam_frustum, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) { } void RasterizerSceneGLES3::render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray &p_instances) { @@ -3618,10 +3608,11 @@ void RasterizerSceneGLES3::render_particle_collider_heightfield(RID p_collider, RenderDataGLES3 render_data; render_data.cam_projection = cm; + render_data.cam_frustum = cm.get_projection_planes(Transform3D()); render_data.cam_transform = cam_xform; render_data.view_projection[0] = cm; render_data.inv_cam_transform = render_data.cam_transform.affine_inverse(); - render_data.cam_orthogonal = true; + render_data.cam_is_orthogonal = true; render_data.z_near = 0.0; render_data.z_far = cm.get_z_far(); render_data.main_cam_transform = cam_xform; diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index 4f088a0e7de1..6fc3d16aad7e 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -102,8 +102,9 @@ struct RenderDataGLES3 { Transform3D cam_transform; Transform3D inv_cam_transform; Projection cam_projection; - bool cam_orthogonal = false; - bool cam_frustum = false; + Frustum cam_frustum; + bool cam_is_orthogonal = false; + bool cam_is_frustum = false; uint32_t camera_visible_layers = 0xFFFFFFFF; // For billboards to cast correct shadows. @@ -747,10 +748,10 @@ class RasterizerSceneGLES3 : public RendererSceneRender { Sky *dirty_sky_list = nullptr; mutable RID_Owner sky_owner; - void _setup_sky(const RenderDataGLES3 *p_render_data, const PagedArray &p_lights, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size); + void _setup_sky(const RenderDataGLES3 *p_render_data, const PagedArray &p_lights, const Transform3D &p_transform, const Size2i p_screen_size); void _invalidate_sky(Sky *p_sky); void _update_dirty_skys(); - void _update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_sky_energy_multiplier); + void _update_sky_radiance(RID p_env, const Transform3D &p_transform, float p_sky_energy_multiplier); void _draw_sky(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_sky_energy_multiplier, float p_luminance_multiplier, bool p_use_multiview, bool p_flip_y, bool p_apply_color_adjustments_in_post); void _free_sky_data(Sky *p_sky); @@ -836,7 +837,7 @@ class RasterizerSceneGLES3 : public RendererSceneRender { void voxel_gi_set_quality(RS::VoxelGIQuality) override; void render_scene(const Ref &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray &p_instances, const PagedArray &p_lights, const PagedArray &p_reflection_probes, const PagedArray &p_voxel_gi_instances, const PagedArray &p_decals, const PagedArray &p_lightmaps, const PagedArray &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_compositor, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RenderingMethod::RenderInfo *r_render_info = nullptr) override; - void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) override; + void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, const Frustum &p_cam_frustum, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) override; void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray &p_instances) override; void set_scene_pass(uint64_t p_pass) override { diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index e0da52687db9..c3c12051b02f 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -34,7 +34,7 @@ #include "core/input/input.h" #include "core/input/input_map.h" #include "core/math/math_funcs.h" -#include "core/math/projection.h" +#include "core/math/frustum.h" #include "core/os/keyboard.h" #include "editor/debugger/editor_debugger_node.h" #include "editor/editor_main_screen.h" @@ -920,13 +920,13 @@ void Node3DEditorViewport::_find_items_at_pos(const Point2 &p_pos, Vector<_RayRe } Vector3 Node3DEditorViewport::_get_screen_to_space(const Vector3 &p_vector3) { - Projection cm; + Frustum fm; if (orthogonal) { - cm.set_orthogonal(camera->get_size(), get_size().aspect(), get_znear() + p_vector3.z, get_zfar()); + fm.set_orthogonal(camera->get_size(), get_size().aspect(), get_znear() + p_vector3.z, get_zfar()); } else { - cm.set_perspective(get_fov(), get_size().aspect(), get_znear() + p_vector3.z, get_zfar()); + fm.set_perspective(get_fov(), get_size().aspect(), get_znear() + p_vector3.z, get_zfar()); } - Vector2 screen_he = cm.get_viewport_half_extents(); + Vector2 screen_he = fm.get_viewport_half_extents(); Transform3D camera_transform; camera_transform.translate_local(cursor.pos); diff --git a/modules/raycast/raycast_occlusion_cull.cpp b/modules/raycast/raycast_occlusion_cull.cpp index 4fcbe857b687..4405fa80060a 100644 --- a/modules/raycast/raycast_occlusion_cull.cpp +++ b/modules/raycast/raycast_occlusion_cull.cpp @@ -579,7 +579,7 @@ Vector2 RaycastOcclusionCull::_jitter_half_extents(const Vector2 &p_half_extents return p_half_extents + jitter; } -void RaycastOcclusionCull::buffer_update(RID p_buffer, const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal) { +void RaycastOcclusionCull::buffer_update(RID p_buffer, const Transform3D &p_cam_transform, const Frustum &p_cam_frustum, bool p_cam_orthogonal) { if (!buffers.has(p_buffer)) { return; } @@ -593,11 +593,11 @@ void RaycastOcclusionCull::buffer_update(RID p_buffer, const Transform3D &p_cam_ Scenario &scenario = scenarios[buffer.scenario_rid]; scenario.update(); - Vector2 viewport_half = p_cam_projection.get_viewport_half_extents(); + Vector2 viewport_half = p_cam_frustum.get_viewport_half_extents(); Vector2 jitter_viewport_half = _jitter_half_extents(viewport_half, buffer.get_occlusion_buffer_size()); - Vector3 near_bottom_left = Vector3(-jitter_viewport_half.x, -jitter_viewport_half.y, -p_cam_projection.get_z_near()); + Vector3 near_bottom_left = Vector3(-jitter_viewport_half.x, -jitter_viewport_half.y, -p_cam_frustum.get_z_near()); - buffer.update_camera_rays(p_cam_transform, near_bottom_left, 2 * viewport_half, p_cam_projection.get_z_far(), p_cam_orthogonal); + buffer.update_camera_rays(p_cam_transform, near_bottom_left, 2 * viewport_half, p_cam_frustum.get_z_far(), p_cam_orthogonal); scenario.raycast(buffer.camera_rays, buffer.camera_ray_masks.ptr(), buffer.camera_rays_tile_count); buffer.sort_rays(-p_cam_transform.basis.get_column(2), p_cam_orthogonal); diff --git a/modules/raycast/raycast_occlusion_cull.h b/modules/raycast/raycast_occlusion_cull.h index 2726b0caadf8..2f39fd5d3974 100644 --- a/modules/raycast/raycast_occlusion_cull.h +++ b/modules/raycast/raycast_occlusion_cull.h @@ -32,6 +32,7 @@ #define RAYCAST_OCCLUSION_CULL_H #include "core/io/image.h" +#include "core/math/frustum.h" #include "core/math/projection.h" #include "core/object/object.h" #include "core/object/ref_counted.h" @@ -185,7 +186,7 @@ class RaycastOcclusionCull : public RendererSceneOcclusionCull { virtual HZBuffer *buffer_get_ptr(RID p_buffer) override; virtual void buffer_set_scenario(RID p_buffer, RID p_scenario) override; virtual void buffer_set_size(RID p_buffer, const Vector2i &p_size) override; - virtual void buffer_update(RID p_buffer, const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal) override; + virtual void buffer_update(RID p_buffer, const Transform3D &p_cam_transform, const Frustum &p_cam_frustum, bool p_cam_orthogonal) override; virtual RID buffer_get_debug_texture(RID p_buffer) override; diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index c520a42efbc4..07b27b9d957d 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -30,6 +30,7 @@ #include "camera_3d.h" +#include "core/math/frustum.h" #include "core/math/projection.h" #include "core/math/transform_interpolator.h" #include "scene/main/viewport.h" @@ -315,6 +316,25 @@ Projection Camera3D::_get_camera_projection(real_t p_near) const { return cm; } +Frustum Camera3D::_get_camera_frustum() const { + Size2 viewport_size = get_viewport()->get_visible_rect().size; + Frustum fm; + + switch (mode) { + case PROJECTION_PERSPECTIVE: { + fm.set_perspective(fov, viewport_size.aspect(), _near, _far, keep_aspect == KEEP_WIDTH); + } break; + case PROJECTION_ORTHOGONAL: { + fm.set_orthogonal(size, viewport_size.aspect(), _near, _far, keep_aspect == KEEP_WIDTH); + } break; + case PROJECTION_FRUSTUM: { + fm.set_frustum(size, viewport_size.aspect(), frustum_offset, _near, _far); + } break; + } + + return fm; +} + Projection Camera3D::get_camera_projection() const { ERR_FAIL_COND_V_MSG(!is_inside_tree(), Projection(), "Camera is not inside the scene tree."); return _get_camera_projection(_near); @@ -436,8 +456,8 @@ Vector3 Camera3D::project_local_ray_normal(const Point2 &p_pos) const { if (mode == PROJECTION_ORTHOGONAL) { ray = Vector3(0, 0, -1); } else { - Projection cm = _get_camera_projection(_near); - Vector2 screen_he = cm.get_viewport_half_extents(); + Frustum fm = _get_camera_frustum(); + Vector2 screen_he = fm.get_viewport_half_extents(); ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -_near).normalized(); } @@ -482,10 +502,10 @@ bool Camera3D::is_position_behind(const Vector3 &p_pos) const { Vector Camera3D::get_near_plane_points() const { ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector(), "Camera is not inside scene."); - Projection cm = _get_camera_projection(_near); + Frustum fm = _get_camera_frustum(); Vector3 endpoints[8]; - cm.get_endpoints(Transform3D(), endpoints); + fm.get_endpoints(Transform3D(), endpoints); Vector points = { Vector3(), @@ -529,11 +549,11 @@ Vector3 Camera3D::project_position(const Point2 &p_point, real_t p_z_depth) cons } Size2 viewport_size = get_viewport()->get_visible_rect().size; - Projection cm = _get_camera_projection(_near); + Frustum fm = _get_camera_frustum(); Plane z_slice(Vector3(0, 0, 1), -p_z_depth); Vector3 res; - z_slice.intersect_3(cm.get_projection_plane(Projection::Planes::PLANE_RIGHT), cm.get_projection_plane(Projection::Planes::PLANE_TOP), &res); + z_slice.intersect_3(fm.planes[Projection::Planes::PLANE_RIGHT], fm.planes[Projection::Planes::PLANE_TOP], &res); Vector2 vp_he(res.x, res.y); Vector2 point; @@ -807,10 +827,8 @@ bool Camera3D::get_cull_mask_value(int p_layer_number) const { Vector Camera3D::get_frustum() const { ERR_FAIL_COND_V(!is_inside_world(), Vector()); - - Projection cm = _get_camera_projection(_near); - - return cm.get_projection_planes(get_camera_transform()); + Frustum fm = _get_camera_frustum(); + return fm.get_projection_planes(get_camera_transform()); } TypedArray Camera3D::_get_frustum() const { diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index 3e9f940ad648..7ab95903e98f 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -37,6 +37,8 @@ #include "scene/resources/compositor.h" #include "scene/resources/environment.h" +struct Frustum; + class Camera3D : public Node3D { GDCLASS(Camera3D, Node3D); @@ -141,6 +143,7 @@ class Camera3D : public Node3D { static void _bind_methods(); Projection _get_camera_projection(real_t p_near) const; + Frustum _get_camera_frustum() const; public: enum { diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 6c83c7843fb8..b3829f3f6c85 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -4315,10 +4315,10 @@ Vector3 Viewport::camera_3d_override_project_local_ray_normal(const Point2 &p_po if (camera_3d_override.projection == Camera3DOverrideData::PROJECTION_ORTHOGONAL) { ray = Vector3(0, 0, -1); } else { - Projection cm; - cm.set_perspective(camera_3d_override.fov, get_visible_rect().size.aspect(), camera_3d_override.z_near, camera_3d_override.z_far, false); + Frustum fm; + fm.set_perspective(camera_3d_override.fov, get_visible_rect().size.aspect(), camera_3d_override.z_near, camera_3d_override.z_far, false); - Vector2 screen_he = cm.get_viewport_half_extents(); + Vector2 screen_he = fm.get_viewport_half_extents(); ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -camera_3d_override.z_near).normalized(); } diff --git a/servers/rendering/dummy/rasterizer_scene_dummy.h b/servers/rendering/dummy/rasterizer_scene_dummy.h index f129c8674604..7ef8504dd857 100644 --- a/servers/rendering/dummy/rasterizer_scene_dummy.h +++ b/servers/rendering/dummy/rasterizer_scene_dummy.h @@ -151,7 +151,7 @@ class RasterizerSceneDummy : public RendererSceneRender { void voxel_gi_set_quality(RS::VoxelGIQuality) override {} void render_scene(const Ref &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray &p_instances, const PagedArray &p_lights, const PagedArray &p_reflection_probes, const PagedArray &p_voxel_gi_instances, const PagedArray &p_decals, const PagedArray &p_lightmaps, const PagedArray &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_compositor, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RenderingMethod::RenderInfo *r_info = nullptr) override {} - void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) override {} + void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, const Frustum &p_cam_frustum, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) override {} void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray &p_instances) override {} void set_scene_pass(uint64_t p_pass) override {} diff --git a/servers/rendering/renderer_rd/cluster_builder_rd.cpp b/servers/rendering/renderer_rd/cluster_builder_rd.cpp index 752e0fe45319..504d006a22df 100644 --- a/servers/rendering/renderer_rd/cluster_builder_rd.cpp +++ b/servers/rendering/renderer_rd/cluster_builder_rd.cpp @@ -393,20 +393,18 @@ void ClusterBuilderRD::setup(Size2i p_screen_size, uint32_t p_max_elements, RID } } -void ClusterBuilderRD::begin(const Transform3D &p_view_transform, const Projection &p_cam_projection, bool p_flip_y) { +void ClusterBuilderRD::begin(const Transform3D &p_view_transform, const Projection &p_cam_projection, const Frustum &p_cam_frustum, bool p_flip_y) { view_xform = p_view_transform.affine_inverse(); - projection = p_cam_projection; - z_near = projection.get_z_near(); - z_far = projection.get_z_far(); - camera_orthogonal = p_cam_projection.is_orthogonal(); - adjusted_projection = projection; + z_near = p_cam_frustum.get_z_near(); + z_far = p_cam_frustum.get_z_far(); + camera_orthogonal = p_cam_frustum.is_orthogonal(); + adjusted_projection = p_cam_projection; if (!camera_orthogonal) { - adjusted_projection.adjust_perspective_znear(0.0001); + adjusted_projection.adjust_perspective_znear_zfar(0.0001, z_far); } Projection correction; correction.set_depth_correction(p_flip_y); - projection = correction * projection; adjusted_projection = correction * adjusted_projection; // Reset counts. diff --git a/servers/rendering/renderer_rd/cluster_builder_rd.h b/servers/rendering/renderer_rd/cluster_builder_rd.h index 1badce2b8111..6d6be927b291 100644 --- a/servers/rendering/renderer_rd/cluster_builder_rd.h +++ b/servers/rendering/renderer_rd/cluster_builder_rd.h @@ -173,7 +173,6 @@ class ClusterBuilderRD { Transform3D view_xform; Projection adjusted_projection; - Projection projection; float z_far = 0; float z_near = 0; bool camera_orthogonal = false; @@ -232,7 +231,7 @@ class ClusterBuilderRD { public: void setup(Size2i p_screen_size, uint32_t p_max_elements, RID p_depth_buffer, RID p_depth_buffer_sampler, RID p_color_buffer); - void begin(const Transform3D &p_view_transform, const Projection &p_cam_projection, bool p_flip_y); + void begin(const Transform3D &p_view_transform, const Projection &p_cam_projection, const Frustum &p_cam_frustum, bool p_flip_y); _FORCE_INLINE_ void add_light(LightType p_type, const Transform3D &p_transform, float p_radius, float p_spot_aperture) { if (p_type == LIGHT_TYPE_OMNI && cluster_count_by_type[ELEMENT_TYPE_OMNI_LIGHT] == max_elements_by_type) { diff --git a/servers/rendering/renderer_rd/effects/debug_effects.cpp b/servers/rendering/renderer_rd/effects/debug_effects.cpp index ac06f28f4c33..7f20c9986ac2 100644 --- a/servers/rendering/renderer_rd/effects/debug_effects.cpp +++ b/servers/rendering/renderer_rd/effects/debug_effects.cpp @@ -178,7 +178,7 @@ DebugEffects::~DebugEffects() { motion_vectors.shader.version_free(motion_vectors.shader_version); } -void DebugEffects::draw_shadow_frustum(RID p_light, const Projection &p_cam_projection, const Transform3D &p_cam_transform, RID p_dest_fb, const Rect2 p_rect) { +void DebugEffects::draw_shadow_frustum(RID p_light, const Frustum &p_cam_frustum, const Transform3D &p_cam_transform, RID p_dest_fb, const Rect2 p_rect) { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); RID base = light_storage->light_instance_get_base_light(p_light); @@ -202,17 +202,17 @@ void DebugEffects::draw_shadow_frustum(RID p_light, const Projection &p_cam_proj } // Setup our camera info (this is mostly a duplicate of the logic found in RendererSceneCull::_light_instance_setup_directional_shadow). - bool is_orthogonal = p_cam_projection.is_orthogonal(); - real_t aspect = p_cam_projection.get_aspect(); + bool is_orthogonal = p_cam_frustum.is_orthogonal(); + real_t aspect = p_cam_frustum.get_aspect(); real_t fov = 0.0; Vector2 vp_he; if (is_orthogonal) { - vp_he = p_cam_projection.get_viewport_half_extents(); + vp_he = p_cam_frustum.get_viewport_half_extents(); } else { - fov = p_cam_projection.get_fov(); //this is actually yfov, because set aspect tries to keep it + fov = p_cam_frustum.get_fov(); //this is actually yfov, because set aspect tries to keep it } - real_t min_distance = p_cam_projection.get_z_near(); - real_t max_distance = p_cam_projection.get_z_far(); + real_t min_distance = p_cam_frustum.get_z_near(); + real_t max_distance = p_cam_frustum.get_z_far(); real_t shadow_max = RSG::light_storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_MAX_DISTANCE); if (shadow_max > 0 && !is_orthogonal) { max_distance = MIN(shadow_max, max_distance); diff --git a/servers/rendering/renderer_rd/effects/debug_effects.h b/servers/rendering/renderer_rd/effects/debug_effects.h index b813d577e4c0..42e49a821abb 100644 --- a/servers/rendering/renderer_rd/effects/debug_effects.h +++ b/servers/rendering/renderer_rd/effects/debug_effects.h @@ -92,7 +92,7 @@ class DebugEffects { DebugEffects(); ~DebugEffects(); - void draw_shadow_frustum(RID p_light, const Projection &p_cam_projection, const Transform3D &p_cam_transform, RID p_dest_fb, const Rect2 p_rect); + void draw_shadow_frustum(RID p_light, const Frustum &p_cam_frustum, const Transform3D &p_cam_transform, RID p_dest_fb, const Rect2 p_rect); void draw_motion_vectors(RID p_velocity, RID p_depth, RID p_dest_fb, const Projection &p_current_projection, const Transform3D &p_current_transform, const Projection &p_previous_projection, const Transform3D &p_previous_transform, Size2i p_resolution); }; diff --git a/servers/rendering/renderer_rd/environment/fog.cpp b/servers/rendering/renderer_rd/environment/fog.cpp index 090eab384f61..a45ff7e4f708 100644 --- a/servers/rendering/renderer_rd/environment/fog.cpp +++ b/servers/rendering/renderer_rd/environment/fog.cpp @@ -514,7 +514,7 @@ Vector3i Fog::_point_get_position_in_froxel_volume(const Vector3 &p_point, float return Vector3i(fog_position); } -void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const Projection &p_cam_projection, const Transform3D &p_cam_transform, const Transform3D &p_prev_cam_inv_transform, RID p_shadow_atlas, int p_directional_light_count, bool p_use_directional_shadows, int p_positional_light_count, int p_voxel_gi_count, const PagedArray &p_fog_volumes) { +void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const Frustum &p_cam_frustum, const Transform3D &p_cam_transform, const Transform3D &p_prev_cam_inv_transform, RID p_shadow_atlas, int p_directional_light_count, bool p_use_directional_shadows, int p_positional_light_count, int p_voxel_gi_count, const PagedArray &p_fog_volumes) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); @@ -530,15 +530,15 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P VolumetricFogShader::VolumeUBO params; - Vector2 frustum_near_size = p_cam_projection.get_viewport_half_extents(); - Vector2 frustum_far_size = p_cam_projection.get_far_plane_half_extents(); - float z_near = p_cam_projection.get_z_near(); - float z_far = p_cam_projection.get_z_far(); + Vector2 frustum_near_size = p_cam_frustum.get_viewport_half_extents(); + Vector2 frustum_far_size = p_cam_frustum.get_far_plane_half_extents(); + float z_near = p_cam_frustum.get_z_near(); + float z_far = p_cam_frustum.get_z_far(); float fog_end = RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_length(p_settings.env); Vector2 fog_far_size = frustum_near_size.lerp(frustum_far_size, (fog_end - z_near) / (z_far - z_near)); Vector2 fog_near_size; - if (p_cam_projection.is_orthogonal()) { + if (p_cam_frustum.is_orthogonal()) { fog_near_size = fog_far_size; } else { fog_near_size = frustum_near_size.maxf(0.001); @@ -1018,15 +1018,15 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P VolumetricFogShader::ParamsUBO params; - Vector2 frustum_near_size = p_cam_projection.get_viewport_half_extents(); - Vector2 frustum_far_size = p_cam_projection.get_far_plane_half_extents(); - float z_near = p_cam_projection.get_z_near(); - float z_far = p_cam_projection.get_z_far(); + Vector2 frustum_near_size = p_cam_frustum.get_viewport_half_extents(); + Vector2 frustum_far_size = p_cam_frustum.get_far_plane_half_extents(); + float z_near = p_cam_frustum.get_z_near(); + float z_far = p_cam_frustum.get_z_far(); float fog_end = RendererSceneRenderRD::get_singleton()->environment_get_volumetric_fog_length(p_settings.env); Vector2 fog_far_size = frustum_near_size.lerp(frustum_far_size, (fog_end - z_near) / (z_far - z_near)); Vector2 fog_near_size; - if (p_cam_projection.is_orthogonal()) { + if (p_cam_frustum.is_orthogonal()) { fog_near_size = fog_far_size; } else { fog_near_size = frustum_near_size.maxf(0.001); diff --git a/servers/rendering/renderer_rd/environment/fog.h b/servers/rendering/renderer_rd/environment/fog.h index 23de97a268b8..2d4efc7900e9 100644 --- a/servers/rendering/renderer_rd/environment/fog.h +++ b/servers/rendering/renderer_rd/environment/fog.h @@ -351,7 +351,7 @@ class Fog : public RendererFog { RID env; SkyRD *sky; }; - void volumetric_fog_update(const VolumetricFogSettings &p_settings, const Projection &p_cam_projection, const Transform3D &p_cam_transform, const Transform3D &p_prev_cam_inv_transform, RID p_shadow_atlas, int p_directional_light_count, bool p_use_directional_shadows, int p_positional_light_count, int p_voxel_gi_count, const PagedArray &p_fog_volumes); + void volumetric_fog_update(const VolumetricFogSettings &p_settings, const Frustum &p_cam_frustum, const Transform3D &p_cam_transform, const Transform3D &p_prev_cam_inv_transform, RID p_shadow_atlas, int p_directional_light_count, bool p_use_directional_shadows, int p_positional_light_count, int p_voxel_gi_count, const PagedArray &p_fog_volumes); }; } // namespace RendererRD diff --git a/servers/rendering/renderer_rd/environment/gi.cpp b/servers/rendering/renderer_rd/environment/gi.cpp index c4a89996b5f0..3c00e522ab32 100644 --- a/servers/rendering/renderer_rd/environment/gi.cpp +++ b/servers/rendering/renderer_rd/environment/gi.cpp @@ -3107,7 +3107,9 @@ void GI::VoxelGIInstance::update(bool p_update_light_instances, const Vector 0); Projection cm; + Frustum fm; cm.set_orthogonal(-rect.size.width / 2, rect.size.width / 2, -rect.size.height / 2, rect.size.height / 2, 0.0001, aabb.size[z_axis]); + fm.set_orthogonal(-rect.size.width / 2, rect.size.width / 2, -rect.size.height / 2, rect.size.height / 2, 0.0001, aabb.size[z_axis]); if (RendererSceneRenderRD::get_singleton()->cull_argument.size() == 0) { RendererSceneRenderRD::get_singleton()->cull_argument.push_back(nullptr); @@ -3119,7 +3121,7 @@ void GI::VoxelGIInstance::update(bool p_update_light_instances, const Vectorvoxel_gi_get_baked_exposure_normalization(probe); } - RendererSceneRenderRD::get_singleton()->_render_material(to_world_xform * xform, cm, true, RendererSceneRenderRD::get_singleton()->cull_argument, dynamic_maps[0].fb, Rect2i(Vector2i(), rect.size), exposure_normalization); + RendererSceneRenderRD::get_singleton()->_render_material(to_world_xform * xform, cm, fm, true, RendererSceneRenderRD::get_singleton()->cull_argument, dynamic_maps[0].fb, Rect2i(Vector2i(), rect.size), exposure_normalization); VoxelGIDynamicPushConstant push_constant; memset(&push_constant, 0, sizeof(VoxelGIDynamicPushConstant)); diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp index c59332626d75..ebd32567bad5 100644 --- a/servers/rendering/renderer_rd/environment/sky.cpp +++ b/servers/rendering/renderer_rd/environment/sky.cpp @@ -1179,7 +1179,7 @@ void SkyRD::setup_sky(const RenderDataRD *p_render_data, const Size2i p_screen_s correction.add_jitter_offset(p_render_data->scene_data->taa_jitter); Projection projection = p_render_data->scene_data->cam_projection; - if (p_render_data->scene_data->cam_frustum) { + if (p_render_data->scene_data->cam_is_frustum) { // We don't use a full projection matrix for the sky, this is enough to make up for it. projection[2].y = -projection[2].y; } @@ -1205,7 +1205,7 @@ void SkyRD::setup_sky(const RenderDataRD *p_render_data, const Size2i p_screen_s sky_scene_state.ubo.view_eye_offsets[i][3] = 0.0; } - sky_scene_state.ubo.z_far = p_render_data->scene_data->view_projection[0].get_z_far(); // Should be the same for all projection. + sky_scene_state.ubo.z_far = p_render_data->scene_data->z_far; sky_scene_state.ubo.fog_enabled = RendererSceneRenderRD::get_singleton()->environment_get_fog_enabled(p_render_data->environment); sky_scene_state.ubo.fog_density = RendererSceneRenderRD::get_singleton()->environment_get_fog_density(p_render_data->environment); sky_scene_state.ubo.fog_aerial_perspective = RendererSceneRenderRD::get_singleton()->environment_get_fog_aerial_perspective(p_render_data->environment); @@ -1454,11 +1454,7 @@ void SkyRD::update_res_buffers(Ref p_render_buffers, RID p if (custom_fov && sky_scene_state.view_count == 1) { // With custom fov we don't support stereo... - float near_plane = projection.get_z_near(); - float far_plane = projection.get_z_far(); - float aspect = projection.get_aspect(); - - projection.set_perspective(custom_fov, aspect, near_plane, far_plane); + projection.adjust_perspective_fov(custom_fov); } sky_transform = sky_transform * sky_scene_state.cam_transform.basis; @@ -1555,11 +1551,7 @@ void SkyRD::draw_sky(RD::DrawListID p_draw_list, Ref p_ren if (custom_fov && sky_scene_state.view_count == 1) { // With custom fov we don't support stereo... - float near_plane = projection.get_z_near(); - float far_plane = projection.get_z_far(); - float aspect = projection.get_aspect(); - - projection.set_perspective(custom_fov, aspect, near_plane, far_plane); + projection.adjust_perspective_fov(custom_fov); } sky_transform = sky_transform * sky_scene_state.cam_transform.basis; diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index fbe19dfe7b78..74e9a42e9399 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -881,8 +881,8 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con uint32_t lightmap_captures_used = 0; Plane near_plane = Plane(-p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z), p_render_data->scene_data->cam_transform.origin); - near_plane.d += p_render_data->scene_data->cam_projection.get_z_near(); - float z_max = p_render_data->scene_data->cam_projection.get_z_far() - p_render_data->scene_data->cam_projection.get_z_near(); + near_plane.d += p_render_data->scene_data->z_near; + float z_max = p_render_data->scene_data->z_far - p_render_data->scene_data->z_near; RenderList *rl = &render_list[p_render_list]; _update_dirty_geometry_instances(); @@ -902,7 +902,7 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con GeometryInstanceForwardClustered *inst = static_cast((*p_render_data->instances)[i]); Vector3 center = inst->transform.origin; - if (p_render_data->scene_data->cam_orthogonal) { + if (p_render_data->scene_data->cam_is_orthogonal) { if (inst->use_aabb_center) { center = inst->transformed_aabb.get_support(-near_plane.normal); } @@ -1029,7 +1029,7 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con float lod_distance = 0.0; - if (p_render_data->scene_data->cam_orthogonal) { + if (p_render_data->scene_data->cam_is_orthogonal) { lod_distance = 1.0; } else { Vector3 aabb_min = inst->transformed_aabb.position; @@ -1244,7 +1244,7 @@ void RenderForwardClustered::_debug_draw_cluster(Ref p_ren //////////////////////////////////////////////////////////////////////////////// // FOG SHADER -void RenderForwardClustered::_update_volumetric_fog(Ref p_render_buffers, RID p_environment, const Projection &p_cam_projection, const Transform3D &p_cam_transform, const Transform3D &p_prev_cam_inv_transform, RID p_shadow_atlas, int p_directional_light_count, bool p_use_directional_shadows, int p_positional_light_count, int p_voxel_gi_count, const PagedArray &p_fog_volumes) { +void RenderForwardClustered::_update_volumetric_fog(Ref p_render_buffers, RID p_environment, const Frustum &p_cam_frustum, const Transform3D &p_cam_transform, const Transform3D &p_prev_cam_inv_transform, RID p_shadow_atlas, int p_directional_light_count, bool p_use_directional_shadows, int p_positional_light_count, int p_voxel_gi_count, const PagedArray &p_fog_volumes) { ERR_FAIL_COND(p_render_buffers.is_null()); Ref rb_data = p_render_buffers->get_custom_data(RB_SCOPE_FORWARD_CLUSTERED); @@ -1312,7 +1312,7 @@ void RenderForwardClustered::_update_volumetric_fog(Ref p_ settings.sky = &sky; settings.gi = &gi; - RendererRD::Fog::get_singleton()->volumetric_fog_update(settings, p_cam_projection, p_cam_transform, p_prev_cam_inv_transform, p_shadow_atlas, p_directional_light_count, p_use_directional_shadows, p_positional_light_count, p_voxel_gi_count, p_fog_volumes); + RendererRD::Fog::get_singleton()->volumetric_fog_update(settings, p_cam_frustum, p_cam_transform, p_prev_cam_inv_transform, p_shadow_atlas, p_directional_light_count, p_use_directional_shadows, p_positional_light_count, p_voxel_gi_count, p_fog_volumes); } } @@ -1452,7 +1452,7 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo p_render_data->shadows.clear(); p_render_data->directional_shadows.clear(); - float lod_distance_multiplier = p_render_data->scene_data->cam_projection.get_lod_multiplier(); + float lod_distance_multiplier = p_render_data->scene_data->cam_frustum.get_lod_multiplier(); { for (int i = 0; i < p_render_data->render_shadow_count; i++) { RID li = p_render_data->render_shadows[i].light; @@ -1548,7 +1548,7 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo // This only works as we don't filter our cluster by depth buffer. // If we ever make this optimization we should make it optional and only use it in mono. // What we win by filtering out a few lights, we loose by having to do the work double for stereo. - current_cluster_builder->begin(p_render_data->scene_data->cam_transform, p_render_data->scene_data->cam_projection, !p_render_data->reflection_probe.is_valid()); + current_cluster_builder->begin(p_render_data->scene_data->cam_transform, p_render_data->scene_data->cam_projection, p_render_data->scene_data->cam_frustum, !p_render_data->reflection_probe.is_valid()); } bool using_shadows = true; @@ -1576,7 +1576,7 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo if (rb_data.is_valid()) { RENDER_TIMESTAMP("Update Volumetric Fog"); bool directional_shadows = RendererRD::LightStorage::get_singleton()->has_directional_shadows(directional_light_count); - _update_volumetric_fog(rb, p_render_data->environment, p_render_data->scene_data->cam_projection, p_render_data->scene_data->cam_transform, p_render_data->scene_data->prev_cam_transform.affine_inverse(), p_render_data->shadow_atlas, directional_light_count, directional_shadows, positional_light_count, p_render_data->voxel_gi_count, *p_render_data->fog_volumes); + _update_volumetric_fog(rb, p_render_data->environment, p_render_data->scene_data->cam_frustum, p_render_data->scene_data->cam_transform, p_render_data->scene_data->prev_cam_transform.affine_inverse(), p_render_data->shadow_atlas, directional_light_count, directional_shadows, positional_light_count, p_render_data->voxel_gi_count, *p_render_data->fog_volumes); } } @@ -2348,9 +2348,9 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RENDER_TIMESTAMP("FSR2"); for (uint32_t v = 0; v < rb->get_view_count(); v++) { - real_t fov = p_render_data->scene_data->cam_projection.get_fov(); - real_t aspect = p_render_data->scene_data->cam_projection.get_aspect(); - real_t fovy = p_render_data->scene_data->cam_projection.get_fovy(fov, aspect); + real_t fov = p_render_data->scene_data->cam_frustum.get_fov(); + real_t aspect = p_render_data->scene_data->cam_frustum.get_aspect(); + real_t fovy = p_render_data->scene_data->cam_frustum.get_fovy(fov, aspect); Vector2 jitter = p_render_data->scene_data->taa_jitter * Vector2(rb->get_internal_size()) * 0.5f; RendererRD::FSR2Effect::Parameters params; params.context = rb_data->get_fsr2_context(); @@ -2644,6 +2644,7 @@ void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const Page RenderSceneDataRD scene_data; scene_data.flip_y = !p_flip_y; // Q: Why is this inverted? Do we assume flip in shadow logic? scene_data.cam_projection = p_projection; + scene_data.cam_frustum = p_projection.get_projection_planes(Transform3D()); scene_data.cam_transform = p_transform; scene_data.view_projection[0] = p_projection; scene_data.z_far = p_zfar; @@ -2737,6 +2738,7 @@ void RenderForwardClustered::_render_particle_collider_heightfield(RID p_fb, con RenderSceneDataRD scene_data; scene_data.flip_y = true; scene_data.cam_projection = p_cam_projection; + scene_data.cam_frustum = p_cam_projection.get_projection_planes(Transform3D()); scene_data.cam_transform = p_cam_transform; scene_data.view_projection[0] = p_cam_projection; scene_data.z_near = 0.0; @@ -2775,13 +2777,14 @@ void RenderForwardClustered::_render_particle_collider_heightfield(RID p_fb, con RD::get_singleton()->draw_command_end_label(); } -void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) { +void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, const Frustum &p_cam_frustum, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) { RENDER_TIMESTAMP("Setup Rendering 3D Material"); RD::get_singleton()->draw_command_begin_label("Render 3D Material"); RenderSceneDataRD scene_data; scene_data.cam_projection = p_cam_projection; + scene_data.cam_frustum = p_cam_frustum; scene_data.cam_transform = p_cam_transform; scene_data.view_projection[0] = p_cam_projection; scene_data.dual_paraboloid_side = 0; @@ -2963,6 +2966,7 @@ void RenderForwardClustered::_render_sdfgi(Ref p_render_bu float v_size = half_size[up_axis]; float d_size = half_size[i] * 2.0; scene_data.cam_projection.set_orthogonal(-h_size, h_size, -v_size, v_size, 0, d_size); + scene_data.cam_frustum.set_orthogonal(-h_size, h_size, -v_size, v_size, 0, d_size); //print_line("pass: " + itos(i) + " cam hsize: " + rtos(h_size) + " vsize: " + rtos(v_size) + " dsize " + rtos(d_size)); Transform3D to_bounds; diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index 728164c38e06..2a88a021edcc 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -648,7 +648,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { /* Volumetric fog */ RID shadow_sampler; - void _update_volumetric_fog(Ref p_render_buffers, RID p_environment, const Projection &p_cam_projection, const Transform3D &p_cam_transform, const Transform3D &p_prev_cam_inv_transform, RID p_shadow_atlas, int p_directional_light_count, bool p_use_directional_shadows, int p_positional_light_count, int p_voxel_gi_count, const PagedArray &p_fog_volumes); + void _update_volumetric_fog(Ref p_render_buffers, RID p_environment, const Frustum &p_cam_frustum, const Transform3D &p_cam_transform, const Transform3D &p_prev_cam_inv_transform, RID p_shadow_atlas, int p_directional_light_count, bool p_use_directional_shadows, int p_positional_light_count, int p_voxel_gi_count, const PagedArray &p_fog_volumes); /* Render shadows */ @@ -687,7 +687,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { virtual void _render_scene(RenderDataRD *p_render_data, const Color &p_default_bg_color) override; virtual void _render_buffers_debug_draw(const RenderDataRD *p_render_data) override; - virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) override; + virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, const Frustum &p_cam_frustum, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) override; virtual void _render_uv2(const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) override; virtual void _render_sdfgi(Ref p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) override; virtual void _render_particle_collider_heightfield(RID p_fb, const Transform3D &p_cam_transform, const Projection &p_cam_projection, const PagedArray &p_instances) override; diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index ef08c83c96ad..427b720c9e0f 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -666,7 +666,7 @@ void RenderForwardMobile::_pre_opaque_render(RenderDataRD *p_render_data) { p_render_data->shadows.clear(); p_render_data->directional_shadows.clear(); - float lod_distance_multiplier = p_render_data->scene_data->cam_projection.get_lod_multiplier(); + float lod_distance_multiplier = p_render_data->scene_data->cam_frustum.get_lod_multiplier(); { for (int i = 0; i < p_render_data->render_shadow_count; i++) { RID li = p_render_data->render_shadows[i].light; @@ -1396,6 +1396,7 @@ void RenderForwardMobile::_render_shadow_append(RID p_framebuffer, const PagedAr RenderSceneDataRD scene_data; scene_data.flip_y = !p_flip_y; // Q: Why is this inverted? Do we assume flip in shadow logic? scene_data.cam_projection = p_projection; + scene_data.cam_frustum = p_projection.get_projection_planes(Transform3D()); scene_data.cam_transform = p_transform; scene_data.view_projection[0] = p_projection; scene_data.z_near = 0.0; @@ -1477,7 +1478,7 @@ void RenderForwardMobile::_render_shadow_end() { /* */ -void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) { +void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, const Frustum &p_cam_frustum, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) { RENDER_TIMESTAMP("Setup Rendering 3D Material"); RD::get_singleton()->draw_command_begin_label("Render 3D Material"); @@ -1486,6 +1487,7 @@ void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, c RenderSceneDataRD scene_data; scene_data.cam_projection = p_cam_projection; + scene_data.cam_frustum = p_cam_frustum; scene_data.cam_transform = p_cam_transform; scene_data.view_projection[0] = p_cam_projection; scene_data.dual_paraboloid_side = 0; @@ -1613,6 +1615,7 @@ void RenderForwardMobile::_render_particle_collider_heightfield(RID p_fb, const RenderSceneDataRD scene_data; scene_data.flip_y = true; scene_data.cam_projection = p_cam_projection; + scene_data.cam_frustum = p_cam_projection.get_projection_planes(Transform3D()); scene_data.cam_transform = p_cam_transform; scene_data.view_projection[0] = p_cam_projection; scene_data.z_near = 0.0; @@ -1873,8 +1876,8 @@ void RenderForwardMobile::_fill_render_list(RenderListType p_render_list, const uint32_t lightmap_captures_used = 0; Plane near_plane(-p_render_data->scene_data->cam_transform.basis.get_column(Vector3::AXIS_Z), p_render_data->scene_data->cam_transform.origin); - near_plane.d += p_render_data->scene_data->cam_projection.get_z_near(); - float z_max = p_render_data->scene_data->cam_projection.get_z_far() - p_render_data->scene_data->cam_projection.get_z_near(); + near_plane.d += p_render_data->scene_data->z_near; + float z_max = p_render_data->scene_data->z_far - p_render_data->scene_data->z_near; RenderList *rl = &render_list[p_render_list]; @@ -1894,7 +1897,7 @@ void RenderForwardMobile::_fill_render_list(RenderListType p_render_list, const GeometryInstanceForwardMobile *inst = static_cast((*p_render_data->instances)[i]); Vector3 center = inst->transform.origin; - if (p_render_data->scene_data->cam_orthogonal) { + if (p_render_data->scene_data->cam_is_orthogonal) { if (inst->use_aabb_center) { center = inst->transformed_aabb.get_support(-near_plane.normal); } @@ -1960,7 +1963,7 @@ void RenderForwardMobile::_fill_render_list(RenderListType p_render_list, const float lod_distance = 0.0; - if (p_render_data->scene_data->cam_orthogonal) { + if (p_render_data->scene_data->cam_is_orthogonal) { lod_distance = 1.0; } else { Vector3 aabb_min = inst->transformed_aabb.position; diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h index 5a4c11404180..1a5cdab1e63b 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -496,7 +496,7 @@ class RenderForwardMobile : public RendererSceneRenderRD { virtual void _render_scene(RenderDataRD *p_render_data, const Color &p_default_bg_color) override; - virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) override; + virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, const Frustum &p_cam_frustum, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) override; virtual void _render_uv2(const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) override; virtual void _render_sdfgi(Ref p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) override; virtual void _render_particle_collider_heightfield(RID p_fb, const Transform3D &p_cam_transform, const Projection &p_cam_projection, const PagedArray &p_instances) override; diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 7ef42aa79147..0855dcc0f93d 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -458,7 +458,7 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende // In stereo p_render_data->z_near and p_render_data->z_far can be offset for our combined frustum. float z_near = p_render_data->scene_data->view_projection[i].get_z_near(); float z_far = p_render_data->scene_data->view_projection[i].get_z_far(); - bokeh_dof->bokeh_dof_compute(buffers, p_render_data->camera_attributes, z_near, z_far, p_render_data->scene_data->cam_orthogonal); + bokeh_dof->bokeh_dof_compute(buffers, p_render_data->camera_attributes, z_near, z_far, p_render_data->scene_data->cam_is_orthogonal); }; } else { // Set framebuffers. @@ -481,7 +481,7 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende // In stereo p_render_data->z_near and p_render_data->z_far can be offset for our combined frustum. float z_near = p_render_data->scene_data->view_projection[i].get_z_near(); float z_far = p_render_data->scene_data->view_projection[i].get_z_far(); - bokeh_dof->bokeh_dof_raster(buffers, p_render_data->camera_attributes, z_near, z_far, p_render_data->scene_data->cam_orthogonal); + bokeh_dof->bokeh_dof_raster(buffers, p_render_data->camera_attributes, z_near, z_far, p_render_data->scene_data->cam_is_orthogonal); } } RD::get_singleton()->draw_command_end_label(); @@ -860,7 +860,7 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(const RenderDataRD *p_ren RID base = light_storage->light_instance_get_base_light(light); if (light_storage->light_get_type(base) == RS::LIGHT_DIRECTIONAL) { - debug_effects->draw_shadow_frustum(light, p_render_data->scene_data->cam_projection, p_render_data->scene_data->cam_transform, dest_fb, Rect2(Size2(), size)); + debug_effects->draw_shadow_frustum(light, p_render_data->scene_data->cam_frustum, p_render_data->scene_data->cam_transform, dest_fb, Rect2(Size2(), size)); } } } @@ -1129,8 +1129,9 @@ void RendererSceneRenderRD::render_scene(const Ref &p_render // Our first camera is used by default scene_data.cam_transform = p_camera_data->main_transform; scene_data.cam_projection = p_camera_data->main_projection; - scene_data.cam_orthogonal = p_camera_data->is_orthogonal; - scene_data.cam_frustum = p_camera_data->is_frustum; + scene_data.cam_frustum = p_camera_data->main_frustum; + scene_data.cam_is_orthogonal = p_camera_data->is_orthogonal; + scene_data.cam_is_frustum = p_camera_data->is_frustum; scene_data.camera_visible_layers = p_camera_data->visible_layers; scene_data.taa_jitter = p_camera_data->taa_jitter; scene_data.taa_frame_count = p_camera_data->taa_frame_count; @@ -1151,11 +1152,11 @@ void RendererSceneRenderRD::render_scene(const Ref &p_render scene_data.prev_view_projection[v] = p_prev_camera_data->view_projection[v]; } - scene_data.z_near = p_camera_data->main_projection.get_z_near(); - scene_data.z_far = p_camera_data->main_projection.get_z_far(); + scene_data.z_near = p_camera_data->main_frustum.get_z_near(); + scene_data.z_far = p_camera_data->main_frustum.get_z_far(); // this should be the same for all cameras.. - const float lod_distance_multiplier = p_camera_data->main_projection.get_lod_multiplier(); + const float lod_distance_multiplier = p_camera_data->main_frustum.get_lod_multiplier(); // Also, take into account resolution scaling for the multiplier, since we have more leeway with quality // degradation visibility. Conversely, allow upwards scaling, too, for increased mesh detail at high res. @@ -1245,8 +1246,8 @@ void RendererSceneRenderRD::render_scene(const Ref &p_render _render_scene(&render_data, clear_color); } -void RendererSceneRenderRD::render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) { - _render_material(p_cam_transform, p_cam_projection, p_cam_orthogonal, p_instances, p_framebuffer, p_region, 1.0); +void RendererSceneRenderRD::render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, const Frustum &p_cam_frustum, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) { + _render_material(p_cam_transform, p_cam_projection, p_cam_frustum, p_cam_orthogonal, p_instances, p_framebuffer, p_region, 1.0); } void RendererSceneRenderRD::render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray &p_instances) { diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h index 631ad2124782..742b4c261e7c 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h @@ -87,7 +87,7 @@ class RendererSceneRenderRD : public RendererSceneRender { virtual void _render_scene(RenderDataRD *p_render_data, const Color &p_default_color) = 0; virtual void _render_buffers_debug_draw(const RenderDataRD *p_render_data); - virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) = 0; + virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, const Frustum &p_cam_frustum, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) = 0; virtual void _render_uv2(const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) = 0; virtual void _render_sdfgi(Ref p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) = 0; virtual void _render_particle_collider_heightfield(RID p_fb, const Transform3D &p_cam_transform, const Projection &p_cam_projection, const PagedArray &p_instances) = 0; @@ -242,7 +242,7 @@ class RendererSceneRenderRD : public RendererSceneRender { virtual void render_scene(const Ref &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray &p_instances, const PagedArray &p_lights, const PagedArray &p_reflection_probes, const PagedArray &p_voxel_gi_instances, const PagedArray &p_decals, const PagedArray &p_lightmaps, const PagedArray &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_compositor, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RenderingMethod::RenderInfo *r_render_info = nullptr) override; - virtual void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) override; + virtual void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, const Frustum &p_cam_frustum, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) override; virtual void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray &p_instances) override; diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h index 59671c3a1395..ff9683eb5314 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h @@ -47,11 +47,12 @@ class RenderSceneDataRD : public RenderSceneData { Transform3D cam_transform; Projection cam_projection; + Frustum cam_frustum; Vector2 taa_jitter; float taa_frame_count = 0.0f; uint32_t camera_visible_layers; - bool cam_orthogonal = false; - bool cam_frustum = false; + bool cam_is_orthogonal = false; + bool cam_is_frustum = false; bool flip_y = false; // For billboards to cast correct shadows. diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index 2420a242612d..d8447ba30a2b 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -2249,7 +2249,7 @@ void RendererSceneCull::_update_instance_lightmap_captures(Instance *p_instance) geom->geometry_instance->set_lightmap_capture(p_instance->lightmap_sh.ptr()); } -void RendererSceneCull::_light_instance_setup_directional_shadow(int p_shadow_index, Instance *p_instance, const Transform3D p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect) { +void RendererSceneCull::_light_instance_setup_directional_shadow(int p_shadow_index, Instance *p_instance, const Transform3D p_cam_transform, const Frustum &p_cam_frustum, bool p_cam_orthogonal, bool p_cam_vaspect) { // For later tight culling, the light culler needs to know the details of the directional light. light_culler->prepare_directional_light(p_instance, p_shadow_index); @@ -2258,13 +2258,13 @@ void RendererSceneCull::_light_instance_setup_directional_shadow(int p_shadow_in Transform3D light_transform = p_instance->transform; light_transform.orthonormalize(); //scale does not count on lights - real_t max_distance = p_cam_projection.get_z_far(); + real_t max_distance = p_cam_frustum.get_z_far(); real_t shadow_max = RSG::light_storage->light_get_param(p_instance->base, RS::LIGHT_PARAM_SHADOW_MAX_DISTANCE); if (shadow_max > 0 && !p_cam_orthogonal) { //its impractical (and leads to unwanted behaviors) to set max distance in orthogonal camera max_distance = MIN(shadow_max, max_distance); } - max_distance = MAX(max_distance, p_cam_projection.get_z_near() + 0.001); - real_t min_distance = MIN(p_cam_projection.get_z_near(), max_distance); + max_distance = MAX(max_distance, p_cam_frustum.get_z_near() + 0.001); + real_t min_distance = MIN(p_cam_frustum.get_z_near(), max_distance); real_t pancake_size = RSG::light_storage->light_get_param(p_instance->base, RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE); @@ -2304,24 +2304,24 @@ void RendererSceneCull::_light_instance_setup_directional_shadow(int p_shadow_in for (int i = 0; i < splits; i++) { RENDER_TIMESTAMP("Cull DirectionalLight3D, Split " + itos(i)); - // setup a camera matrix for that range! - Projection camera_matrix; + // setup a camera frustum for that range! + Frustum camera_frustum; - real_t aspect = p_cam_projection.get_aspect(); + real_t aspect = p_cam_frustum.get_aspect(); if (p_cam_orthogonal) { - Vector2 vp_he = p_cam_projection.get_viewport_half_extents(); + Vector2 vp_he = p_cam_frustum.get_viewport_half_extents(); - camera_matrix.set_orthogonal(vp_he.y * 2.0, aspect, distances[(i == 0 || !overlap) ? i : i - 1], distances[i + 1], false); + camera_frustum.set_orthogonal(vp_he.y * 2.0, aspect, distances[(i == 0 || !overlap) ? i : i - 1], distances[i + 1], false); } else { - real_t fov = p_cam_projection.get_fov(); //this is actually yfov, because set aspect tries to keep it - camera_matrix.set_perspective(fov, aspect, distances[(i == 0 || !overlap) ? i : i - 1], distances[i + 1], true); + real_t fov = p_cam_frustum.get_fov(); //this is actually yfov, because set aspect tries to keep it + camera_frustum.set_perspective(fov, aspect, distances[(i == 0 || !overlap) ? i : i - 1], distances[i + 1], true); } //obtain the frustum endpoints Vector3 endpoints[8]; // frustum plane endpoints - bool res = camera_matrix.get_endpoints(p_cam_transform, endpoints); + bool res = camera_frustum.get_endpoints(p_cam_transform, endpoints); ERR_CONTINUE(!res); // obtain the light frustum ranges (given endpoints) @@ -2457,7 +2457,7 @@ void RendererSceneCull::_light_instance_setup_directional_shadow(int p_shadow_in ortho_transform.basis = transform.basis; ortho_transform.origin = x_vec * (x_min_cam + half_x) + y_vec * (y_min_cam + half_y) + z_vec * z_max; - cull.shadows[p_shadow_index].cascades[i].frustum = Frustum(light_frustum_planes); + cull.shadows[p_shadow_index].cascades[i].frustum = light_frustum_planes; cull.shadows[p_shadow_index].cascades[i].projection = ortho_camera; cull.shadows[p_shadow_index].cascades[i].transform = ortho_transform; cull.shadows[p_shadow_index].cascades[i].zfar = z_max - z_min_cam; @@ -2470,7 +2470,7 @@ void RendererSceneCull::_light_instance_setup_directional_shadow(int p_shadow_in } } -bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, const Transform3D p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_shadow_atlas, Scenario *p_scenario, float p_screen_mesh_lod_threshold, uint32_t p_visible_layers) { +bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, const Transform3D p_cam_transform, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_shadow_atlas, Scenario *p_scenario, float p_screen_mesh_lod_threshold, uint32_t p_visible_layers) { InstanceLightData *light = static_cast(p_instance->base_data); Transform3D light_transform = p_instance->transform; @@ -2559,7 +2559,9 @@ bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, cons real_t radius = RSG::light_storage->light_get_param(p_instance->base, RS::LIGHT_PARAM_RANGE); Projection cm; + Frustum fm; cm.set_perspective(90, 1, radius * 0.005f, radius); + fm.set_perspective(90, 1, radius * 0.005f, radius); for (int i = 0; i < 6; i++) { RENDER_TIMESTAMP("Cull OmniLight3D Shadow Cube, Side " + itos(i)); @@ -2584,7 +2586,7 @@ bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, cons Transform3D xform = light_transform * Transform3D().looking_at(view_normals[i], view_up[i]); - Vector planes = cm.get_projection_planes(xform); + Vector planes = fm.get_projection_planes(xform); instance_shadow_cull_result.clear(); @@ -2649,9 +2651,11 @@ bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, cons real_t angle = RSG::light_storage->light_get_param(p_instance->base, RS::LIGHT_PARAM_SPOT_ANGLE); Projection cm; + Frustum fm; cm.set_perspective(angle * 2.0, 1.0, 0.005f * radius, radius); + fm.set_perspective(angle * 2.0, 1.0, 0.005f * radius, radius); - Vector planes = cm.get_projection_planes(light_transform); + Vector planes = fm.get_projection_planes(light_transform); instance_shadow_cull_result.clear(); @@ -2736,6 +2740,7 @@ void RendererSceneCull::render_camera(const Ref &p_render_bu // Normal camera Transform3D transform = camera->transform; Projection projection; + Frustum frustum; bool vaspect = camera->vaspect; bool is_orthogonal = false; bool is_frustum = false; @@ -2748,6 +2753,12 @@ void RendererSceneCull::render_camera(const Ref &p_render_bu camera->znear, camera->zfar, camera->vaspect); + frustum.set_orthogonal( + camera->size, + p_viewport_size.width / (float)p_viewport_size.height, + camera->znear, + camera->zfar, + camera->vaspect); is_orthogonal = true; } break; case Camera::PERSPECTIVE: { @@ -2757,6 +2768,12 @@ void RendererSceneCull::render_camera(const Ref &p_render_bu camera->znear, camera->zfar, camera->vaspect); + frustum.set_perspective( + camera->fov, + p_viewport_size.width / (float)p_viewport_size.height, + camera->znear, + camera->zfar, + camera->vaspect); } break; case Camera::FRUSTUM: { @@ -2767,11 +2784,18 @@ void RendererSceneCull::render_camera(const Ref &p_render_bu camera->znear, camera->zfar, camera->vaspect); + frustum.set_frustum( + camera->size, + p_viewport_size.width / (float)p_viewport_size.height, + camera->offset, + camera->znear, + camera->zfar, + camera->vaspect); is_frustum = true; } break; } - camera_data.set_camera(transform, projection, is_orthogonal, is_frustum, vaspect, jitter, taa_frame_count, camera->visible_layers); + camera_data.set_camera(transform, projection, frustum, is_orthogonal, is_frustum, vaspect, jitter, taa_frame_count, camera->visible_layers); } else { // Setup our camera for our XR interface. // We can support multiple views here each with their own camera @@ -2793,7 +2817,7 @@ void RendererSceneCull::render_camera(const Ref &p_render_bu } if (view_count == 1) { - camera_data.set_camera(transforms[0], projections[0], false, false, camera->vaspect, jitter, p_jitter_phase_count, camera->visible_layers); + camera_data.set_camera(transforms[0], projections[0], projections[0].get_projection_planes(Transform3D()), false, false, camera->vaspect, jitter, p_jitter_phase_count, camera->visible_layers); } else if (view_count == 2) { camera_data.set_multiview_camera(view_count, transforms, projections, false, false, camera->vaspect); } else { @@ -2806,7 +2830,7 @@ void RendererSceneCull::render_camera(const Ref &p_render_bu RENDER_TIMESTAMP("Update Occlusion Buffer") // For now just cull on the first camera - RendererSceneOcclusionCull::get_singleton()->buffer_update(p_viewport, camera_data.main_transform, camera_data.main_projection, camera_data.is_orthogonal); + RendererSceneOcclusionCull::get_singleton()->buffer_update(p_viewport, camera_data.main_transform, camera_data.main_frustum, camera_data.is_orthogonal); _render_scene(&camera_data, p_render_buffers, environment, camera->attributes, compositor, camera->visible_layers, p_scenario, p_viewport, p_shadow_atlas, RID(), -1, p_screen_mesh_lod_threshold, true, r_render_info); #endif @@ -3201,7 +3225,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c Instance *render_reflection_probe = instance_owner.get_or_null(p_reflection_probe); //if null, not rendering to it // Prepare the light - camera volume culling system. - light_culler->prepare_camera(p_camera_data->main_transform, p_camera_data->main_projection); + light_culler->prepare_camera(p_camera_data->main_transform, p_camera_data->main_frustum); Scenario *scenario = scenario_owner.get_or_null(p_scenario); Vector3 camera_position = p_camera_data->main_transform.origin; @@ -3252,8 +3276,8 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c /* STEP 2 - CULL */ - Vector planes = p_camera_data->main_projection.get_projection_planes(p_camera_data->main_transform); - cull.frustum = Frustum(planes); + Vector planes = p_camera_data->main_frustum.get_projection_planes(p_camera_data->main_transform); + cull.frustum = FrustumSign(planes); Vector directional_lights; // directional lights @@ -3287,7 +3311,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c RSG::light_storage->set_directional_shadow_count(lights_with_shadow.size()); for (int i = 0; i < lights_with_shadow.size(); i++) { - _light_instance_setup_directional_shadow(i, lights_with_shadow[i], p_camera_data->main_transform, p_camera_data->main_projection, p_camera_data->is_orthogonal, p_camera_data->vaspect); + _light_instance_setup_directional_shadow(i, lights_with_shadow[i], p_camera_data->main_transform, p_camera_data->main_frustum, p_camera_data->is_orthogonal, p_camera_data->vaspect); } } @@ -3415,11 +3439,11 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c { //compute coverage Transform3D cam_xf = p_camera_data->main_transform; - float zn = p_camera_data->main_projection.get_z_near(); + float zn = p_camera_data->main_frustum.get_z_near(); Plane p(-cam_xf.basis.get_column(2), cam_xf.origin + cam_xf.basis.get_column(2) * -zn); //camera near plane // near plane half width and height - Vector2 vp_half_extents = p_camera_data->main_projection.get_viewport_half_extents(); + Vector2 vp_half_extents = p_camera_data->main_frustum.get_viewport_half_extents(); switch (RSG::light_storage->light_get_type(ins->base)) { case RS::LIGHT_OMNI: { @@ -3512,7 +3536,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c if (redraw && max_shadows_used < MAX_UPDATE_SHADOWS) { //must redraw! RENDER_TIMESTAMP("> Render Light3D " + itos(i)); - if (_light_instance_update_shadow(ins, p_camera_data->main_transform, p_camera_data->main_projection, p_camera_data->is_orthogonal, p_camera_data->vaspect, p_shadow_atlas, scenario, p_screen_mesh_lod_threshold, p_visible_layers)) { + if (_light_instance_update_shadow(ins, p_camera_data->main_transform, p_camera_data->is_orthogonal, p_camera_data->vaspect, p_shadow_atlas, scenario, p_screen_mesh_lod_threshold, p_visible_layers)) { light->make_shadow_dirty(); } RENDER_TIMESTAMP("< Render Light3D " + itos(i)); @@ -3648,7 +3672,7 @@ void RendererSceneCull::render_empty_scene(const Ref &p_rend RENDER_TIMESTAMP("Render Empty 3D Scene"); RendererSceneRender::CameraData camera_data; - camera_data.set_camera(Transform3D(), Projection(), true, false, false); + camera_data.set_camera(Transform3D(), Projection(), Frustum(), true, false, false); scene_render->render_scene(p_render_buffers, &camera_data, &camera_data, PagedArray(), PagedArray(), PagedArray(), PagedArray(), PagedArray(), PagedArray(), PagedArray(), environment, RID(), compositor, p_shadow_atlas, RID(), scenario->reflection_atlas, RID(), 0, 0, nullptr, 0, nullptr, 0, nullptr); #endif @@ -3703,7 +3727,9 @@ bool RendererSceneCull::_render_reflection_probe_step(Instance *p_instance, int //render cubemap side Projection cm; + Frustum fm; cm.set_perspective(90, 1, 0.01, max_distance); + fm.set_perspective(90, 1, 0.01, max_distance); Transform3D local_view; local_view.set_look_at(origin_offset, origin_offset + view_normals[p_step], view_up[p_step]); @@ -3726,7 +3752,7 @@ bool RendererSceneCull::_render_reflection_probe_step(Instance *p_instance, int RENDER_TIMESTAMP("Render ReflectionProbe, Step " + itos(p_step)); RendererSceneRender::CameraData camera_data; - camera_data.set_camera(xform, cm, false, false, false); + camera_data.set_camera(xform, cm, fm, false, false, false); Ref render_buffers = RSG::light_storage->reflection_probe_atlas_get_render_buffers(scenario->reflection_atlas); _render_scene(&camera_data, render_buffers, environment, RID(), RID(), RSG::light_storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, RID(), shadow_atlas, reflection_probe->instance, p_step, mesh_lod_threshold, use_shadows); diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h index 8161f50a3512..8c4385e1c63a 100644 --- a/servers/rendering/renderer_scene_cull.h +++ b/servers/rendering/renderer_scene_cull.h @@ -32,6 +32,7 @@ #define RENDERER_SCENE_CULL_H #include "core/math/dynamic_bvh.h" +#include "core/math/frustum.h" #include "core/math/transform_interpolator.h" #include "core/templates/bin_sorted_array.h" #include "core/templates/local_vector.h" @@ -158,15 +159,15 @@ class RendererSceneCull : public RenderingMethod { uint32_t signs[3]; }; - struct Frustum { + struct FrustumSign { Vector planes; Vector plane_signs; const Plane *planes_ptr; const PlaneSign *plane_signs_ptr; uint32_t plane_count; - _ALWAYS_INLINE_ Frustum() {} - _ALWAYS_INLINE_ Frustum(const Frustum &p_frustum) { + _ALWAYS_INLINE_ FrustumSign() {} + _ALWAYS_INLINE_ FrustumSign(const FrustumSign &p_frustum) { planes = p_frustum.planes; plane_signs = p_frustum.plane_signs; @@ -174,7 +175,7 @@ class RendererSceneCull : public RenderingMethod { plane_signs_ptr = plane_signs.ptr(); plane_count = p_frustum.plane_count; } - _ALWAYS_INLINE_ void operator=(const Frustum &p_frustum) { + _ALWAYS_INLINE_ void operator=(const FrustumSign &p_frustum) { planes = p_frustum.planes; plane_signs = p_frustum.plane_signs; @@ -182,7 +183,7 @@ class RendererSceneCull : public RenderingMethod { plane_signs_ptr = plane_signs.ptr(); plane_count = p_frustum.plane_count; } - _ALWAYS_INLINE_ Frustum(const Vector &p_planes) { + _ALWAYS_INLINE_ FrustumSign(const Vector &p_planes) { planes = p_planes; planes_ptr = planes.ptrw(); plane_count = planes.size(); @@ -193,6 +194,17 @@ class RendererSceneCull : public RenderingMethod { plane_signs_ptr = plane_signs.ptr(); } + _ALWAYS_INLINE_ FrustumSign(const Frustum &p_frustum) { + plane_count = 6; + for (int i = 0; i < 6; i++) { + planes.push_back(p_frustum[i]); + PlaneSign ps(p_frustum[i]); + plane_signs.push_back(ps); + } + + planes_ptr = planes.ptrw(); + plane_signs_ptr = plane_signs.ptr(); + } }; struct InstanceBounds { @@ -211,7 +223,7 @@ class RendererSceneCull : public RenderingMethod { bounds[4] = p_aabb.position.y + p_aabb.size.y; bounds[5] = p_aabb.position.z + p_aabb.size.z; } - _ALWAYS_INLINE_ bool in_frustum(const Frustum &p_frustum) const { + _ALWAYS_INLINE_ bool in_frustum(const FrustumSign &p_frustum) const { // This is not a full SAT check and the possibility of false positives exist, // but the tradeoff vs performance is still very good. @@ -1111,9 +1123,9 @@ class RendererSceneCull : public RenderingMethod { _FORCE_INLINE_ void _update_instance_lightmap_captures(Instance *p_instance); void _unpair_instance(Instance *p_instance); - void _light_instance_setup_directional_shadow(int p_shadow_index, Instance *p_instance, const Transform3D p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect); + void _light_instance_setup_directional_shadow(int p_shadow_index, Instance *p_instance, const Transform3D p_cam_transform, const Frustum &p_cam_frustum, bool p_cam_orthogonal, bool p_cam_vaspect); - _FORCE_INLINE_ bool _light_instance_update_shadow(Instance *p_instance, const Transform3D p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_shadow_atlas, Scenario *p_scenario, float p_scren_mesh_lod_threshold, uint32_t p_visible_layers = 0xFFFFFF); + _FORCE_INLINE_ bool _light_instance_update_shadow(Instance *p_instance, const Transform3D p_cam_transform, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_shadow_atlas, Scenario *p_scenario, float p_scren_mesh_lod_threshold, uint32_t p_visible_layers = 0xFFFFFF); RID _render_get_environment(RID p_camera, RID p_scenario); RID _render_get_compositor(RID p_camera, RID p_scenario); @@ -1123,7 +1135,7 @@ class RendererSceneCull : public RenderingMethod { RID light_instance; uint32_t caster_mask; struct Cascade { - Frustum frustum; + FrustumSign frustum; Projection projection; Transform3D transform; @@ -1154,7 +1166,7 @@ class RendererSceneCull : public RenderingMethod { SpinLock lock; - Frustum frustum; + FrustumSign frustum; } cull; struct VisibilityCullData { diff --git a/servers/rendering/renderer_scene_occlusion_cull.h b/servers/rendering/renderer_scene_occlusion_cull.h index 14b97918ec3f..af0dee25a918 100644 --- a/servers/rendering/renderer_scene_occlusion_cull.h +++ b/servers/rendering/renderer_scene_occlusion_cull.h @@ -31,6 +31,7 @@ #ifndef RENDERER_SCENE_OCCLUSION_CULL_H #define RENDERER_SCENE_OCCLUSION_CULL_H +#include "core/math/frustum.h" #include "core/math/projection.h" #include "core/templates/local_vector.h" #include "servers/rendering_server.h" @@ -223,7 +224,7 @@ class RendererSceneOcclusionCull { } virtual void buffer_set_scenario(RID p_buffer, RID p_scenario) { _print_warning(); } virtual void buffer_set_size(RID p_buffer, const Vector2i &p_size) { _print_warning(); } - virtual void buffer_update(RID p_buffer, const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal) {} + virtual void buffer_update(RID p_buffer, const Transform3D &p_cam_transform, const Frustum &p_cam_frustum, bool p_cam_orthogonal) {} virtual RID buffer_get_debug_texture(RID p_buffer) { _print_warning(); diff --git a/servers/rendering/renderer_scene_render.cpp b/servers/rendering/renderer_scene_render.cpp index 07259f73d201..c261f9385fc0 100644 --- a/servers/rendering/renderer_scene_render.cpp +++ b/servers/rendering/renderer_scene_render.cpp @@ -33,7 +33,7 @@ ///////////////////////////////////////////////////////////////////////////// // CameraData -void RendererSceneRender::CameraData::set_camera(const Transform3D p_transform, const Projection p_projection, bool p_is_orthogonal, bool p_is_frustum, bool p_vaspect, const Vector2 &p_taa_jitter, float p_taa_frame_count, const uint32_t p_visible_layers) { +void RendererSceneRender::CameraData::set_camera(const Transform3D p_transform, const Projection p_projection, const Frustum p_frustum, bool p_is_orthogonal, bool p_is_frustum, bool p_vaspect, const Vector2 &p_taa_jitter, float p_taa_frame_count, const uint32_t p_visible_layers) { view_count = 1; is_orthogonal = p_is_orthogonal; is_frustum = p_is_frustum; @@ -41,6 +41,7 @@ void RendererSceneRender::CameraData::set_camera(const Transform3D p_transform, main_transform = p_transform; main_projection = p_projection; + main_frustum = p_frustum; visible_layers = p_visible_layers; view_offset[0] = Transform3D(); @@ -180,6 +181,7 @@ void RendererSceneRender::CameraData::set_multiview_camera(uint32_t p_view_count // 16. Use this to build the combined camera matrix. main_projection.set_frustum(local_min_vec.x, local_max_vec.x, local_min_vec.y, local_max_vec.y, z_near, z_far); + main_frustum.set_frustum(local_min_vec.x, local_max_vec.x, local_min_vec.y, local_max_vec.y, z_near, z_far); ///////////////////////////////////////////////////////////////////////////// // 3. Copy our view data diff --git a/servers/rendering/renderer_scene_render.h b/servers/rendering/renderer_scene_render.h index 99418e0411a9..25e7b4503293 100644 --- a/servers/rendering/renderer_scene_render.h +++ b/servers/rendering/renderer_scene_render.h @@ -31,6 +31,7 @@ #ifndef RENDERER_SCENE_RENDER_H #define RENDERER_SCENE_RENDER_H +#include "core/math/frustum.h" #include "core/math/projection.h" #include "core/templates/paged_array.h" #include "servers/rendering/renderer_geometry_instance.h" @@ -307,19 +308,20 @@ class RendererSceneRender { // Main/center projection Transform3D main_transform; Projection main_projection; + Frustum main_frustum; Transform3D view_offset[RendererSceneRender::MAX_RENDER_VIEWS]; Projection view_projection[RendererSceneRender::MAX_RENDER_VIEWS]; Vector2 taa_jitter; float taa_frame_count = 0.0f; - void set_camera(const Transform3D p_transform, const Projection p_projection, bool p_is_orthogonal, bool p_is_frustum, bool p_vaspect, const Vector2 &p_taa_jitter = Vector2(), float p_taa_frame_count = 0.0f, uint32_t p_visible_layers = 0xFFFFFFFF); + void set_camera(const Transform3D p_transform, const Projection p_projection, const Frustum p_frustum, bool p_is_orthogonal, bool p_is_frustum, bool p_vaspect, const Vector2 &p_taa_jitter = Vector2(), float p_taa_frame_count = 0.0f, uint32_t p_visible_layers = 0xFFFFFFFF); void set_multiview_camera(uint32_t p_view_count, const Transform3D *p_transforms, const Projection *p_projections, bool p_is_orthogonal, bool p_is_frustum, bool p_vaspect); }; virtual void render_scene(const Ref &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray &p_instances, const PagedArray &p_lights, const PagedArray &p_reflection_probes, const PagedArray &p_voxel_gi_instances, const PagedArray &p_decals, const PagedArray &p_lightmaps, const PagedArray &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_compositor, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RenderingMethod::RenderInfo *r_render_info = nullptr) = 0; - virtual void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) = 0; + virtual void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, const Frustum &p_cam_frustum, bool p_cam_orthogonal, const PagedArray &p_instances, RID p_framebuffer, const Rect2i &p_region) = 0; virtual void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray &p_instances) = 0; virtual void set_scene_pass(uint64_t p_pass) = 0; diff --git a/servers/rendering/rendering_light_culler.cpp b/servers/rendering/rendering_light_culler.cpp index 8dc2d85bdf89..7ffd66081067 100644 --- a/servers/rendering/rendering_light_culler.cpp +++ b/servers/rendering/rendering_light_culler.cpp @@ -470,7 +470,7 @@ bool RenderingLightCuller::_add_light_camera_planes(LightCullPlanes &r_cull_plan return true; } -bool RenderingLightCuller::prepare_camera(const Transform3D &p_cam_transform, const Projection &p_cam_matrix) { +bool RenderingLightCuller::prepare_camera(const Transform3D &p_cam_transform, const Frustum &p_cam_frustum) { data.debug_count++; if (data.debug_count >= 120) { data.debug_count = 0; @@ -495,7 +495,7 @@ bool RenderingLightCuller::prepare_camera(const Transform3D &p_cam_transform, co } // Get the camera frustum planes in world space. - data.frustum_planes = p_cam_matrix.get_projection_planes(p_cam_transform); + data.frustum_planes = p_cam_frustum.get_projection_planes(p_cam_transform); DEV_CHECK_ONCE(data.frustum_planes.size() == 6); data.regular_cull_planes.num_cull_planes = 0; diff --git a/servers/rendering/rendering_light_culler.h b/servers/rendering/rendering_light_culler.h index b0437d231027..cf4eb047ff40 100644 --- a/servers/rendering/rendering_light_culler.h +++ b/servers/rendering/rendering_light_culler.h @@ -130,7 +130,7 @@ class RenderingLightCuller { public: // Before each pass with a different camera, you must call this so the culler can pre-create // the camera frustum planes and corner points in world space which are used for the culling. - bool prepare_camera(const Transform3D &p_cam_transform, const Projection &p_cam_matrix); + bool prepare_camera(const Transform3D &p_cam_transform, const Frustum &p_cam_frustum); // REGULAR LIGHTS (SPOT, OMNI). // These are prepared then used for culling one by one, single threaded. diff --git a/tests/core/math/test_frustum.h b/tests/core/math/test_frustum.h new file mode 100644 index 000000000000..644796fb48f3 --- /dev/null +++ b/tests/core/math/test_frustum.h @@ -0,0 +1,156 @@ +/**************************************************************************/ +/* test_frustum.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef TEST_FRUSTUM_H +#define TEST_FRUSTUM_H + +#include "core/math/frustum.h" + +#include "tests/test_macros.h" + +namespace TestFrustum { + +TEST_CASE("[Frustum] Construction from planes") { + constexpr real_t sqrt2half = 1.41421356237309504880 / 2; + + Vector planes; + planes.push_back(Plane(0, 0, 1, -0.02)); // NEAR with znear = 0.02 + planes.push_back(Plane(0, 0, -1, 50)); // FAR with zfar = 50 + planes.push_back(Plane(-sqrt2half, 0, sqrt2half, 0)); // LEFT with fov 90° and aspect 1.0 + planes.push_back(Plane(0, sqrt2half, sqrt2half, 0)); // TOP with fov 90° and aspect 1.0 + planes.push_back(Plane(sqrt2half, 0, sqrt2half, 0)); // RIGHT with fov 90° and aspect 1.0 + planes.push_back(Plane(0, -sqrt2half, sqrt2half, 0)); // BOTTOM with fov 90° and aspect 1.0 + + Frustum frustum(planes); + + CHECK(frustum.planes[0] == planes[0]); + CHECK(frustum.planes[1] == planes[1]); + CHECK(frustum.planes[2] == planes[2]); + CHECK(frustum.planes[3] == planes[3]); + CHECK(frustum.planes[4] == planes[4]); + CHECK(frustum.planes[5] == planes[5]); +} + +TEST_CASE("[Frustum] Orthographic tests") { + Frustum frustum_perspective; + Frustum frustum_orthographic; + + frustum_perspective.set_perspective(50, 0.8, 0.2, 10.0); + frustum_orthographic.set_orthogonal(-4, 5, -6, 7, 0.2, 9); + + CHECK(!frustum_perspective.is_orthogonal()); + CHECK(frustum_orthographic.is_orthogonal()); +} + +TEST_CASE("[Frustum] Orthographic construction") { + Frustum frustum; + Projection ref; + + frustum.set_orthogonal(-4, 5, -6, 7, 0.2, 9); + ref.set_orthogonal(-4, 5, -6, 7, 0.2, 9); + + Vector truth = ref.get_projection_planes(Transform3D()); + + CHECK(frustum.planes[0].is_equal_approx(truth[0].normalized())); + CHECK(frustum.planes[1].is_equal_approx(truth[1].normalized())); + CHECK(frustum.planes[2].is_equal_approx(truth[2].normalized())); + CHECK(frustum.planes[3].is_equal_approx(truth[3].normalized())); + CHECK(frustum.planes[4].is_equal_approx(truth[4].normalized())); + CHECK(frustum.planes[5].is_equal_approx(truth[5].normalized())); +} + +TEST_CASE("[Frustum] Perspective construction") { + Frustum frustum; + Projection ref; + + frustum.set_perspective(50, 0.8, 0.2, 10.0); + ref.set_perspective(50, 0.8, 0.2, 10.0); + + Vector truth = ref.get_projection_planes(Transform3D()); + + CHECK(frustum.planes[0].is_equal_approx(truth[0].normalized())); + CHECK(frustum.planes[1].is_equal_approx(truth[1].normalized())); + CHECK(frustum.planes[2].is_equal_approx(truth[2].normalized())); + CHECK(frustum.planes[3].is_equal_approx(truth[3].normalized())); + CHECK(frustum.planes[4].is_equal_approx(truth[4].normalized())); + CHECK(frustum.planes[5].is_equal_approx(truth[5].normalized())); +} + +TEST_CASE("[Frustum] Frustum construction") { + Frustum frustum; + Projection ref; + + frustum.set_frustum(-4, 5, -6, 7, 0.2, 9); + ref.set_frustum(-4, 5, -6, 7, 0.2, 9); + + Vector truth = ref.get_projection_planes(Transform3D()); + + CHECK(frustum.planes[0].is_equal_approx(truth[0].normalized())); + CHECK(frustum.planes[1].is_equal_approx(truth[1].normalized())); + CHECK(frustum.planes[2].is_equal_approx(truth[2].normalized())); + CHECK(frustum.planes[3].is_equal_approx(truth[3].normalized())); + CHECK(frustum.planes[4].is_equal_approx(truth[4].normalized())); + CHECK(frustum.planes[5].is_equal_approx(truth[5].normalized())); +} + +TEST_CASE("[Frustum] get_fovy()") { + real_t fov = Frustum::get_fovy(90, 0.5); + CHECK(fov == doctest::Approx(53.1301)); +} + +TEST_CASE("[Frustum] Perspective camera values retrieval") { + Frustum frustum; + + frustum.set_perspective(30, 0.8, 0.2, 10.0); + + CHECK(frustum.get_z_near() == doctest::Approx(0.2)); + CHECK(frustum.get_z_far() == doctest::Approx(10)); + CHECK(Frustum::get_fovy(frustum.get_fov(), 1.0/0.8) == doctest::Approx(30)); + CHECK(frustum.get_aspect() == doctest::Approx(0.8)); +} + +TEST_CASE("[Frustum] Half extents") { + constexpr real_t sqrt2 = 1.41421356237309504880; + + Frustum frustum; + Vector2 near; + + frustum.set_perspective(90, 0.8, 0.1, 40, false); + near = frustum.get_viewport_half_extents(); + CHECK(near.is_equal_approx(Vector2(0.08, 0.1))); + + frustum.set_perspective(45, 0.5, 0.01, 50, true); + near = frustum.get_viewport_half_extents(); + CHECK(near.is_equal_approx(Vector2((sqrt2 - 1) * 0.01, (sqrt2 - 1) * 0.02))); +} + +} //namespace TestFrustum + +#endif // #define TEST_FRUSTUM_H \ No newline at end of file diff --git a/tests/core/math/test_projection.h b/tests/core/math/test_projection.h index 02bee245a7db..5871c8c5e685 100644 --- a/tests/core/math/test_projection.h +++ b/tests/core/math/test_projection.h @@ -265,7 +265,7 @@ TEST_CASE("[Projection] Jitter offset") { CHECK(proj[3] == offsetted[3]); } -TEST_CASE("[Projection] Adjust znear") { +TEST_CASE("[Projection] Adjust znear zfar") { Projection persp = Projection::create_perspective(90, 0.5, 1, 50, false); Projection adjusted = persp.perspective_znear_adjusted(2); @@ -274,12 +274,12 @@ TEST_CASE("[Projection] Adjust znear") { CHECK(adjusted[2].is_equal_approx(Vector4(persp[2][0], persp[2][1], -1.083333, persp[2][3]))); CHECK(adjusted[3].is_equal_approx(Vector4(persp[3][0], persp[3][1], -4.166666, persp[3][3]))); - persp.adjust_perspective_znear(2); + persp.adjust_perspective_znear_zfar(2, 60); CHECK(persp[0] == adjusted[0]); CHECK(persp[1] == adjusted[1]); - CHECK(persp[2] == adjusted[2]); - CHECK(persp[3] == adjusted[3]); + CHECK(persp[2].is_equal_approx(Vector4(adjusted[2][0], adjusted[2][1], -1.068965, adjusted[2][3]))); + CHECK(persp[3].is_equal_approx(Vector4(adjusted[3][0], adjusted[3][1], -4.137931, adjusted[3][3]))); } TEST_CASE("[Projection] Set light bias") { @@ -292,6 +292,24 @@ TEST_CASE("[Projection] Set light bias") { CHECK(proj[3] == Vector4(0.5, 0.5, 0.5, 1)); } + +TEST_CASE("[Projection] Adjust fov") { + Projection persp = Projection::create_perspective(90, 0.5, 1, 50, false); + Projection adjusted = persp.perspective_fov_adjusted(45); + + CHECK(adjusted[0].is_equal_approx(Vector4(2.0 / 0.414214, persp[0][1], persp[0][2], persp[0][3]))); + CHECK(adjusted[1].is_equal_approx(Vector4(persp[1][0], 1.0 / 0.414214, persp[1][2], persp[1][3]))); + CHECK(adjusted[2] == persp[2]); + CHECK(adjusted[3] == persp[3]); + + persp.adjust_perspective_fov(45); + + CHECK(persp[0] == adjusted[0]); + CHECK(persp[1] == adjusted[1]); + CHECK(persp[2] == adjusted[2]); + CHECK(persp[3] == adjusted[3]); +} + TEST_CASE("[Projection] Depth correction") { Projection corrected = Projection::create_depth_correction(true); diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 67adad673f6d..eb2f6f2fc283 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -63,6 +63,7 @@ #include "tests/core/math/test_basis.h" #include "tests/core/math/test_color.h" #include "tests/core/math/test_expression.h" +#include "tests/core/math/test_frustum.h" #include "tests/core/math/test_geometry_2d.h" #include "tests/core/math/test_geometry_3d.h" #include "tests/core/math/test_math_funcs.h"