diff options
author | RĂ©mi Verschelde <remi@verschelde.fr> | 2021-04-27 19:07:12 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-27 19:07:12 +0200 |
commit | 95cfce661bbe9700209c09dfe297ab7ef5ebfe09 (patch) | |
tree | 9f3c8a98b619e2a0dd171d6cb6d8dc2e791baa14 /servers | |
parent | b999fbc4bd349cce153c2133dd0487694add1a05 (diff) | |
parent | 4d9d99bb827967e2bb931eeb8c3f0e079b39ae1a (diff) |
Merge pull request #48050 from JFonS/occlusion_culling
Diffstat (limited to 'servers')
-rw-r--r-- | servers/rendering/renderer_rd/renderer_scene_render_rd.cpp | 13 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/renderer_scene_render_rd.h | 4 | ||||
-rw-r--r-- | servers/rendering/renderer_scene.h | 8 | ||||
-rw-r--r-- | servers/rendering/renderer_scene_cull.cpp | 109 | ||||
-rw-r--r-- | servers/rendering/renderer_scene_cull.h | 28 | ||||
-rw-r--r-- | servers/rendering/renderer_scene_occlusion_cull.cpp | 192 | ||||
-rw-r--r-- | servers/rendering/renderer_scene_occlusion_cull.h | 201 | ||||
-rw-r--r-- | servers/rendering/renderer_scene_render.h | 2 | ||||
-rw-r--r-- | servers/rendering/renderer_viewport.cpp | 75 | ||||
-rw-r--r-- | servers/rendering/renderer_viewport.h | 16 | ||||
-rw-r--r-- | servers/rendering/rendering_server_default.h | 7 | ||||
-rw-r--r-- | servers/rendering_server.cpp | 21 | ||||
-rw-r--r-- | servers/rendering_server.h | 20 |
13 files changed, 666 insertions, 30 deletions
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index a98f67f163..a742c4cc28 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -1873,7 +1873,7 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(RID p_rende storage->render_target_disable_clear_request(rb->render_target); } -void RendererSceneRenderRD::_render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas) { +void RendererSceneRenderRD::_render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer) { EffectsRD *effects = storage->get_effects(); RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); @@ -1932,6 +1932,13 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(RID p_render_buffers, RID RID reflection_texture = rb->reflection_buffer; effects->copy_to_fb_rect(ambient_texture, storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, false, false, true, reflection_texture); } + + if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_OCCLUDERS) { + if (p_occlusion_buffer.is_valid()) { + Size2 rtsize = storage->render_target_get_size(rb->render_target); + effects->copy_to_fb_rect(storage->texture_get_rd_texture(p_occlusion_buffer), storage->render_target_get_rd_framebuffer(rb->render_target), Rect2i(Vector2(), rtsize), true, false); + } + } } void RendererSceneRenderRD::environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction) { @@ -3516,7 +3523,7 @@ void RendererSceneRenderRD::_pre_opaque_render(bool p_use_ssao, bool p_use_gi, R } } -void RendererSceneRenderRD::render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_gi_probes, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_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) { +void RendererSceneRenderRD::render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_gi_probes, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, 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_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) { // getting this here now so we can direct call a bunch of things more easily RenderBuffers *rb = nullptr; if (p_render_buffers.is_valid()) { @@ -3643,7 +3650,7 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const Transform & RENDER_TIMESTAMP("Tonemap"); _render_buffers_post_process_and_tonemap(p_render_buffers, p_environment, p_camera_effects, p_cam_projection); - _render_buffers_debug_draw(p_render_buffers, p_shadow_atlas); + _render_buffers_debug_draw(p_render_buffers, p_shadow_atlas, p_occluder_debug_tex); if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SDFGI && rb != nullptr && rb->sdfgi != nullptr) { rb->sdfgi->debug_draw(p_cam_projection, p_cam_transform, rb->width, rb->height, rb->render_target, rb->texture); } diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h index 4bf0818206..8c01b69b91 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h @@ -441,7 +441,7 @@ private: void _allocate_blur_textures(RenderBuffers *rb); void _allocate_luminance_textures(RenderBuffers *rb); - void _render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas); + void _render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer); void _render_buffers_post_process_and_tonemap(RID p_render_buffers, RID p_environment, RID p_camera_effects, const CameraMatrix &p_projection); /* Cluster */ @@ -1125,7 +1125,7 @@ public: float render_buffers_get_volumetric_fog_end(RID p_render_buffers); float render_buffers_get_volumetric_fog_detail_spread(RID p_render_buffers); - void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_gi_probes, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_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); + void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_gi_probes, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, 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_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); void render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region); diff --git a/servers/rendering/renderer_scene.h b/servers/rendering/renderer_scene.h index 551d4f4240..db1e3d1377 100644 --- a/servers/rendering/renderer_scene.h +++ b/servers/rendering/renderer_scene.h @@ -49,6 +49,10 @@ public: virtual void camera_set_use_vertical_aspect(RID p_camera, bool p_enable) = 0; virtual bool is_camera(RID p_camera) const = 0; + virtual RID occluder_allocate() = 0; + virtual void occluder_initialize(RID p_occluder) = 0; + virtual void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) = 0; + virtual RID scenario_allocate() = 0; virtual void scenario_initialize(RID p_rid) = 0; @@ -197,8 +201,8 @@ public: virtual void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) = 0; virtual void render_empty_scene(RID p_render_buffers, RID p_scenario, RID p_shadow_atlas) = 0; - virtual void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, Size2 p_viewport_size, float p_lod_threshold, RID p_shadow_atlas) = 0; - virtual void render_camera(RID p_render_buffers, Ref<XRInterface> &p_interface, XRInterface::Eyes p_eye, RID p_camera, RID p_scenario, Size2 p_viewport_size, float p_lod_threshold, RID p_shadow_atlas) = 0; + virtual void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_lod_threshold, RID p_shadow_atlas) = 0; + virtual void render_camera(RID p_render_buffers, Ref<XRInterface> &p_interface, XRInterface::Eyes p_eye, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_lod_threshold, RID p_shadow_atlas) = 0; virtual void update() = 0; virtual void render_probes() = 0; diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index c7caf71fcc..5417695ad3 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -109,6 +109,20 @@ bool RendererSceneCull::is_camera(RID p_camera) const { return camera_owner.owns(p_camera); } +/* OCCLUDER API */ + +RID RendererSceneCull::occluder_allocate() { + return RendererSceneOcclusionCull::get_singleton()->occluder_allocate(); +} + +void RendererSceneCull::occluder_initialize(RID p_rid) { + RendererSceneOcclusionCull::get_singleton()->occluder_initialize(p_rid); +} + +void RendererSceneCull::occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) { + RendererSceneOcclusionCull::get_singleton()->occluder_set_mesh(p_occluder, p_vertices, p_indices); +} + /* SCENARIO API */ void RendererSceneCull::_instance_pair(Instance *p_A, Instance *p_B) { @@ -310,6 +324,8 @@ void RendererSceneCull::scenario_initialize(RID p_rid) { scenario->instance_aabbs.set_page_pool(&instance_aabb_page_pool); scenario->instance_data.set_page_pool(&instance_data_page_pool); + RendererSceneOcclusionCull::get_singleton()->add_scenario(p_rid); + scenario_owner.initialize_rid(p_rid, scenario); } @@ -497,6 +513,11 @@ void RendererSceneCull::instance_set_base(RID p_instance, RID p_base) { scene_render->free(gi_probe->probe_instance); } break; + case RS::INSTANCE_OCCLUDER: { + if (scenario && instance->visible) { + RendererSceneOcclusionCull::get_singleton()->scenario_remove_instance(instance->scenario->self, p_instance); + } + } break; default: { } } @@ -514,6 +535,11 @@ void RendererSceneCull::instance_set_base(RID p_instance, RID p_base) { if (p_base.is_valid()) { instance->base_type = RSG::storage->get_base_type(p_base); + + if (instance->base_type == RS::INSTANCE_NONE && RendererSceneOcclusionCull::get_singleton()->is_occluder(p_base)) { + instance->base_type = RS::INSTANCE_OCCLUDER; + } + ERR_FAIL_COND(instance->base_type == RS::INSTANCE_NONE); switch (instance->base_type) { @@ -588,6 +614,11 @@ void RendererSceneCull::instance_set_base(RID p_instance, RID p_base) { gi_probe->probe_instance = scene_render->gi_probe_instance_create(p_base); } break; + case RS::INSTANCE_OCCLUDER: { + if (scenario) { + RendererSceneOcclusionCull::get_singleton()->scenario_set_instance(scenario->self, p_instance, p_base, instance->transform, instance->visible); + } + } break; default: { } } @@ -655,6 +686,11 @@ void RendererSceneCull::instance_set_scenario(RID p_instance, RID p_scenario) { gi_probe_update_list.remove(&gi_probe->update_element); } } break; + case RS::INSTANCE_OCCLUDER: { + if (instance->visible) { + RendererSceneOcclusionCull::get_singleton()->scenario_remove_instance(instance->scenario->self, p_instance); + } + } break; default: { } } @@ -684,6 +720,9 @@ void RendererSceneCull::instance_set_scenario(RID p_instance, RID p_scenario) { gi_probe_update_list.add(&gi_probe->update_element); } } break; + case RS::INSTANCE_OCCLUDER: { + RendererSceneOcclusionCull::get_singleton()->scenario_set_instance(scenario->self, p_instance, instance->base, instance->transform, instance->visible); + } break; default: { } } @@ -801,6 +840,12 @@ void RendererSceneCull::instance_set_visible(RID p_instance, bool p_visible) { InstanceParticlesCollisionData *collision = static_cast<InstanceParticlesCollisionData *>(instance->base_data); RSG::storage->particles_collision_instance_set_active(collision->instance, p_visible); } + + if (instance->base_type == RS::INSTANCE_OCCLUDER) { + if (instance->scenario) { + RendererSceneOcclusionCull::get_singleton()->scenario_set_instance(instance->scenario->self, p_instance, instance->base, instance->transform, p_visible); + } + } } inline bool is_geometry_instance(RenderingServer::InstanceType p_type) { @@ -998,6 +1043,18 @@ void RendererSceneCull::instance_geometry_set_flag(RID p_instance, RS::InstanceF } } break; + case RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING: { + instance->ignore_occlusion_culling = p_enabled; + + if (instance->scenario && instance->array_index >= 0) { + InstanceData &idata = instance->scenario->instance_data[instance->array_index]; + if (instance->ignore_occlusion_culling) { + idata.flags |= InstanceData::FLAG_IGNORE_OCCLUSION_CULLING; + } else { + idata.flags &= ~uint32_t(InstanceData::FLAG_IGNORE_OCCLUSION_CULLING); + } + } + } break; default: { } } @@ -1210,6 +1267,10 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { heightfield_particle_colliders_update_list.insert(p_instance); } RSG::storage->particles_collision_instance_set_transform(collision->instance, p_instance->transform); + } else if (p_instance->base_type == RS::INSTANCE_OCCLUDER) { + if (p_instance->scenario) { + RendererSceneOcclusionCull::get_singleton()->scenario_set_instance(p_instance->scenario->self, p_instance->self, p_instance->base, p_instance->transform, p_instance->visible); + } } if (p_instance->aabb.has_no_surface()) { @@ -1337,6 +1398,9 @@ void RendererSceneCull::_update_instance(Instance *p_instance) { if (p_instance->mesh_instance.is_valid()) { idata.flags |= InstanceData::FLAG_USES_MESH_INSTANCE; } + if (p_instance->ignore_occlusion_culling) { + idata.flags |= InstanceData::FLAG_IGNORE_OCCLUSION_CULLING; + } p_instance->scenario->instance_data.push_back(idata); p_instance->scenario->instance_aabbs.push_back(InstanceBounds(p_instance->transformed_aabb)); @@ -2119,7 +2183,7 @@ bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, cons return animated_material_found; } -void RendererSceneCull::render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas) { +void RendererSceneCull::render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas) { // render to mono camera #ifndef _3D_DISABLED @@ -2164,11 +2228,14 @@ void RendererSceneCull::render_camera(RID p_render_buffers, RID p_camera, RID p_ RID environment = _render_get_environment(p_camera, p_scenario); - _render_scene(camera->transform, camera_matrix, ortho, camera->vaspect, p_render_buffers, environment, camera->effects, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), -1, p_screen_lod_threshold); + RENDER_TIMESTAMP("Update occlusion buffer") + RendererSceneOcclusionCull::get_singleton()->buffer_update(p_viewport, camera->transform, camera_matrix, ortho, RendererThreadPool::singleton->thread_work_pool); + + _render_scene(camera->transform, camera_matrix, ortho, camera->vaspect, p_render_buffers, environment, camera->effects, camera->visible_layers, p_scenario, p_viewport, p_shadow_atlas, RID(), -1, p_screen_lod_threshold); #endif } -void RendererSceneCull::render_camera(RID p_render_buffers, Ref<XRInterface> &p_interface, XRInterface::Eyes p_eye, RID p_camera, RID p_scenario, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas) { +void RendererSceneCull::render_camera(RID p_render_buffers, Ref<XRInterface> &p_interface, XRInterface::Eyes p_eye, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas) { // render for AR/VR interface #if 0 Camera *camera = camera_owner.getornull(p_camera); @@ -2253,7 +2320,7 @@ void RendererSceneCull::render_camera(RID p_render_buffers, Ref<XRInterface> &p_ #endif }; -void RendererSceneCull::_frustum_cull_threaded(uint32_t p_thread, FrustumCullData *cull_data) { +void RendererSceneCull::_frustum_cull_threaded(uint32_t p_thread, CullData *cull_data) { uint32_t cull_total = cull_data->scenario->instance_data.size(); uint32_t total_threads = RendererThreadPool::singleton->thread_work_pool.get_thread_count(); uint32_t cull_from = p_thread * cull_total / total_threads; @@ -2262,7 +2329,7 @@ void RendererSceneCull::_frustum_cull_threaded(uint32_t p_thread, FrustumCullDat _frustum_cull(*cull_data, frustum_cull_result_threads[p_thread], cull_from, cull_to); } -void RendererSceneCull::_frustum_cull(FrustumCullData &cull_data, FrustumCullResult &cull_result, uint64_t p_from, uint64_t p_to) { +void RendererSceneCull::_frustum_cull(CullData &cull_data, FrustumCullResult &cull_result, uint64_t p_from, uint64_t p_to) { uint64_t frame_number = RSG::rasterizer->get_frame_number(); float lightmap_probe_update_speed = RSG::storage->lightmap_get_probe_capture_update_speed() * RSG::rasterizer->get_frame_delta_time(); @@ -2271,10 +2338,14 @@ void RendererSceneCull::_frustum_cull(FrustumCullData &cull_data, FrustumCullRes RID instance_pair_buffer[MAX_INSTANCE_PAIRS]; + Transform inv_cam_transform = cull_data.cam_transform.inverse(); + float z_near = cull_data.camera_matrix->get_z_near(); + for (uint64_t i = p_from; i < p_to; i++) { bool mesh_visible = false; - if (cull_data.scenario->instance_aabbs[i].in_frustum(cull_data.cull->frustum)) { + if (cull_data.scenario->instance_aabbs[i].in_frustum(cull_data.cull->frustum) && (cull_data.occlusion_buffer == nullptr || cull_data.scenario->instance_data[i].flags & InstanceData::FLAG_IGNORE_OCCLUSION_CULLING || + !cull_data.occlusion_buffer->is_occluded(cull_data.scenario->instance_aabbs[i].bounds, cull_data.cam_transform.origin, inv_cam_transform, *cull_data.camera_matrix, z_near))) { InstanceData &idata = cull_data.scenario->instance_data[i]; uint32_t base_type = idata.flags & InstanceData::FLAG_BASE_TYPE_MASK; @@ -2469,7 +2540,7 @@ void RendererSceneCull::_frustum_cull(FrustumCullData &cull_data, FrustumCullRes } } -void RendererSceneCull::_render_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_render_buffers, RID p_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, bool p_using_shadows) { +void RendererSceneCull::_render_scene(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_render_buffers, RID p_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, bool p_using_shadows) { // Note, in stereo rendering: // - p_cam_transform will be a transform in the middle of our two eyes // - p_cam_projection is a wider frustrum that encompasses both eyes @@ -2566,7 +2637,7 @@ void RendererSceneCull::_render_scene(const Transform p_cam_transform, const Cam uint64_t cull_from = 0; uint64_t cull_to = scenario->instance_data.size(); - FrustumCullData cull_data; + CullData cull_data; //prepare for eventual thread usage cull_data.cull = &cull; @@ -2575,6 +2646,8 @@ void RendererSceneCull::_render_scene(const Transform p_cam_transform, const Cam cull_data.cam_transform = p_cam_transform; cull_data.visible_layers = p_visible_layers; cull_data.render_reflection_probe = render_reflection_probe; + cull_data.occlusion_buffer = RendererSceneOcclusionCull::get_singleton()->buffer_get_ptr(p_viewport); + cull_data.camera_matrix = &p_cam_projection; //#define DEBUG_CULL_TIME #ifdef DEBUG_CULL_TIME uint64_t time_from = OS::get_singleton()->get_ticks_usec(); @@ -2781,8 +2854,13 @@ void RendererSceneCull::_render_scene(const Transform p_cam_transform, const Cam } /* PROCESS GEOMETRY AND DRAW SCENE */ + RID occluders_tex; + if (p_viewport.is_valid()) { + occluders_tex = RSG::viewport->viewport_get_occluder_debug_texture(p_viewport); + } + RENDER_TIMESTAMP("Render Scene "); - scene_render->render_scene(p_render_buffers, p_cam_transform, p_cam_projection, p_cam_orthogonal, frustum_cull_result.geometry_instances, frustum_cull_result.light_instances, frustum_cull_result.reflections, frustum_cull_result.gi_probes, frustum_cull_result.decals, frustum_cull_result.lightmaps, p_environment, camera_effects, p_shadow_atlas, p_reflection_probe.is_valid() ? RID() : scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass, p_screen_lod_threshold, render_shadow_data, max_shadows_used, render_sdfgi_data, cull.sdfgi.region_count, &sdfgi_update_data); + scene_render->render_scene(p_render_buffers, p_cam_transform, p_cam_projection, p_cam_orthogonal, frustum_cull_result.geometry_instances, frustum_cull_result.light_instances, frustum_cull_result.reflections, frustum_cull_result.gi_probes, frustum_cull_result.decals, frustum_cull_result.lightmaps, p_environment, camera_effects, p_shadow_atlas, occluders_tex, p_reflection_probe.is_valid() ? RID() : scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass, p_screen_lod_threshold, render_shadow_data, max_shadows_used, render_sdfgi_data, cull.sdfgi.region_count, &sdfgi_update_data); for (uint32_t i = 0; i < max_shadows_used; i++) { render_shadow_data[i].instances.clear(); @@ -2829,7 +2907,7 @@ void RendererSceneCull::render_empty_scene(RID p_render_buffers, RID p_scenario, environment = scenario->fallback_environment; } RENDER_TIMESTAMP("Render Empty Scene "); - scene_render->render_scene(p_render_buffers, Transform(), CameraMatrix(), true, PagedArray<RendererSceneRender::GeometryInstance *>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), RID(), RID(), p_shadow_atlas, scenario->reflection_atlas, RID(), 0, 0, nullptr, 0, nullptr, 0, nullptr); + scene_render->render_scene(p_render_buffers, Transform(), CameraMatrix(), true, PagedArray<RendererSceneRender::GeometryInstance *>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), RID(), RID(), p_shadow_atlas, RID(), scenario->reflection_atlas, RID(), 0, 0, nullptr, 0, nullptr, 0, nullptr); #endif } @@ -2899,7 +2977,7 @@ bool RendererSceneCull::_render_reflection_probe_step(Instance *p_instance, int } RENDER_TIMESTAMP("Render Reflection Probe, Step " + itos(p_step)); - _render_scene(xform, cm, false, false, RID(), environment, RID(), RSG::storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, shadow_atlas, reflection_probe->instance, p_step, lod_threshold, use_shadows); + _render_scene(xform, cm, false, false, RID(), environment, RID(), RSG::storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, RID(), shadow_atlas, reflection_probe->instance, p_step, lod_threshold, use_shadows); } else { //do roughness postprocess step until it believes it's done @@ -3473,8 +3551,11 @@ bool RendererSceneCull::free(RID p_rid) { scene_render->free(scenario->reflection_probe_shadow_atlas); scene_render->free(scenario->reflection_atlas); scenario_owner.free(p_rid); + RendererSceneOcclusionCull::get_singleton()->remove_scenario(p_rid); memdelete(scenario); + } else if (RendererSceneOcclusionCull::get_singleton()->is_occluder(p_rid)) { + RendererSceneOcclusionCull::get_singleton()->free_occluder(p_rid); } else if (instance_owner.owns(p_rid)) { // delete the instance @@ -3543,6 +3624,8 @@ RendererSceneCull::RendererSceneCull() { indexer_update_iterations = GLOBAL_GET("rendering/limits/spatial_indexer/update_iterations_per_frame"); thread_cull_threshold = GLOBAL_GET("rendering/limits/spatial_indexer/threaded_cull_minimum_instances"); thread_cull_threshold = MAX(thread_cull_threshold, (uint32_t)RendererThreadPool::singleton->thread_work_pool.get_thread_count()); //make sure there is at least one thread per CPU + + dummy_occlusion_culling = memnew(RendererSceneOcclusionCull); } RendererSceneCull::~RendererSceneCull() { @@ -3561,4 +3644,8 @@ RendererSceneCull::~RendererSceneCull() { frustum_cull_result_threads[i].reset(); } frustum_cull_result_threads.clear(); + + if (dummy_occlusion_culling) { + memdelete(dummy_occlusion_culling); + } } diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h index d7d59665ec..36eece9dd8 100644 --- a/servers/rendering/renderer_scene_cull.h +++ b/servers/rendering/renderer_scene_cull.h @@ -45,8 +45,10 @@ #include "core/templates/rid_owner.h" #include "core/templates/self_list.h" #include "servers/rendering/renderer_scene.h" +#include "servers/rendering/renderer_scene_occlusion_cull.h" #include "servers/rendering/renderer_scene_render.h" #include "servers/xr/xr_interface.h" + class RendererSceneCull : public RendererScene { public: RendererSceneRender *scene_render; @@ -109,6 +111,14 @@ public: virtual void camera_set_use_vertical_aspect(RID p_camera, bool p_enable); virtual bool is_camera(RID p_camera) const; + /* OCCLUDER API */ + + virtual RID occluder_allocate(); + virtual void occluder_initialize(RID p_occluder); + virtual void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices); + + RendererSceneOcclusionCull *dummy_occlusion_culling; + /* SCENARIO API */ struct Instance; @@ -248,6 +258,7 @@ public: FLAG_USES_BAKED_LIGHT = (1 << 16), FLAG_USES_MESH_INSTANCE = (1 << 17), FLAG_REFLECTION_PROBE_DIRTY = (1 << 18), + FLAG_IGNORE_OCCLUSION_CULLING = (1 << 19), }; uint32_t flags = 0; @@ -346,6 +357,8 @@ public: float lod_bias; + bool ignore_occlusion_culling; + Vector<RID> materials; RS::ShadowCastingSetting cast_shadows; @@ -470,6 +483,7 @@ public: lightmap = nullptr; lightmap_cull_index = 0; lod_bias = 1.0; + ignore_occlusion_culling = false; scenario = nullptr; @@ -921,24 +935,26 @@ public: Frustum frustum; } cull; - struct FrustumCullData { + struct CullData { Cull *cull; Scenario *scenario; RID shadow_atlas; Transform cam_transform; uint32_t visible_layers; Instance *render_reflection_probe; + const RendererSceneOcclusionCull::HZBuffer *occlusion_buffer; + const CameraMatrix *camera_matrix; }; - void _frustum_cull_threaded(uint32_t p_thread, FrustumCullData *cull_data); - void _frustum_cull(FrustumCullData &cull_data, FrustumCullResult &cull_result, uint64_t p_from, uint64_t p_to); + void _frustum_cull_threaded(uint32_t p_thread, CullData *cull_data); + void _frustum_cull(CullData &cull_data, FrustumCullResult &cull_result, uint64_t p_from, uint64_t p_to); bool _render_reflection_probe_step(Instance *p_instance, int p_step); - void _render_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_render_buffers, RID p_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, bool p_using_shadows = true); + void _render_scene(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_render_buffers, RID p_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, bool p_using_shadows = true); void render_empty_scene(RID p_render_buffers, RID p_scenario, RID p_shadow_atlas); - void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas); - void render_camera(RID p_render_buffers, Ref<XRInterface> &p_interface, XRInterface::Eyes p_eye, RID p_camera, RID p_scenario, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas); + void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas); + void render_camera(RID p_render_buffers, Ref<XRInterface> &p_interface, XRInterface::Eyes p_eye, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas); void update_dirty_instances(); void render_particle_colliders(); diff --git a/servers/rendering/renderer_scene_occlusion_cull.cpp b/servers/rendering/renderer_scene_occlusion_cull.cpp new file mode 100644 index 0000000000..c491ccbe7a --- /dev/null +++ b/servers/rendering/renderer_scene_occlusion_cull.cpp @@ -0,0 +1,192 @@ +/*************************************************************************/ +/* renderer_scene_occlusion_cull.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 "renderer_scene_occlusion_cull.h" + +RendererSceneOcclusionCull *RendererSceneOcclusionCull::singleton = nullptr; + +const Vector3 RendererSceneOcclusionCull::HZBuffer::corners[8] = { + Vector3(0, 0, 0), + Vector3(0, 0, 1), + Vector3(0, 1, 0), + Vector3(0, 1, 1), + Vector3(1, 0, 0), + Vector3(1, 0, 1), + Vector3(1, 1, 0), + Vector3(1, 1, 1) +}; + +bool RendererSceneOcclusionCull::HZBuffer::is_empty() const { + return sizes.is_empty(); +} + +void RendererSceneOcclusionCull::HZBuffer::clear() { + if (sizes.is_empty()) { + return; // Already cleared + } + + data.clear(); + sizes.clear(); + mips.clear(); + + debug_data.clear(); + if (debug_image.is_valid()) { + debug_image.unref(); + } + RS::get_singleton()->free(debug_texture); +} + +void RendererSceneOcclusionCull::HZBuffer::resize(const Size2i &p_size) { + if (p_size == Size2i()) { + clear(); + return; + } + + if (!sizes.is_empty() && p_size == sizes[0]) { + return; // Size didn't change + } + + int mip_count = 0; + int data_size = 0; + int w = p_size.x; + int h = p_size.y; + + while (true) { + data_size += h * w; + + w = MAX(1, w >> 1); + h = MAX(1, h >> 1); + + mip_count++; + + if (w == 1U && h == 1U) { + data_size += 1U; + mip_count++; + break; + } + } + + data.resize(data_size); + mips.resize(mip_count); + sizes.resize(mip_count); + + w = p_size.x; + h = p_size.y; + float *ptr = data.ptr(); + + for (int i = 0; i < mip_count; i++) { + sizes[i] = Size2i(w, h); + mips[i] = ptr; + + ptr = &ptr[w * h]; + w = MAX(1, w >> 1); + h = MAX(1, h >> 1); + } + + for (int i = 0; i < data_size; i++) { + data[i] = FLT_MAX; + } + + debug_data.resize(sizes[0].x * sizes[0].y); + if (debug_texture.is_valid()) { + RS::get_singleton()->free(debug_texture); + debug_texture = RID(); + } +} + +void RendererSceneOcclusionCull::HZBuffer::update_mips() { + if (sizes.is_empty()) { + return; + } + + for (uint32_t mip = 1; mip < mips.size(); mip++) { + for (int y = 0; y < sizes[mip].y; y++) { + for (int x = 0; x < sizes[mip].x; x++) { + int prev_x = x * 2; + int prev_y = y * 2; + + int prev_w = sizes[mip - 1].width; + int prev_h = sizes[mip - 1].height; + + bool odd_w = (prev_w % 2) != 0; + bool odd_h = (prev_h % 2) != 0; + +#define CHECK_OFFSET(xx, yy) max_depth = MAX(max_depth, mips[mip - 1][MIN(prev_h - 1, prev_y + (yy)) * prev_w + MIN(prev_w - 1, prev_x + (xx))]) + + float max_depth = mips[mip - 1][prev_y * sizes[mip - 1].x + prev_x]; + CHECK_OFFSET(0, 1); + CHECK_OFFSET(1, 0); + CHECK_OFFSET(1, 1); + + if (odd_w) { + CHECK_OFFSET(2, 0); + CHECK_OFFSET(2, 1); + } + + if (odd_h) { + CHECK_OFFSET(0, 2); + CHECK_OFFSET(1, 2); + } + + if (odd_w && odd_h) { + CHECK_OFFSET(2, 2); + } + + mips[mip][y * sizes[mip].x + x] = max_depth; +#undef CHECK_OFFSET + } + } + } +} + +RID RendererSceneOcclusionCull::HZBuffer::get_debug_texture() { + if (sizes.is_empty() || sizes[0] == Size2i()) { + return RID(); + } + + if (debug_image.is_null()) { + debug_image.instance(); + } + + unsigned char *ptrw = debug_data.ptrw(); + for (int i = 0; i < debug_data.size(); i++) { + ptrw[i] = MIN(mips[0][i] / debug_tex_range, 1.0) * 255; + } + + debug_image->create(sizes[0].x, sizes[0].y, false, Image::FORMAT_L8, debug_data); + + if (debug_texture.is_null()) { + debug_texture = RS::get_singleton()->texture_2d_create(debug_image); + } else { + RenderingServer::get_singleton()->texture_2d_update_immediate(debug_texture, debug_image); + } + + return debug_texture; +} diff --git a/servers/rendering/renderer_scene_occlusion_cull.h b/servers/rendering/renderer_scene_occlusion_cull.h new file mode 100644 index 0000000000..390bbaa64b --- /dev/null +++ b/servers/rendering/renderer_scene_occlusion_cull.h @@ -0,0 +1,201 @@ +/*************************************************************************/ +/* renderer_scene_occlusion_cull.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 RENDERER_SCENE_OCCLUSION_CULL_H +#define RENDERER_SCENE_OCCLUSION_CULL_H + +#include "core/math/camera_matrix.h" +#include "core/templates/local_vector.h" +#include "servers/rendering_server.h" + +class RendererSceneOcclusionCull { +protected: + static RendererSceneOcclusionCull *singleton; + +public: + class HZBuffer { + protected: + static const Vector3 corners[8]; + + LocalVector<float> data; + LocalVector<Size2i> sizes; + LocalVector<float *> mips; + + RID debug_texture; + Ref<Image> debug_image; + PackedByteArray debug_data; + float debug_tex_range = 0.0f; + + public: + bool is_empty() const; + virtual void clear(); + virtual void resize(const Size2i &p_size); + + void update_mips(); + + _FORCE_INLINE_ bool is_occluded(const float p_bounds[6], const Vector3 &p_cam_position, const Transform &p_cam_inv_transform, const CameraMatrix &p_cam_projection, float p_near) const { + if (is_empty()) { + return false; + } + + Vector3 closest_point = Vector3(CLAMP(p_cam_position.x, p_bounds[0], p_bounds[3]), CLAMP(p_cam_position.y, p_bounds[1], p_bounds[4]), CLAMP(p_cam_position.z, p_bounds[2], p_bounds[5])); + + if (closest_point == p_cam_position) { + return false; + } + + Vector3 closest_point_view = p_cam_inv_transform.xform(closest_point); + if (closest_point_view.z > -p_near) { + return false; + } + + float min_depth; + if (p_cam_projection.is_orthogonal()) { + min_depth = (-closest_point_view.z) - p_near; + } else { + float r = -p_near / closest_point_view.z; + Vector3 closest_point_proj = Vector3(closest_point_view.x * r, closest_point_view.y * r, -p_near); + min_depth = closest_point_proj.distance_to(closest_point_view); + } + + Vector2 rect_min = Vector2(FLT_MAX, FLT_MAX); + Vector2 rect_max = Vector2(FLT_MIN, FLT_MIN); + + for (int j = 0; j < 8; j++) { + Vector3 c = RendererSceneOcclusionCull::HZBuffer::corners[j]; + Vector3 nc = Vector3(1, 1, 1) - c; + Vector3 corner = Vector3(p_bounds[0] * c.x + p_bounds[3] * nc.x, p_bounds[1] * c.y + p_bounds[4] * nc.y, p_bounds[2] * c.z + p_bounds[5] * nc.z); + Vector3 view = p_cam_inv_transform.xform(corner); + + Vector3 projected = p_cam_projection.xform(view); + Vector2 normalized = Vector2(projected.x * 0.5f + 0.5f, projected.y * 0.5f + 0.5f); + rect_min = rect_min.min(normalized); + rect_max = rect_max.max(normalized); + } + + rect_max = rect_max.min(Vector2(1, 1)); + rect_min = rect_min.max(Vector2(0, 0)); + + int mip_count = mips.size(); + + Vector2 screen_diagonal = (rect_max - rect_min) * sizes[0]; + float size = MAX(screen_diagonal.x, screen_diagonal.y); + float l = Math::ceil(Math::log2(size)); + int lod = CLAMP(l, 0, mip_count - 1); + + const int max_samples = 512; + int sample_count = 0; + bool visible = true; + + for (; lod >= 0; lod--) { + int w = sizes[lod].x; + int h = sizes[lod].y; + + int minx = CLAMP(rect_min.x * w - 1, 0, w - 1); + int maxx = CLAMP(rect_max.x * w + 1, 0, w - 1); + + int miny = CLAMP(rect_min.y * h - 1, 0, h - 1); + int maxy = CLAMP(rect_max.y * h + 1, 0, h - 1); + + sample_count += (maxx - minx + 1) * (maxy - miny + 1); + + if (sample_count > max_samples) { + return false; + } + + visible = false; + for (int y = miny; y <= maxy; y++) { + for (int x = minx; x <= maxx; x++) { + float depth = mips[lod][y * w + x]; + if (depth > min_depth) { + visible = true; + break; + } + } + if (visible) { + break; + } + } + + if (!visible) { + return true; + } + } + + return !visible; + } + + RID get_debug_texture(); + + virtual ~HZBuffer(){}; + }; + + static RendererSceneOcclusionCull *get_singleton() { return singleton; } + + void _print_warining() { + WARN_PRINT_ONCE("Occlusion culling is disabled at build time."); + } + + virtual bool is_occluder(RID p_rid) { return false; } + virtual RID occluder_allocate() { return RID(); } + virtual void occluder_initialize(RID p_occluder) {} + virtual void free_occluder(RID p_occluder) { _print_warining(); } + virtual void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) { _print_warining(); } + + virtual void add_scenario(RID p_scenario) {} + virtual void remove_scenario(RID p_scenario) {} + virtual void scenario_set_instance(RID p_scenario, RID p_instance, RID p_occluder, const Transform &p_xform, bool p_enabled) { _print_warining(); } + virtual void scenario_remove_instance(RID p_scenario, RID p_instance) { _print_warining(); } + + virtual void add_buffer(RID p_buffer) { _print_warining(); } + virtual void remove_buffer(RID p_buffer) { _print_warining(); } + virtual HZBuffer *buffer_get_ptr(RID p_buffer) { + return nullptr; + } + virtual void buffer_set_scenario(RID p_buffer, RID p_scenario) { _print_warining(); } + virtual void buffer_set_size(RID p_buffer, const Vector2i &p_size) { _print_warining(); } + virtual void buffer_update(RID p_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, ThreadWorkPool &p_thread_pool) {} + virtual RID buffer_get_debug_texture(RID p_buffer) { + _print_warining(); + return RID(); + } + + virtual void set_build_quality(RS::ViewportOcclusionCullingBuildQuality p_quality) {} + + RendererSceneOcclusionCull() { + singleton = this; + }; + + virtual ~RendererSceneOcclusionCull() { + singleton = nullptr; + }; +}; + +#endif //RENDERER_SCENE_OCCLUSION_CULL_H diff --git a/servers/rendering/renderer_scene_render.h b/servers/rendering/renderer_scene_render.h index 9ca9574f6f..3f28fac549 100644 --- a/servers/rendering/renderer_scene_render.h +++ b/servers/rendering/renderer_scene_render.h @@ -216,7 +216,7 @@ public: uint32_t positional_light_count; }; - virtual void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_gi_probes, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_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) = 0; + virtual void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_gi_probes, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, 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_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) = 0; virtual void render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) = 0; virtual void render_particle_collider_heightfield(RID p_collider, const Transform &p_transform, const PagedArray<GeometryInstance *> &p_instances) = 0; diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index a5d5033c18..f7be6c6c60 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -79,11 +79,26 @@ void RendererViewport::_draw_3d(Viewport *p_viewport, XRInterface::Eyes p_eye) { xr_interface = XRServer::get_singleton()->get_primary_interface(); } + if (p_viewport->use_occlusion_culling) { + if (p_viewport->occlusion_buffer_dirty) { + float aspect = p_viewport->size.aspect(); + int max_size = occlusion_rays_per_thread * RendererThreadPool::singleton->thread_work_pool.get_thread_count(); + + int viewport_size = p_viewport->size.width * p_viewport->size.height; + max_size = CLAMP(max_size, viewport_size / (32 * 32), viewport_size / (2 * 2)); // At least one depth pixel for every 16x16 region. At most one depth pixel for every 2x2 region. + + float height = Math::sqrt(max_size / aspect); + Size2i new_size = Size2i(height * aspect, height); + RendererSceneOcclusionCull::get_singleton()->buffer_set_size(p_viewport->self, new_size); + p_viewport->occlusion_buffer_dirty = false; + } + } + float screen_lod_threshold = p_viewport->lod_threshold / float(p_viewport->size.width); if (p_viewport->use_xr && xr_interface.is_valid()) { - RSG::scene->render_camera(p_viewport->render_buffers, xr_interface, p_eye, p_viewport->camera, p_viewport->scenario, p_viewport->size, screen_lod_threshold, p_viewport->shadow_atlas); + RSG::scene->render_camera(p_viewport->render_buffers, xr_interface, p_eye, p_viewport->camera, p_viewport->scenario, p_viewport->self, p_viewport->size, screen_lod_threshold, p_viewport->shadow_atlas); } else { - RSG::scene->render_camera(p_viewport->render_buffers, p_viewport->camera, p_viewport->scenario, p_viewport->size, screen_lod_threshold, p_viewport->shadow_atlas); + RSG::scene->render_camera(p_viewport->render_buffers, p_viewport->camera, p_viewport->scenario, p_viewport->self, p_viewport->size, screen_lod_threshold, p_viewport->shadow_atlas); } RENDER_TIMESTAMP("<End Rendering 3D Scene"); } @@ -647,6 +662,8 @@ void RendererViewport::viewport_set_size(RID p_viewport, int p_width, int p_heig RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, viewport->screen_space_aa, viewport->use_debanding); } } + + viewport->occlusion_buffer_dirty = true; } void RendererViewport::viewport_set_active(RID p_viewport, bool p_active) { @@ -655,6 +672,7 @@ void RendererViewport::viewport_set_active(RID p_viewport, bool p_active) { if (p_active) { ERR_FAIL_COND(active_viewports.find(viewport) != -1); //already active + viewport->occlusion_buffer_dirty = true; active_viewports.push_back(viewport); } else { active_viewports.erase(viewport); @@ -739,6 +757,16 @@ RID RendererViewport::viewport_get_texture(RID p_viewport) const { return RSG::storage->render_target_get_texture(viewport->render_target); } +RID RendererViewport::viewport_get_occluder_debug_texture(RID p_viewport) const { + const Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND_V(!viewport, RID()); + + if (viewport->use_occlusion_culling && viewport->debug_draw == RenderingServer::VIEWPORT_DEBUG_DRAW_OCCLUDERS) { + return RendererSceneOcclusionCull::get_singleton()->buffer_get_debug_texture(p_viewport); + } + return RID(); +} + void RendererViewport::viewport_set_hide_scenario(RID p_viewport, bool p_hide) { Viewport *viewport = viewport_owner.getornull(p_viewport); ERR_FAIL_COND(!viewport); @@ -772,6 +800,9 @@ void RendererViewport::viewport_set_scenario(RID p_viewport, RID p_scenario) { ERR_FAIL_COND(!viewport); viewport->scenario = p_scenario; + if (viewport->use_occlusion_culling) { + RendererSceneOcclusionCull::get_singleton()->buffer_set_scenario(p_viewport, p_scenario); + } } void RendererViewport::viewport_attach_canvas(RID p_viewport, RID p_canvas) { @@ -888,6 +919,41 @@ void RendererViewport::viewport_set_use_debanding(RID p_viewport, bool p_use_deb } } +void RendererViewport::viewport_set_use_occlusion_culling(RID p_viewport, bool p_use_occlusion_culling) { + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + if (viewport->use_occlusion_culling == p_use_occlusion_culling) { + return; + } + viewport->use_occlusion_culling = p_use_occlusion_culling; + + if (viewport->use_occlusion_culling) { + RendererSceneOcclusionCull::get_singleton()->add_buffer(p_viewport); + RendererSceneOcclusionCull::get_singleton()->buffer_set_scenario(p_viewport, viewport->scenario); + } else { + RendererSceneOcclusionCull::get_singleton()->remove_buffer(p_viewport); + } + + viewport->occlusion_buffer_dirty = true; +} + +void RendererViewport::viewport_set_occlusion_rays_per_thread(int p_rays_per_thread) { + if (occlusion_rays_per_thread == p_rays_per_thread) { + return; + } + + occlusion_rays_per_thread = p_rays_per_thread; + + for (int i = 0; i < active_viewports.size(); i++) { + active_viewports[i]->occlusion_buffer_dirty = true; + } +} + +void RendererViewport::viewport_set_occlusion_culling_build_quality(RS::ViewportOcclusionCullingBuildQuality p_quality) { + RendererSceneOcclusionCull::get_singleton()->set_build_quality(p_quality); +} + void RendererViewport::viewport_set_lod_threshold(RID p_viewport, float p_pixels) { Viewport *viewport = viewport_owner.getornull(p_viewport); ERR_FAIL_COND(!viewport); @@ -985,6 +1051,10 @@ bool RendererViewport::free(RID p_rid) { viewport_set_scenario(p_rid, RID()); active_viewports.erase(viewport); + if (viewport->use_occlusion_culling) { + RendererSceneOcclusionCull::get_singleton()->remove_buffer(p_rid); + } + viewport_owner.free(p_rid); memdelete(viewport); @@ -1026,4 +1096,5 @@ void RendererViewport::call_set_use_vsync(bool p_enable) { } RendererViewport::RendererViewport() { + occlusion_rays_per_thread = GLOBAL_GET("rendering/occlusion_culling/occlusion_rays_per_thread"); } diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h index f5ed543e8d..5c372e8c9a 100644 --- a/servers/rendering/renderer_viewport.h +++ b/servers/rendering/renderer_viewport.h @@ -31,9 +31,9 @@ #ifndef VISUALSERVERVIEWPORT_H #define VISUALSERVERVIEWPORT_H +#include "core/templates/local_vector.h" #include "core/templates/rid_owner.h" #include "core/templates/self_list.h" -#include "renderer_compositor.h" #include "servers/rendering_server.h" #include "servers/xr/xr_interface.h" @@ -61,6 +61,9 @@ public: RS::ViewportScreenSpaceAA screen_space_aa; bool use_debanding; + bool use_occlusion_culling; + bool occlusion_buffer_dirty; + DisplayServer::WindowID viewport_to_screen; Rect2 viewport_to_screen_rect; bool viewport_render_direct_to_screen; @@ -143,6 +146,8 @@ public: msaa = RS::VIEWPORT_MSAA_DISABLED; screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED; use_debanding = false; + use_occlusion_culling = false; + occlusion_buffer_dirty = true; snap_2d_transforms_to_pixel = false; snap_2d_vertices_to_pixel = false; @@ -185,6 +190,10 @@ private: void _draw_3d(Viewport *p_viewport, XRInterface::Eyes p_eye); void _draw_viewport(Viewport *p_viewport, XRInterface::Eyes p_eye = XRInterface::EYE_MONO); + int occlusion_rays_per_thread = 512; + + void _resize_occlusion_culling_buffer(const Size2i &p_size); + public: RID viewport_allocate(); void viewport_initialize(RID p_rid); @@ -204,6 +213,7 @@ public: void viewport_set_clear_mode(RID p_viewport, RS::ViewportClearMode p_clear_mode); RID viewport_get_texture(RID p_viewport) const; + RID viewport_get_occluder_debug_texture(RID p_viewport) const; void viewport_set_hide_scenario(RID p_viewport, bool p_hide); void viewport_set_hide_canvas(RID p_viewport, bool p_hide); @@ -225,7 +235,9 @@ public: void viewport_set_msaa(RID p_viewport, RS::ViewportMSAA p_msaa); void viewport_set_screen_space_aa(RID p_viewport, RS::ViewportScreenSpaceAA p_mode); void viewport_set_use_debanding(RID p_viewport, bool p_use_debanding); - + void viewport_set_use_occlusion_culling(RID p_viewport, bool p_use_occlusion_culling); + void viewport_set_occlusion_rays_per_thread(int p_rays_per_thread); + void viewport_set_occlusion_culling_build_quality(RS::ViewportOcclusionCullingBuildQuality p_quality); void viewport_set_lod_threshold(RID p_viewport, float p_pixels); virtual int viewport_get_render_info(RID p_viewport, RS::ViewportRenderInfo p_info); diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 683a22fd9a..324c002d6f 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -540,6 +540,10 @@ public: FUNC2(camera_set_camera_effects, RID, RID) FUNC2(camera_set_use_vertical_aspect, RID, bool) + /* OCCLUDER */ + FUNCRIDSPLIT(occluder) + FUNC3(occluder_set_mesh, RID, const PackedVector3Array &, const PackedInt32Array &); + #undef server_name #undef ServerName //from now on, calls forwarded to this singleton @@ -590,6 +594,9 @@ public: FUNC2(viewport_set_msaa, RID, ViewportMSAA) FUNC2(viewport_set_screen_space_aa, RID, ViewportScreenSpaceAA) FUNC2(viewport_set_use_debanding, RID, bool) + FUNC2(viewport_set_use_occlusion_culling, RID, bool) + FUNC1(viewport_set_occlusion_rays_per_thread, int) + FUNC1(viewport_set_occlusion_culling_build_quality, ViewportOcclusionCullingBuildQuality) FUNC2(viewport_set_lod_threshold, RID, float) FUNC2R(int, viewport_get_render_info, RID, ViewportRenderInfo) diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index f8644b5ecb..3b6838c9ba 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -1594,7 +1594,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("gi_probe_set_compress", "probe", "enable"), &RenderingServer::gi_probe_set_compress); ClassDB::bind_method(D_METHOD("gi_probe_is_compressed", "probe"), &RenderingServer::gi_probe_is_compressed); #endif -/* + /* ClassDB::bind_method(D_METHOD("lightmap_create()"), &RenderingServer::lightmap_capture_create); ClassDB::bind_method(D_METHOD("lightmap_capture_set_bounds", "capture", "bounds"), &RenderingServer::lightmap_capture_set_bounds); ClassDB::bind_method(D_METHOD("lightmap_capture_get_bounds", "capture"), &RenderingServer::lightmap_capture_get_bounds); @@ -1607,6 +1607,10 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("lightmap_capture_set_energy", "capture", "energy"), &RenderingServer::lightmap_capture_set_energy); ClassDB::bind_method(D_METHOD("lightmap_capture_get_energy", "capture"), &RenderingServer::lightmap_capture_get_energy); */ + + ClassDB::bind_method(D_METHOD("occluder_create"), &RenderingServer::occluder_create); + ClassDB::bind_method(D_METHOD("occluder_set_mesh"), &RenderingServer::occluder_set_mesh); + #endif ClassDB::bind_method(D_METHOD("particles_create"), &RenderingServer::particles_create); ClassDB::bind_method(D_METHOD("particles_set_emitting", "particles", "emitting"), &RenderingServer::particles_set_emitting); @@ -1667,6 +1671,9 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("viewport_set_shadow_atlas_quadrant_subdivision", "viewport", "quadrant", "subdivision"), &RenderingServer::viewport_set_shadow_atlas_quadrant_subdivision); ClassDB::bind_method(D_METHOD("viewport_set_msaa", "viewport", "msaa"), &RenderingServer::viewport_set_msaa); ClassDB::bind_method(D_METHOD("viewport_set_use_debanding", "viewport", "enable"), &RenderingServer::viewport_set_use_debanding); + ClassDB::bind_method(D_METHOD("viewport_set_use_occlusion_culling", "viewport", "enable"), &RenderingServer::viewport_set_use_occlusion_culling); + ClassDB::bind_method(D_METHOD("viewport_set_occlusion_rays_per_thread", "rays_per_thread"), &RenderingServer::viewport_set_occlusion_rays_per_thread); + ClassDB::bind_method(D_METHOD("viewport_set_occlusion_culling_build_quality", "quality"), &RenderingServer::viewport_set_occlusion_culling_build_quality); ClassDB::bind_method(D_METHOD("viewport_get_render_info", "viewport", "info"), &RenderingServer::viewport_get_render_info); ClassDB::bind_method(D_METHOD("viewport_set_debug_draw", "viewport", "draw"), &RenderingServer::viewport_set_debug_draw); @@ -1694,6 +1701,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("scenario_create"), &RenderingServer::scenario_create); ClassDB::bind_method(D_METHOD("scenario_set_debug", "scenario", "debug_mode"), &RenderingServer::scenario_set_debug); ClassDB::bind_method(D_METHOD("scenario_set_environment", "scenario", "environment"), &RenderingServer::scenario_set_environment); + ClassDB::bind_method(D_METHOD("scenario_set_camera_effects", "scenario", "effects"), &RenderingServer::scenario_set_camera_effects); ClassDB::bind_method(D_METHOD("scenario_set_fallback_environment", "scenario", "environment"), &RenderingServer::scenario_set_fallback_environment); #ifndef _3D_DISABLED @@ -2024,6 +2032,7 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_SDFGI); BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_SDFGI_PROBES); BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_GI_BUFFER); + BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_OCCLUDERS); BIND_ENUM_CONSTANT(SKY_MODE_QUALITY); BIND_ENUM_CONSTANT(SKY_MODE_REALTIME); @@ -2093,6 +2102,10 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(SCENARIO_DEBUG_OVERDRAW); BIND_ENUM_CONSTANT(SCENARIO_DEBUG_SHADELESS); + BIND_ENUM_CONSTANT(VIEWPORT_OCCLUSION_BUILD_QUALITY_LOW); + BIND_ENUM_CONSTANT(VIEWPORT_OCCLUSION_BUILD_QUALITY_MEDIUM); + BIND_ENUM_CONSTANT(VIEWPORT_OCCLUSION_BUILD_QUALITY_HIGH); + BIND_ENUM_CONSTANT(INSTANCE_NONE); BIND_ENUM_CONSTANT(INSTANCE_MESH); BIND_ENUM_CONSTANT(INSTANCE_MULTIMESH); @@ -2104,12 +2117,14 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(INSTANCE_DECAL); BIND_ENUM_CONSTANT(INSTANCE_GI_PROBE); BIND_ENUM_CONSTANT(INSTANCE_LIGHTMAP); + BIND_ENUM_CONSTANT(INSTANCE_OCCLUDER); BIND_ENUM_CONSTANT(INSTANCE_MAX); BIND_ENUM_CONSTANT(INSTANCE_GEOMETRY_MASK); BIND_ENUM_CONSTANT(INSTANCE_FLAG_USE_BAKED_LIGHT); BIND_ENUM_CONSTANT(INSTANCE_FLAG_USE_DYNAMIC_GI); BIND_ENUM_CONSTANT(INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE); + BIND_ENUM_CONSTANT(INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING); BIND_ENUM_CONSTANT(INSTANCE_FLAG_MAX); BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_OFF); @@ -2340,6 +2355,10 @@ RenderingServer::RenderingServer() { ProjectSettings::get_singleton()->set_custom_property_info("rendering/anti_aliasing/screen_space_roughness_limiter/amount", PropertyInfo(Variant::FLOAT, "rendering/anti_aliasing/screen_space_roughness_limiter/amount", PROPERTY_HINT_RANGE, "0.01,4.0,0.01")); ProjectSettings::get_singleton()->set_custom_property_info("rendering/anti_aliasing/screen_space_roughness_limiter/limit", PropertyInfo(Variant::FLOAT, "rendering/anti_aliasing/screen_space_roughness_limiter/limit", PROPERTY_HINT_RANGE, "0.01,1.0,0.01")); + GLOBAL_DEF_RST("rendering/occlusion_culling/occlusion_rays_per_thread", 512); + GLOBAL_DEF_RST("rendering/occlusion_culling/bvh_build_quality", 2); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/occlusion_culling/bvh_build_quality", PropertyInfo(Variant::INT, "rendering/occlusion_culling/bvh_build_quality", PROPERTY_HINT_ENUM, "Low,Medium,High")); + GLOBAL_DEF("rendering/environment/glow/upscale_mode", 1); ProjectSettings::get_singleton()->set_custom_property_info("rendering/environment/glow/upscale_mode", PropertyInfo(Variant::INT, "rendering/environment/glow/upscale_mode", PROPERTY_HINT_ENUM, "Linear (Fast),Bicubic (Slow)")); GLOBAL_DEF("rendering/environment/glow/upscale_mode.mobile", 0); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 694fae7fde..c74bb22aad 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -713,6 +713,11 @@ public: virtual void camera_set_camera_effects(RID p_camera, RID p_camera_effects) = 0; virtual void camera_set_use_vertical_aspect(RID p_camera, bool p_enable) = 0; + /* OCCLUDER API */ + + virtual RID occluder_create() = 0; + virtual void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) = 0; + /* VIEWPORT TARGET API */ enum CanvasItemTextureFilter { @@ -826,6 +831,17 @@ public: virtual void viewport_set_lod_threshold(RID p_viewport, float p_pixels) = 0; + virtual void viewport_set_use_occlusion_culling(RID p_viewport, bool p_use_debanding) = 0; + virtual void viewport_set_occlusion_rays_per_thread(int p_rays_per_thread) = 0; + + enum ViewportOcclusionCullingBuildQuality { + VIEWPORT_OCCLUSION_BUILD_QUALITY_LOW = 0, + VIEWPORT_OCCLUSION_BUILD_QUALITY_MEDIUM = 1, + VIEWPORT_OCCLUSION_BUILD_QUALITY_HIGH = 2, + }; + + virtual void viewport_set_occlusion_culling_build_quality(ViewportOcclusionCullingBuildQuality p_quality) = 0; + enum ViewportRenderInfo { VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME, VIEWPORT_RENDER_INFO_VERTICES_IN_FRAME, @@ -862,6 +878,7 @@ public: VIEWPORT_DEBUG_DRAW_CLUSTER_SPOT_LIGHTS, VIEWPORT_DEBUG_DRAW_CLUSTER_DECALS, VIEWPORT_DEBUG_DRAW_CLUSTER_REFLECTION_PROBES, + VIEWPORT_DEBUG_DRAW_OCCLUDERS, }; virtual void viewport_set_debug_draw(RID p_viewport, ViewportDebugDraw p_draw) = 0; @@ -1109,6 +1126,7 @@ public: INSTANCE_DECAL, INSTANCE_GI_PROBE, INSTANCE_LIGHTMAP, + INSTANCE_OCCLUDER, INSTANCE_MAX, INSTANCE_GEOMETRY_MASK = (1 << INSTANCE_MESH) | (1 << INSTANCE_MULTIMESH) | (1 << INSTANCE_IMMEDIATE) | (1 << INSTANCE_PARTICLES) @@ -1147,6 +1165,7 @@ public: INSTANCE_FLAG_USE_BAKED_LIGHT, INSTANCE_FLAG_USE_DYNAMIC_GI, INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, + INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, INSTANCE_FLAG_MAX }; @@ -1505,6 +1524,7 @@ VARIANT_ENUM_CAST(RenderingServer::ViewportMSAA); VARIANT_ENUM_CAST(RenderingServer::ViewportScreenSpaceAA); VARIANT_ENUM_CAST(RenderingServer::ViewportRenderInfo); VARIANT_ENUM_CAST(RenderingServer::ViewportDebugDraw); +VARIANT_ENUM_CAST(RenderingServer::ViewportOcclusionCullingBuildQuality); VARIANT_ENUM_CAST(RenderingServer::SkyMode); VARIANT_ENUM_CAST(RenderingServer::EnvironmentBG); VARIANT_ENUM_CAST(RenderingServer::EnvironmentAmbientSource); |