Dynamic object support for GI Probes (a bit buggy still)

This commit is contained in:
Juan Linietsky 2019-10-10 23:14:56 -03:00
parent a95fb114ba
commit 561b431d85
19 changed files with 1623 additions and 358 deletions

View file

@ -2656,7 +2656,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture,
image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
image_memory_barrier.pNext = NULL;
image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
image_memory_barrier.newLayout = src_tex->layout;
image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
@ -2677,7 +2677,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture,
image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
image_memory_barrier.pNext = NULL;
image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
image_memory_barrier.newLayout = dst_tex->layout;

View file

@ -60,6 +60,9 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback(VkDebugU
strstr(pCallbackData->pMessage, "must be a memory object") != NULL) {
return VK_FALSE;
}
if (strstr(pCallbackData->pMessageIdName, "UNASSIGNED-CoreValidation-DrawState-ClearCmdBeforeDraw") != NULL) {
return VK_FALSE;
}
if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
strcat(prefix, "VERBOSE : ");

View file

@ -298,6 +298,7 @@ void GeometryInstance::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "cast_shadow", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"), "set_cast_shadows_setting", "get_cast_shadows_setting");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0.01"), "set_extra_cull_margin", "get_extra_cull_margin");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "use_in_baked_light"), "set_flag", "get_flag", FLAG_USE_BAKED_LIGHT);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "use_dynamic_gi"), "set_flag", "get_flag", FLAG_USE_DYNAMIC_GI);
ADD_GROUP("LOD", "lod_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_min_distance", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_min_distance", "get_lod_min_distance");
@ -313,6 +314,7 @@ void GeometryInstance::_bind_methods() {
BIND_ENUM_CONSTANT(SHADOW_CASTING_SETTING_SHADOWS_ONLY);
BIND_ENUM_CONSTANT(FLAG_USE_BAKED_LIGHT);
BIND_ENUM_CONSTANT(FLAG_USE_DYNAMIC_GI);
BIND_ENUM_CONSTANT(FLAG_DRAW_NEXT_FRAME_IF_VISIBLE);
BIND_ENUM_CONSTANT(FLAG_MAX);
}

View file

@ -87,6 +87,7 @@ class GeometryInstance : public VisualInstance {
public:
enum Flags {
FLAG_USE_BAKED_LIGHT = VS::INSTANCE_FLAG_USE_BAKED_LIGHT,
FLAG_USE_DYNAMIC_GI = VS::INSTANCE_FLAG_USE_DYNAMIC_GI,
FLAG_DRAW_NEXT_FRAME_IF_VISIBLE = VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE,
FLAG_MAX = VS::INSTANCE_FLAG_MAX,
};

View file

@ -140,7 +140,8 @@ public:
bool mirror : 8;
bool receive_shadows : 8;
bool visible : 8;
bool baked_light : 4; //this flag is only to know if it actually did use baked light
bool baked_light : 2; //this flag is only to know if it actually did use baked light
bool dynamic_gi : 2; //this flag is only to know if it actually did use baked light
bool redraw_if_visible : 4;
float depth; //used for sorting
@ -151,6 +152,9 @@ public:
RID lightmap;
Vector<Color> lightmap_capture_data; //in a array (12 values) to avoid wasting space if unused. Alpha is unused, but needed to send to shader
AABB aabb;
AABB transformed_aabb;
virtual void dependency_deleted(RID p_dependency) = 0;
virtual void dependency_changed(bool p_aabb, bool p_dependencies) = 0;
@ -204,6 +208,7 @@ public:
layer_mask = 1;
instance_version = 0;
baked_light = false;
dynamic_gi = false;
redraw_if_visible = false;
lightmap_capture = NULL;
}
@ -233,11 +238,12 @@ public:
virtual RID gi_probe_instance_create(RID p_gi_probe) = 0;
virtual void gi_probe_instance_set_transform_to_data(RID p_probe, const Transform &p_xform) = 0;
virtual bool gi_probe_needs_update(RID p_probe) const = 0;
virtual void gi_probe_update(RID p_probe, const Vector<RID> &p_light_instances) = 0;
virtual void gi_probe_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, int p_dynamic_object_count, InstanceBase **p_dynamic_objects) = 0;
virtual void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID p_environment, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) = 0;
virtual void render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count) = 0;
virtual void render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) = 0;
virtual void set_scene_pass(uint64_t p_pass) = 0;
virtual void set_time(double p_time) = 0;

View file

@ -308,9 +308,11 @@ void RasterizerSceneForwardRD::ShaderData::set_code(const String &p_code) {
if (depth_draw == DEPTH_DRAW_OPAQUE) {
depth_stencil.enable_depth_write = false; //alpha does not draw depth
}
} else if (uses_depth_pre_pass && (k == SHADER_VERSION_DEPTH_PASS || k == SHADER_VERSION_DEPTH_PASS_DP || k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL || k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS)) {
} else if (uses_depth_pre_pass && (k == SHADER_VERSION_DEPTH_PASS || k == SHADER_VERSION_DEPTH_PASS_DP || k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL || k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS || k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL)) {
if (k == SHADER_VERSION_DEPTH_PASS || k == SHADER_VERSION_DEPTH_PASS_DP) {
//none, blend state contains nothing
} else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) {
blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way
} else {
blend_state = blend_state_opaque; //writes to normal and roughness in opaque way
}
@ -326,6 +328,9 @@ void RasterizerSceneForwardRD::ShaderData::set_code(const String &p_code) {
//none, leave empty
} else if (k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL || k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS) {
blend_state = blend_state_opaque; //writes to normal and roughness in opaque way
} else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) {
blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way
} else {
//specular write
blend_state = blend_state_opaque_specular;
@ -601,7 +606,7 @@ bool RasterizerSceneForwardRD::free(RID p_rid) {
return false;
}
void RasterizerSceneForwardRD::_fill_instances(RenderList::Element **p_elements, int p_element_count) {
void RasterizerSceneForwardRD::_fill_instances(RenderList::Element **p_elements, int p_element_count, bool p_for_depth) {
for (int i = 0; i < p_element_count; i++) {
@ -637,6 +642,10 @@ void RasterizerSceneForwardRD::_fill_instances(RenderList::Element **p_elements,
}
}
if (p_for_depth) {
id.gi_offset = 0xFFFFFFFF;
continue;
}
//forward
uint32_t reflection_count = 0;
@ -850,6 +859,9 @@ void RasterizerSceneForwardRD::_render_list(RenderingDevice::DrawListID p_draw_l
case PASS_MODE_DEPTH_NORMAL_ROUGHNESS: {
shader_version = SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS;
} break;
case PASS_MODE_DEPTH_MATERIAL: {
shader_version = SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL;
} break;
}
RenderPipelineVertexFormatCacheRD *pipeline = nullptr;
@ -939,12 +951,12 @@ void RasterizerSceneForwardRD::_render_list(RenderingDevice::DrawListID p_draw_l
}
}
void RasterizerSceneForwardRD::_setup_environment(RID p_render_target, RID p_environment, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, RID p_reflection_probe, bool p_no_fog, const Size2 &p_screen_pixel_size, RID p_shadow_atlas) {
void RasterizerSceneForwardRD::_setup_environment(RID p_render_target, RID p_environment, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, RID p_reflection_probe, bool p_no_fog, const Size2 &p_screen_pixel_size, RID p_shadow_atlas, bool p_flip_y) {
//CameraMatrix projection = p_cam_projection;
//projection.flip_y(); // Vulkan and modern APIs use Y-Down
CameraMatrix correction;
correction.set_depth_correction(!p_reflection_probe.is_valid());
correction.set_depth_correction(p_flip_y);
CameraMatrix projection = correction * p_cam_projection;
//store camera into ubo
@ -1236,7 +1248,7 @@ void RasterizerSceneForwardRD::_add_geometry_with_material(InstanceBase *p_insta
return;
}
if (!p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_pre_pass) {
if (p_pass_mode != PASS_MODE_DEPTH_MATERIAL && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_pre_pass) {
//shader does not use discard and does not write a vertex position, use generic material
if (p_pass_mode == PASS_MODE_SHADOW || p_pass_mode == PASS_MODE_DEPTH) {
p_material = (MaterialData *)storage->material_get_data(default_material, RasterizerStorageRD::SHADER_TYPE_3D);
@ -1837,7 +1849,7 @@ void RasterizerSceneForwardRD::_render_scene(RenderBufferData *p_buffer_data, co
_setup_lights(p_light_cull_result, p_light_cull_count, p_cam_transform.affine_inverse(), p_shadow_atlas, using_shadows);
_setup_reflections(p_reflection_probe_cull_result, p_reflection_probe_cull_count, p_cam_transform.affine_inverse(), p_environment);
_setup_gi_probes(p_gi_probe_cull_result, p_gi_probe_cull_count, p_cam_transform);
_setup_environment(render_target, p_environment, p_cam_projection, p_cam_transform, p_reflection_probe, p_reflection_probe.is_valid(), screen_pixel_size, p_shadow_atlas);
_setup_environment(render_target, p_environment, p_cam_projection, p_cam_transform, p_reflection_probe, p_reflection_probe.is_valid(), screen_pixel_size, p_shadow_atlas, !p_reflection_probe.is_valid());
render_list.clear();
_fill_render_list(p_cull_result, p_cull_count, PASS_MODE_COLOR, render_buffer == nullptr);
@ -1896,7 +1908,7 @@ void RasterizerSceneForwardRD::_render_scene(RenderBufferData *p_buffer_data, co
render_list.sort_by_key(false);
_fill_instances(render_list.elements, render_list.element_count);
_fill_instances(render_list.elements, render_list.element_count, false);
bool can_continue = true; //unless the middle buffers are needed
bool debug_giprobes = debug_draw == VS::VIEWPORT_DEBUG_DRAW_GI_PROBE_ALBEDO || debug_draw == VS::VIEWPORT_DEBUG_DRAW_GI_PROBE_LIGHTING || debug_draw == VS::VIEWPORT_DEBUG_DRAW_GI_PROBE_EMISSION;
@ -1952,7 +1964,7 @@ void RasterizerSceneForwardRD::_render_scene(RenderBufferData *p_buffer_data, co
render_list.sort_by_reverse_depth_and_priority(true);
_fill_instances(&render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count);
_fill_instances(&render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, false);
{
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(alpha_framebuffer, can_continue ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, can_continue ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ);
@ -2087,7 +2099,7 @@ void RasterizerSceneForwardRD::_render_shadow(RID p_framebuffer, InstanceBase **
scene_state.ubo.z_far = p_zfar;
scene_state.ubo.dual_paraboloid_side = p_use_dp_flip ? -1 : 1;
_setup_environment(RID(), RID(), p_projection, p_transform, RID(), true, Vector2(1, 1), RID());
_setup_environment(RID(), RID(), p_projection, p_transform, RID(), true, Vector2(1, 1), RID(), true);
render_list.clear();
@ -2101,7 +2113,7 @@ void RasterizerSceneForwardRD::_render_shadow(RID p_framebuffer, InstanceBase **
render_list.sort_by_key(false);
_fill_instances(render_list.elements, render_list.element_count);
_fill_instances(render_list.elements, render_list.element_count, true);
{
//regular forward for now
@ -2111,6 +2123,47 @@ void RasterizerSceneForwardRD::_render_shadow(RID p_framebuffer, InstanceBase **
}
}
void RasterizerSceneForwardRD::_render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) {
RENDER_TIMESTAMP("Setup Rendering Shadow");
_update_render_base_uniform_set();
render_pass++;
scene_state.ubo.shadow_z_offset = 0;
scene_state.ubo.shadow_z_slope_scale = 0;
scene_state.ubo.z_far = 0;
scene_state.ubo.dual_paraboloid_side = 0;
_setup_environment(RID(), RID(), p_cam_projection, p_cam_transform, RID(), true, Vector2(1, 1), RID(), false);
render_list.clear();
PassMode pass_mode = PASS_MODE_DEPTH_MATERIAL;
_fill_render_list(p_cull_result, p_cull_count, pass_mode, true);
_setup_render_pass_uniform_set(RID(), RID(), RID(), RID(), RID(), RID(), RID());
RENDER_TIMESTAMP("Render Material");
render_list.sort_by_key(false);
_fill_instances(render_list.elements, render_list.element_count, true);
{
//regular forward for now
Vector<Color> clear;
clear.push_back(Color(0, 0, 0, 0));
clear.push_back(Color(0, 0, 0, 0));
clear.push_back(Color(0, 0, 0, 0));
clear.push_back(Color(0, 0, 0, 0));
clear.push_back(Color(0, 0, 0, 0));
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, clear, 1.0, 0, p_region);
_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), render_list.elements, render_list.element_count, true, pass_mode, true);
RD::get_singleton()->draw_list_end();
}
}
void RasterizerSceneForwardRD::_update_render_base_uniform_set() {
if (render_base_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set) || gi_probe_slots_are_dirty()) {
@ -2425,6 +2478,7 @@ RasterizerSceneForwardRD::RasterizerSceneForwardRD(RasterizerStorageRD *p_storag
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n");
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define ENABLE_WRITE_NORMAL_BUFFER\n");
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define ENABLE_WRITE_NORMAL_ROUGHNESS_BUFFER\n");
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n");
shader_versions.push_back("");
shader_versions.push_back("\n#define MODE_MULTIPLE_RENDER_TARGETS\n");
shader_versions.push_back("\n#define USE_VOXEL_CONE_TRACING\n");

View file

@ -45,6 +45,7 @@ class RasterizerSceneForwardRD : public RasterizerSceneRD {
SHADER_VERSION_DEPTH_PASS_DP,
SHADER_VERSION_DEPTH_PASS_WITH_NORMAL,
SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS,
SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL,
SHADER_VERSION_COLOR_PASS,
SHADER_VERSION_COLOR_PASS_WITH_SEPARATE_SPECULAR,
SHADER_VERSION_VCT_COLOR_PASS,
@ -519,14 +520,15 @@ class RasterizerSceneForwardRD : public RasterizerSceneRD {
PASS_MODE_DEPTH,
PASS_MODE_DEPTH_NORMAL,
PASS_MODE_DEPTH_NORMAL_ROUGHNESS,
PASS_MODE_DEPTH_MATERIAL,
};
void _setup_environment(RID p_render_target, RID p_environment, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, RID p_reflection_probe, bool p_no_fog, const Size2 &p_screen_pixel_size, RID p_shadow_atlas);
void _setup_environment(RID p_render_target, RID p_environment, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, RID p_reflection_probe, bool p_no_fog, const Size2 &p_screen_pixel_size, RID p_shadow_atlas, bool p_flip_y);
void _setup_lights(RID *p_light_cull_result, int p_light_cull_count, const Transform &p_camera_inverse_transform, RID p_shadow_atlas, bool p_using_shadows);
void _setup_reflections(RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, const Transform &p_camera_inverse_transform, RID p_environment);
void _setup_gi_probes(RID *p_gi_probe_probe_cull_result, int p_gi_probe_probe_cull_count, const Transform &p_camera_transform);
void _fill_instances(RenderList::Element **p_elements, int p_element_count);
void _fill_instances(RenderList::Element **p_elements, int p_element_count, bool p_for_depth);
void _render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderList::Element **p_elements, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, bool p_no_gi);
_FORCE_INLINE_ void _add_geometry(InstanceBase *p_instance, uint32_t p_surface, RID p_material, PassMode p_pass_mode, uint32_t p_geometry_index);
_FORCE_INLINE_ void _add_geometry_with_material(InstanceBase *p_instance, uint32_t p_surface, MaterialData *p_material, RID p_material_rid, PassMode p_pass_mode, uint32_t p_geometry_index);
@ -540,6 +542,7 @@ class RasterizerSceneForwardRD : public RasterizerSceneRD {
protected:
virtual void _render_scene(RenderBufferData *p_buffer_data, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID p_environment, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass);
virtual void _render_shadow(RID p_framebuffer, InstanceBase **p_cull_result, int p_cull_count, const CameraMatrix &p_projection, const Transform &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip);
virtual void _render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region);
public:
virtual void set_time(double p_time);

View file

@ -1245,7 +1245,7 @@ bool RasterizerSceneRD::gi_probe_needs_update(RID p_probe) const {
return gi_probe->last_probe_version != storage->gi_probe_get_version(gi_probe->probe);
}
void RasterizerSceneRD::gi_probe_update(RID p_probe, const Vector<RID> &p_light_instances) {
void RasterizerSceneRD::gi_probe_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, int p_dynamic_object_count, InstanceBase **p_dynamic_objects) {
GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe);
ERR_FAIL_COND(!gi_probe);
@ -1266,6 +1266,13 @@ void RasterizerSceneRD::gi_probe_update(RID p_probe, const Vector<RID> &p_light_
gi_probe->mipmaps.clear();
}
for (int i = 0; i < gi_probe->dynamic_maps.size(); i++) {
RD::get_singleton()->free(gi_probe->dynamic_maps[i].texture);
RD::get_singleton()->free(gi_probe->dynamic_maps[i].depth);
}
gi_probe->dynamic_maps.clear();
Vector3i octree_size = storage->gi_probe_get_octree_size(gi_probe->probe);
if (octree_size != Vector3i()) {
@ -1354,6 +1361,21 @@ void RasterizerSceneRD::gi_probe_update(RID p_probe, const Vector<RID> &p_light_
u.ids.push_back(gi_probe->write_buffer);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 9;
u.ids.push_back(storage->gi_probe_get_sdf_texture(gi_probe->probe));
uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_SAMPLER;
u.binding = 10;
u.ids.push_back(storage->sampler_rd_get_default(VS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, VS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
uniforms.push_back(u);
}
{
Vector<RD::Uniform> copy_uniforms = uniforms;
if (i == 0) {
@ -1376,13 +1398,6 @@ void RasterizerSceneRD::gi_probe_update(RID p_probe, const Vector<RID> &p_light_
u.ids.push_back(gi_probe->texture);
copy_uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_SAMPLER;
u.binding = 6;
u.ids.push_back(storage->sampler_rd_get_default(VS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, VS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
copy_uniforms.push_back(u);
}
if (gi_probe_use_anisotropy) {
{
@ -1436,138 +1451,579 @@ void RasterizerSceneRD::gi_probe_update(RID p_probe, const Vector<RID> &p_light_
gi_probe->mipmaps.push_back(mipmap);
}
{
uint32_t dynamic_map_size = MAX(MAX(octree_size.x, octree_size.y), octree_size.z);
uint32_t oversample = nearest_power_of_2_templated(4);
int mipmap_index = 0;
while (mipmap_index < gi_probe->mipmaps.size()) {
GIProbeInstance::DynamicMap dmap;
if (oversample > 0) {
dmap.size = dynamic_map_size * (1 << oversample);
dmap.mipmap = -1;
oversample--;
} else {
dmap.size = dynamic_map_size >> mipmap_index;
dmap.mipmap = mipmap_index;
mipmap_index++;
}
RD::TextureFormat dtf;
dtf.width = dmap.size;
dtf.height = dmap.size;
dtf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
dtf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT;
if (gi_probe->dynamic_maps.size() == 0) {
dtf.usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
}
dmap.texture = RD::get_singleton()->texture_create(dtf, RD::TextureView());
if (gi_probe->dynamic_maps.size() == 0) {
//render depth for first one
dtf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D32_SFLOAT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D32_SFLOAT : RD::DATA_FORMAT_X8_D24_UNORM_PACK32;
dtf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
dmap.fb_depth = RD::get_singleton()->texture_create(dtf, RD::TextureView());
}
//just use depth as-is
dtf.format = RD::DATA_FORMAT_R32_SFLOAT;
dtf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
dmap.depth = RD::get_singleton()->texture_create(dtf, RD::TextureView());
if (gi_probe->dynamic_maps.size() == 0) {
dtf.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
dtf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
dmap.albedo = RD::get_singleton()->texture_create(dtf, RD::TextureView());
dmap.normal = RD::get_singleton()->texture_create(dtf, RD::TextureView());
dmap.orm = RD::get_singleton()->texture_create(dtf, RD::TextureView());
Vector<RID> fb;
fb.push_back(dmap.albedo);
fb.push_back(dmap.normal);
fb.push_back(dmap.orm);
fb.push_back(dmap.texture); //emission
fb.push_back(dmap.depth);
fb.push_back(dmap.fb_depth);
dmap.fb = RD::get_singleton()->framebuffer_create(fb);
{
Vector<RD::Uniform> uniforms;
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
u.binding = 3;
u.ids.push_back(gi_probe_lights_uniform);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 5;
u.ids.push_back(dmap.albedo);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 6;
u.ids.push_back(dmap.normal);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 7;
u.ids.push_back(dmap.orm);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 8;
u.ids.push_back(dmap.fb_depth);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 9;
u.ids.push_back(storage->gi_probe_get_sdf_texture(gi_probe->probe));
uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_SAMPLER;
u.binding = 10;
u.ids.push_back(storage->sampler_rd_get_default(VS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, VS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 11;
u.ids.push_back(dmap.texture);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 12;
u.ids.push_back(dmap.depth);
uniforms.push_back(u);
}
dmap.uniform_set = RD::get_singleton()->uniform_set_create(uniforms, giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_DYNAMIC_OBJECT_LIGHTING], 0);
}
} else {
bool plot = dmap.mipmap >= 0;
bool write = dmap.mipmap < (gi_probe->mipmaps.size() - 1);
Vector<RD::Uniform> uniforms;
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 5;
u.ids.push_back(gi_probe->dynamic_maps[gi_probe->dynamic_maps.size() - 1].texture);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 6;
u.ids.push_back(gi_probe->dynamic_maps[gi_probe->dynamic_maps.size() - 1].depth);
uniforms.push_back(u);
}
if (write) {
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 7;
u.ids.push_back(dmap.texture);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 8;
u.ids.push_back(dmap.depth);
uniforms.push_back(u);
}
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 9;
u.ids.push_back(storage->gi_probe_get_sdf_texture(gi_probe->probe));
uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_SAMPLER;
u.binding = 10;
u.ids.push_back(storage->sampler_rd_get_default(VS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, VS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
uniforms.push_back(u);
}
if (plot) {
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 11;
u.ids.push_back(gi_probe->mipmaps[dmap.mipmap].texture);
uniforms.push_back(u);
}
if (gi_probe_is_anisotropic()) {
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 12;
u.ids.push_back(gi_probe->mipmaps[dmap.mipmap].anisotropy[0]);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 13;
u.ids.push_back(gi_probe->mipmaps[dmap.mipmap].anisotropy[1]);
uniforms.push_back(u);
}
}
}
dmap.uniform_set = RD::get_singleton()->uniform_set_create(uniforms, giprobe_lighting_shader_version_shaders[(write && plot) ? GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE_PLOT : write ? GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE : GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_PLOT], 0);
}
gi_probe->dynamic_maps.push_back(dmap);
}
}
}
gi_probe->last_probe_data_version = data_version;
gi_probe_slots_dirty = true;
p_update_light_instances = true; //just in case
}
// UDPDATE TIME
uint32_t light_count = MIN(gi_probe_max_lights, (uint32_t)p_light_instances.size());
{
Transform to_cell = storage->gi_probe_get_to_cell_xform(gi_probe->probe);
Transform to_probe_xform = (gi_probe->transform * to_cell.affine_inverse()).affine_inverse();
//update lights
for (uint32_t i = 0; i < light_count; i++) {
GIProbeLight &l = gi_probe_lights[i];
RID light_instance = p_light_instances[i];
RID light = light_instance_get_base_light(light_instance);
l.type = storage->light_get_type(light);
l.attenuation = storage->light_get_param(light, VS::LIGHT_PARAM_ATTENUATION);
l.energy = storage->light_get_param(light, VS::LIGHT_PARAM_ENERGY) * storage->light_get_param(light, VS::LIGHT_PARAM_INDIRECT_ENERGY);
l.radius = to_cell.basis.xform(Vector3(storage->light_get_param(light, VS::LIGHT_PARAM_RANGE), 0, 0)).length();
Color color = storage->light_get_color(light).to_linear();
l.color[0] = color.r;
l.color[1] = color.g;
l.color[2] = color.b;
l.spot_angle_radians = Math::deg2rad(storage->light_get_param(light, VS::LIGHT_PARAM_SPOT_ANGLE));
l.spot_attenuation = storage->light_get_param(light, VS::LIGHT_PARAM_SPOT_ATTENUATION);
Transform xform = light_instance_get_base_transform(light_instance);
Vector3 pos = to_probe_xform.xform(xform.origin);
Vector3 dir = to_probe_xform.basis.xform(-xform.basis.get_axis(2)).normalized();
l.position[0] = pos.x;
l.position[1] = pos.y;
l.position[2] = pos.z;
l.direction[0] = dir.x;
l.direction[1] = dir.y;
l.direction[2] = dir.z;
l.has_shadow = storage->light_has_shadow(light);
}
RD::get_singleton()->buffer_update(gi_probe_lights_uniform, 0, sizeof(GIProbeLight) * light_count, gi_probe_lights, true);
if (gi_probe->has_dynamic_object_data) {
//if it has dynamic object data, it needs to be cleared
RD::get_singleton()->texture_clear(gi_probe->texture, Color(0, 0, 0, 0), 0, gi_probe->mipmaps.size(), 0, 1, true);
}
// PROCESS MIPMAPS
if (gi_probe->mipmaps.size()) {
//can update mipmaps
uint32_t light_count = 0;
Vector3i probe_size = storage->gi_probe_get_octree_size(gi_probe->probe);
if (p_update_light_instances || p_dynamic_object_count > 0) {
GIProbePushConstant push_constant;
light_count = MIN(gi_probe_max_lights, (uint32_t)p_light_instances.size());
push_constant.limits[0] = probe_size.x;
push_constant.limits[1] = probe_size.y;
push_constant.limits[2] = probe_size.z;
push_constant.stack_size = gi_probe->mipmaps.size();
push_constant.emission_scale = 1.0;
push_constant.propagation = storage->gi_probe_get_propagation(gi_probe->probe);
push_constant.dynamic_range = storage->gi_probe_get_dynamic_range(gi_probe->probe);
push_constant.light_count = light_count;
push_constant.aniso_strength = storage->gi_probe_get_anisotropy_strength(gi_probe->probe);
{
Transform to_cell = storage->gi_probe_get_to_cell_xform(gi_probe->probe);
Transform to_probe_xform = (gi_probe->transform * to_cell.affine_inverse()).affine_inverse();
//update lights
/* print_line("probe update to version " + itos(gi_probe->last_probe_version));
print_line("propagation " + rtos(push_constant.propagation));
print_line("dynrange " + rtos(push_constant.dynamic_range));
*/
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
for (uint32_t i = 0; i < light_count; i++) {
GIProbeLight &l = gi_probe_lights[i];
RID light_instance = p_light_instances[i];
RID light = light_instance_get_base_light(light_instance);
int passes = storage->gi_probe_is_using_two_bounces(gi_probe->probe) ? 2 : 1;
int wg_size = 64;
int wg_limit_x = RD::get_singleton()->limit_get(RD::LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_X);
l.type = storage->light_get_type(light);
l.attenuation = storage->light_get_param(light, VS::LIGHT_PARAM_ATTENUATION);
l.energy = storage->light_get_param(light, VS::LIGHT_PARAM_ENERGY) * storage->light_get_param(light, VS::LIGHT_PARAM_INDIRECT_ENERGY);
l.radius = to_cell.basis.xform(Vector3(storage->light_get_param(light, VS::LIGHT_PARAM_RANGE), 0, 0)).length();
Color color = storage->light_get_color(light).to_linear();
l.color[0] = color.r;
l.color[1] = color.g;
l.color[2] = color.b;
for (int pass = 0; pass < passes; pass++) {
l.spot_angle_radians = Math::deg2rad(storage->light_get_param(light, VS::LIGHT_PARAM_SPOT_ANGLE));
l.spot_attenuation = storage->light_get_param(light, VS::LIGHT_PARAM_SPOT_ATTENUATION);
for (int i = 0; i < gi_probe->mipmaps.size(); i++) {
if (i == 0) {
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[pass == 0 ? GI_PROBE_SHADER_VERSION_COMPUTE_LIGHT : GI_PROBE_SHADER_VERSION_COMPUTE_SECOND_BOUNCE]);
} else if (i == 1) {
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_COMPUTE_MIPMAP]);
}
Transform xform = light_instance_get_base_transform(light_instance);
Vector3 pos = to_probe_xform.xform(xform.origin);
Vector3 dir = to_probe_xform.basis.xform(-xform.basis.get_axis(2)).normalized();
l.position[0] = pos.x;
l.position[1] = pos.y;
l.position[2] = pos.z;
l.direction[0] = dir.x;
l.direction[1] = dir.y;
l.direction[2] = dir.z;
l.has_shadow = storage->light_has_shadow(light);
}
RD::get_singleton()->buffer_update(gi_probe_lights_uniform, 0, sizeof(GIProbeLight) * light_count, gi_probe_lights, true);
}
}
if (gi_probe->has_dynamic_object_data || p_update_light_instances || p_dynamic_object_count) {
// PROCESS MIPMAPS
if (gi_probe->mipmaps.size()) {
//can update mipmaps
Vector3i probe_size = storage->gi_probe_get_octree_size(gi_probe->probe);
GIProbePushConstant push_constant;
push_constant.limits[0] = probe_size.x;
push_constant.limits[1] = probe_size.y;
push_constant.limits[2] = probe_size.z;
push_constant.stack_size = gi_probe->mipmaps.size();
push_constant.emission_scale = 1.0;
push_constant.propagation = storage->gi_probe_get_propagation(gi_probe->probe);
push_constant.dynamic_range = storage->gi_probe_get_dynamic_range(gi_probe->probe);
push_constant.light_count = light_count;
push_constant.aniso_strength = storage->gi_probe_get_anisotropy_strength(gi_probe->probe);
/* print_line("probe update to version " + itos(gi_probe->last_probe_version));
print_line("propagation " + rtos(push_constant.propagation));
print_line("dynrange " + rtos(push_constant.dynamic_range));
*/
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
int passes;
if (p_update_light_instances) {
passes = storage->gi_probe_is_using_two_bounces(gi_probe->probe) ? 2 : 1;
} else {
passes = 1; //only re-blitting is necessary
}
int wg_size = 64;
int wg_limit_x = RD::get_singleton()->limit_get(RD::LIMIT_MAX_COMPUTE_WORKGROUP_COUNT_X);
for (int pass = 0; pass < passes; pass++) {
if (p_update_light_instances) {
for (int i = 0; i < gi_probe->mipmaps.size(); i++) {
if (i == 0) {
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[pass == 0 ? GI_PROBE_SHADER_VERSION_COMPUTE_LIGHT : GI_PROBE_SHADER_VERSION_COMPUTE_SECOND_BOUNCE]);
} else if (i == 1) {
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_COMPUTE_MIPMAP]);
}
if (pass == 1 || i > 0) {
RD::get_singleton()->compute_list_add_barrier(compute_list); //wait til previous step is done
}
if (pass == 0 || i > 0) {
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi_probe->mipmaps[i].uniform_set, 0);
} else {
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi_probe->mipmaps[i].second_bounce_uniform_set, 0);
}
push_constant.cell_offset = gi_probe->mipmaps[i].cell_offset;
push_constant.cell_count = gi_probe->mipmaps[i].cell_count;
int wg_todo = (gi_probe->mipmaps[i].cell_count - 1) / wg_size + 1;
while (wg_todo) {
int wg_count = MIN(wg_todo, wg_limit_x);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbePushConstant));
RD::get_singleton()->compute_list_dispatch(compute_list, wg_count, 1, 1);
wg_todo -= wg_count;
push_constant.cell_offset += wg_count * wg_size;
}
}
if (pass == 1 || i > 0) {
RD::get_singleton()->compute_list_add_barrier(compute_list); //wait til previous step is done
}
if (pass == 0 || i > 0) {
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi_probe->mipmaps[i].uniform_set, 0);
} else {
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi_probe->mipmaps[i].second_bounce_uniform_set, 0);
}
push_constant.cell_offset = gi_probe->mipmaps[i].cell_offset;
push_constant.cell_count = gi_probe->mipmaps[i].cell_count;
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_WRITE_TEXTURE]);
int wg_todo = (gi_probe->mipmaps[i].cell_count - 1) / wg_size + 1;
while (wg_todo) {
int wg_count = MIN(wg_todo, wg_limit_x);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbePushConstant));
RD::get_singleton()->compute_list_dispatch(compute_list, wg_count, 1, 1);
wg_todo -= wg_count;
push_constant.cell_offset += wg_count * wg_size;
for (int i = 0; i < gi_probe->mipmaps.size(); i++) {
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi_probe->mipmaps[i].write_uniform_set, 0);
push_constant.cell_offset = gi_probe->mipmaps[i].cell_offset;
push_constant.cell_count = gi_probe->mipmaps[i].cell_count;
int wg_todo = (gi_probe->mipmaps[i].cell_count - 1) / wg_size + 1;
while (wg_todo) {
int wg_count = MIN(wg_todo, wg_limit_x);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbePushConstant));
RD::get_singleton()->compute_list_dispatch(compute_list, wg_count, 1, 1);
wg_todo -= wg_count;
push_constant.cell_offset += wg_count * wg_size;
}
}
}
RD::get_singleton()->compute_list_add_barrier(compute_list); //wait til previous step is done
RD::get_singleton()->compute_list_end();
}
}
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_WRITE_TEXTURE]);
gi_probe->has_dynamic_object_data = false; //clear until dynamic object data is used again
for (int i = 0; i < gi_probe->mipmaps.size(); i++) {
if (p_dynamic_object_count && gi_probe->dynamic_maps.size()) {
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi_probe->mipmaps[i].write_uniform_set, 0);
Vector3i octree_size = storage->gi_probe_get_octree_size(gi_probe->probe);
int multiplier = gi_probe->dynamic_maps[0].size / MAX(MAX(octree_size.x, octree_size.y), octree_size.z);
push_constant.cell_offset = gi_probe->mipmaps[i].cell_offset;
push_constant.cell_count = gi_probe->mipmaps[i].cell_count;
Transform oversample_scale;
oversample_scale.basis.scale(Vector3(multiplier, multiplier, multiplier));
int wg_todo = (gi_probe->mipmaps[i].cell_count - 1) / wg_size + 1;
while (wg_todo) {
int wg_count = MIN(wg_todo, wg_limit_x);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbePushConstant));
RD::get_singleton()->compute_list_dispatch(compute_list, wg_count, 1, 1);
wg_todo -= wg_count;
push_constant.cell_offset += wg_count * wg_size;
Transform to_cell = oversample_scale * storage->gi_probe_get_to_cell_xform(gi_probe->probe);
Transform to_world_xform = gi_probe->transform * to_cell.affine_inverse();
Transform to_probe_xform = to_world_xform.affine_inverse();
AABB probe_aabb(Vector3(), octree_size);
//this could probably be better parallelized in compute..
for (int i = 0; i < p_dynamic_object_count; i++) {
InstanceBase *instance = p_dynamic_objects[i];
//not used, so clear
instance->depth_layer = 0;
instance->depth = 0;
//transform aabb to giprobe
AABB aabb = (to_probe_xform * instance->transform).xform(instance->aabb);
//this needs to wrap to grid resolution to avoid jitter
//also extend margin a bit just in case
Vector3i begin = aabb.position - Vector3i(1, 1, 1);
Vector3i end = aabb.position + aabb.size + Vector3i(1, 1, 1);
for (int j = 0; j < 3; j++) {
if ((end[j] - begin[j]) & 1) {
end[j]++; //for half extents split, it needs to be even
}
begin[j] = MAX(begin[j], 0);
end[j] = MIN(end[j], octree_size[j] * multiplier);
}
//aabb = aabb.intersection(probe_aabb); //intersect
aabb.position = begin;
aabb.size = end - begin;
//print_line("aabb: " + aabb);
for (int j = 0; j < 6; j++) {
//if (j != 0 && j != 3) {
// continue;
//}
static const Vector3 render_z[6] = {
Vector3(1, 0, 0),
Vector3(0, 1, 0),
Vector3(0, 0, 1),
Vector3(-1, 0, 0),
Vector3(0, -1, 0),
Vector3(0, 0, -1),
};
static const Vector3 render_up[6] = {
Vector3(0, 1, 0),
Vector3(0, 0, 1),
Vector3(0, 1, 0),
Vector3(0, 1, 0),
Vector3(0, 0, 1),
Vector3(0, 1, 0),
};
Vector3 render_dir = render_z[j];
Vector3 up_dir = render_up[j];
Vector3 center = aabb.position + aabb.size * 0.5;
Transform xform;
xform.set_look_at(center - aabb.size * 0.5 * render_dir, center, up_dir);
Vector3 x_dir = xform.basis.get_axis(0).abs();
int x_axis = int(Vector3(0, 1, 2).dot(x_dir));
Vector3 y_dir = xform.basis.get_axis(1).abs();
int y_axis = int(Vector3(0, 1, 2).dot(y_dir));
Vector3 z_dir = -xform.basis.get_axis(2);
int z_axis = int(Vector3(0, 1, 2).dot(z_dir.abs()));
Rect2i rect(aabb.position[x_axis], aabb.position[y_axis], aabb.size[x_axis], aabb.size[y_axis]);
bool x_flip = bool(Vector3(1, 1, 1).dot(xform.basis.get_axis(0)) < 0);
bool y_flip = bool(Vector3(1, 1, 1).dot(xform.basis.get_axis(1)) < 0);
bool z_flip = bool(Vector3(1, 1, 1).dot(xform.basis.get_axis(2)) > 0);
CameraMatrix cm;
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]);
_render_material(to_world_xform * xform, cm, true, &instance, 1, gi_probe->dynamic_maps[0].fb, Rect2i(Vector2i(), rect.size));
GIProbeDynamicPushConstant push_constant;
zeromem(&push_constant, sizeof(GIProbeDynamicPushConstant));
push_constant.limits[0] = octree_size.x;
push_constant.limits[1] = octree_size.y;
push_constant.limits[2] = octree_size.z;
push_constant.light_count = p_light_instances.size();
push_constant.x_dir[0] = x_dir[0];
push_constant.x_dir[1] = x_dir[1];
push_constant.x_dir[2] = x_dir[2];
push_constant.y_dir[0] = y_dir[0];
push_constant.y_dir[1] = y_dir[1];
push_constant.y_dir[2] = y_dir[2];
push_constant.z_dir[0] = z_dir[0];
push_constant.z_dir[1] = z_dir[1];
push_constant.z_dir[2] = z_dir[2];
push_constant.z_base = xform.origin[z_axis];
push_constant.z_sign = (z_flip ? -1.0 : 1.0);
push_constant.pos_multiplier = float(1.0) / multiplier;
push_constant.dynamic_range = storage->gi_probe_get_dynamic_range(gi_probe->probe);
push_constant.flip_x = x_flip;
push_constant.flip_y = y_flip;
push_constant.rect_pos[0] = rect.position[0];
push_constant.rect_pos[1] = rect.position[1];
push_constant.rect_size[0] = rect.size[0];
push_constant.rect_size[1] = rect.size[1];
push_constant.prev_rect_ofs[0] = 0;
push_constant.prev_rect_ofs[1] = 0;
push_constant.prev_rect_size[0] = 0;
push_constant.prev_rect_size[1] = 0;
push_constant.keep_downsample_color = true;
//process lighting
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_DYNAMIC_OBJECT_LIGHTING]);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi_probe->dynamic_maps[0].uniform_set, 0);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbeDynamicPushConstant));
RD::get_singleton()->compute_list_dispatch(compute_list, (rect.size.x - 1) / 8 + 1, (rect.size.y - 1) / 8 + 1, 1);
//print_line("rect: " + itos(i) + ": " + rect);
for (int k = 1; k < gi_probe->dynamic_maps.size(); k++) {
// enlarge the rect if needed so all pixels fit when downscaled,
// this ensures downsampling is smooth and optimal because no pixels are left behind
//x
if (rect.position.x & 1) {
rect.size.x++;
push_constant.prev_rect_ofs[0] = 1; //this is used to ensure reading is also optimal
} else {
push_constant.prev_rect_ofs[0] = 0;
}
if (rect.size.x & 1) {
rect.size.x++;
}
rect.position.x >>= 1;
rect.size.x = MAX(1, rect.size.x >> 1);
//y
if (rect.position.y & 1) {
rect.size.y++;
push_constant.prev_rect_ofs[1] = 1;
} else {
push_constant.prev_rect_ofs[1] = 0;
}
if (rect.size.y & 1) {
rect.size.y++;
}
rect.position.y >>= 1;
rect.size.y = MAX(1, rect.size.y >> 1);
//shrink limits to ensure plot does not go outside map
if (gi_probe->dynamic_maps[i].mipmap > 0) {
for (int l = 0; l < 3; l++) {
push_constant.limits[l] = MAX(1, push_constant.limits[l] >> 1);
}
}
//print_line("rect: " + itos(i) + ": " + rect);
push_constant.rect_pos[0] = rect.position[0];
push_constant.rect_pos[1] = rect.position[1];
push_constant.prev_rect_size[0] = push_constant.rect_size[0];
push_constant.prev_rect_size[1] = push_constant.rect_size[1];
push_constant.rect_size[0] = rect.size[0];
push_constant.rect_size[1] = rect.size[1];
push_constant.keep_downsample_color = gi_probe->dynamic_maps[i].mipmap <= 0;
;
RD::get_singleton()->compute_list_add_barrier(compute_list);
if (gi_probe->dynamic_maps[k].mipmap < 0) {
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE]);
} else if (k < gi_probe->dynamic_maps.size() - 1) {
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE_PLOT]);
} else {
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_lighting_shader_version_pipelines[GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_PLOT]);
}
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, gi_probe->dynamic_maps[k].uniform_set, 0);
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GIProbeDynamicPushConstant));
RD::get_singleton()->compute_list_dispatch(compute_list, (rect.size.x - 1) / 8 + 1, (rect.size.y - 1) / 8 + 1, 1);
}
RD::get_singleton()->compute_list_end();
}
}
RD::get_singleton()->compute_list_end();
gi_probe->has_dynamic_object_data = true; //clear until dynamic object data is used again
}
gi_probe->last_probe_version = storage->gi_probe_get_version(gi_probe->probe);
@ -1584,6 +2040,7 @@ void RasterizerSceneRD::_debug_giprobe(RID p_gi_probe, RD::DrawListID p_draw_lis
CameraMatrix transform = (p_camera_with_transform * CameraMatrix(gi_probe->transform)) * CameraMatrix(storage->gi_probe_get_to_cell_xform(gi_probe->probe).affine_inverse());
int level = 0;
Vector3i octree_size = storage->gi_probe_get_octree_size(gi_probe->probe);
GIProbeDebugPushConstant push_constant;
push_constant.alpha = p_alpha;
@ -1591,7 +2048,10 @@ void RasterizerSceneRD::_debug_giprobe(RID p_gi_probe, RD::DrawListID p_draw_lis
push_constant.cell_offset = gi_probe->mipmaps[level].cell_offset;
push_constant.level = level;
int cell_count = gi_probe->mipmaps[level].cell_count;
push_constant.bounds[0] = octree_size.x >> level;
push_constant.bounds[1] = octree_size.y >> level;
push_constant.bounds[2] = octree_size.z >> level;
push_constant.pad = 0;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
@ -1643,8 +2103,15 @@ void RasterizerSceneRD::_debug_giprobe(RID p_gi_probe, RD::DrawListID p_draw_lis
}
}
int cell_count;
if (!p_emission && p_lighting && gi_probe->has_dynamic_object_data) {
cell_count = push_constant.bounds[0] * push_constant.bounds[1] * push_constant.bounds[2];
} else {
cell_count = gi_probe->mipmaps[level].cell_count;
}
giprobe_debug_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, giprobe_debug_shader_version_shaders[0], 0);
RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, giprobe_debug_shader_version_pipelines[p_emission ? GI_PROBE_DEBUG_EMISSION : p_lighting ? GI_PROBE_DEBUG_LIGHT : GI_PROBE_DEBUG_COLOR].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer)));
RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, giprobe_debug_shader_version_pipelines[p_emission ? GI_PROBE_DEBUG_EMISSION : p_lighting ? (gi_probe->has_dynamic_object_data ? GI_PROBE_DEBUG_LIGHT_FULL : GI_PROBE_DEBUG_LIGHT) : GI_PROBE_DEBUG_COLOR].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, giprobe_debug_uniform_set, 0);
RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(GIProbeDebugPushConstant));
RD::get_singleton()->draw_list_draw(p_draw_list, false, cell_count, 36);
@ -1875,6 +2342,11 @@ void RasterizerSceneRD::render_shadow(RID p_light, RID p_shadow_atlas, int p_pas
}
}
void RasterizerSceneRD::render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) {
_render_material(p_cam_transform, p_cam_projection, p_cam_ortogonal, p_cull_result, p_cull_count, p_framebuffer, p_region);
}
bool RasterizerSceneRD::free(RID p_rid) {
if (render_buffers_owner.owns(p_rid)) {
@ -1903,6 +2375,11 @@ bool RasterizerSceneRD::free(RID p_rid) {
RD::get_singleton()->free(gi_probe->anisotropy[1]);
}
for (int i = 0; i < gi_probe->dynamic_maps.size(); i++) {
RD::get_singleton()->free(gi_probe->dynamic_maps[i].texture);
RD::get_singleton()->free(gi_probe->dynamic_maps[i].depth);
}
gi_probe_slots.write[gi_probe->slot] = RID();
gi_probe_instance_owner.free(p_rid);
@ -2002,6 +2479,10 @@ RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) {
versions.push_back("\n#define MODE_SECOND_BOUNCE\n");
versions.push_back("\n#define MODE_UPDATE_MIPMAPS\n");
versions.push_back("\n#define MODE_WRITE_TEXTURE\n");
versions.push_back("\n#define MODE_DYNAMIC\n#define MODE_DYNAMIC_LIGHTING\n");
versions.push_back("\n#define MODE_DYNAMIC\n#define MODE_DYNAMIC_SHRINK\n#define MODE_DYNAMIC_SHRINK_WRITE\n");
versions.push_back("\n#define MODE_DYNAMIC\n#define MODE_DYNAMIC_SHRINK\n#define MODE_DYNAMIC_SHRINK_PLOT\n");
versions.push_back("\n#define MODE_DYNAMIC\n#define MODE_DYNAMIC_SHRINK\n#define MODE_DYNAMIC_SHRINK_PLOT\n#define MODE_DYNAMIC_SHRINK_WRITE\n");
giprobe_shader.initialize(versions, defines);
giprobe_lighting_shader_version = giprobe_shader.version_create();
@ -2021,6 +2502,7 @@ RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) {
versions.push_back("\n#define MODE_DEBUG_COLOR\n");
versions.push_back("\n#define MODE_DEBUG_LIGHT\n");
versions.push_back("\n#define MODE_DEBUG_EMISSION\n");
versions.push_back("\n#define MODE_DEBUG_LIGHT\n#define MODE_DEBUG_LIGHT_FULL\n");
giprobe_debug_shader.initialize(versions, defines);
giprobe_debug_shader_version = giprobe_debug_shader.version_create();

View file

@ -26,6 +26,7 @@ protected:
virtual void _render_scene(RenderBufferData *p_buffer_data, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID p_environment, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) = 0;
virtual void _render_shadow(RID p_framebuffer, InstanceBase **p_cull_result, int p_cull_count, const CameraMatrix &p_projection, const Transform &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool use_dp_flip) = 0;
virtual void _render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) = 0;
virtual void _debug_giprobe(RID p_gi_probe, RenderingDevice::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha);
@ -155,6 +156,26 @@ private:
uint32_t pad;
};
struct GIProbeDynamicPushConstant {
int32_t limits[3];
uint32_t light_count;
int32_t x_dir[3];
float z_base;
int32_t y_dir[3];
float z_sign;
int32_t z_dir[3];
float pos_multiplier;
uint32_t rect_pos[2];
uint32_t rect_size[2];
uint32_t prev_rect_ofs[2];
uint32_t prev_rect_size[2];
uint32_t flip_x;
uint32_t flip_y;
float dynamic_range;
uint32_t keep_downsample_color;
};
struct GIProbeInstance {
RID probe;
@ -174,6 +195,21 @@ private:
};
Vector<Mipmap> mipmaps;
struct DynamicMap {
RID texture; //color normally, or emission on first pass
RID fb_depth; //actual depth buffer for the first pass, float depth for later passes
RID depth; //actual depth buffer for the first pass, float depth for later passes
RID normal; //normal buffer for the first pass
RID albedo; //emission buffer for the first pass
RID orm; //orm buffer for the first pass
RID fb; //used for rendering, only valid on first map
RID uniform_set;
uint32_t size;
int mipmap; // mipmap to write to, -1 if no mipmap assigned
};
Vector<DynamicMap> dynamic_maps;
int slot = -1;
uint32_t last_probe_version = 0;
uint32_t last_probe_data_version = 0;
@ -181,6 +217,8 @@ private:
uint64_t last_pass = 0;
uint32_t render_index = 0;
bool has_dynamic_object_data = false;
Transform transform;
};
@ -198,6 +236,10 @@ private:
GI_PROBE_SHADER_VERSION_COMPUTE_SECOND_BOUNCE,
GI_PROBE_SHADER_VERSION_COMPUTE_MIPMAP,
GI_PROBE_SHADER_VERSION_WRITE_TEXTURE,
GI_PROBE_SHADER_VERSION_DYNAMIC_OBJECT_LIGHTING,
GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE,
GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_PLOT,
GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE_PLOT,
GI_PROBE_SHADER_VERSION_MAX
};
GiprobeShaderRD giprobe_shader;
@ -211,6 +253,7 @@ private:
GI_PROBE_DEBUG_COLOR,
GI_PROBE_DEBUG_LIGHT,
GI_PROBE_DEBUG_EMISSION,
GI_PROBE_DEBUG_LIGHT_FULL,
GI_PROBE_DEBUG_MAX
};
@ -220,6 +263,8 @@ private:
float dynamic_range;
float alpha;
uint32_t level;
int32_t bounds[3];
uint32_t pad;
};
GiprobeDebugShaderRD giprobe_debug_shader;
@ -662,7 +707,8 @@ public:
RID gi_probe_instance_create(RID p_base);
void gi_probe_instance_set_transform_to_data(RID p_probe, const Transform &p_xform);
bool gi_probe_needs_update(RID p_probe) const;
void gi_probe_update(RID p_probe, const Vector<RID> &p_light_instances);
void gi_probe_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, int p_dynamic_object_count, InstanceBase **p_dynamic_objects);
_FORCE_INLINE_ uint32_t gi_probe_instance_get_slot(RID p_probe) {
GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe);
return gi_probe->slot;
@ -726,6 +772,8 @@ public:
void render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count);
void render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region);
virtual void set_scene_pass(uint64_t p_pass) { scene_pass = p_pass; }
_FORCE_INLINE_ uint64_t get_scene_pass() { return scene_pass; }

View file

@ -3558,41 +3558,19 @@ void RasterizerStorageRD::gi_probe_allocate(RID p_gi_probe, const Transform &p_t
bool data_version_changed = false;
if (gi_probe->octree_buffer_size != p_octree_cells.size() || gi_probe->data_buffer_size != p_data_cells.size()) {
//buffer size changed, clear if needed
if (gi_probe->octree_buffer.is_valid()) {
RD::get_singleton()->free(gi_probe->octree_buffer);
RD::get_singleton()->free(gi_probe->data_buffer);
gi_probe->octree_buffer = RID();
gi_probe->data_buffer = RID();
gi_probe->octree_buffer_size = 0;
gi_probe->data_buffer_size = 0;
gi_probe->cell_count = 0;
if (gi_probe->octree_buffer.is_valid()) {
RD::get_singleton()->free(gi_probe->octree_buffer);
RD::get_singleton()->free(gi_probe->data_buffer);
if (gi_probe->sdf_texture.is_valid()) {
RD::get_singleton()->free(gi_probe->sdf_texture);
}
data_version_changed = true;
} else if (gi_probe->octree_buffer_size) {
//they are the same and size did not change..
//update
PoolVector<uint8_t>::Read rc = p_octree_cells.read();
PoolVector<uint8_t>::Read rd = p_data_cells.read();
RD::get_singleton()->buffer_update(gi_probe->octree_buffer, 0, gi_probe->octree_buffer_size, rc.ptr());
RD::get_singleton()->buffer_update(gi_probe->data_buffer, 0, gi_probe->data_buffer_size, rd.ptr());
}
if (gi_probe->level_counts.size() != p_level_counts.size()) {
data_version_changed = true;
} else {
for (int i = 0; i < p_level_counts.size(); i++) {
if (gi_probe->level_counts[i] != p_level_counts[i]) {
data_version_changed = true;
break;
}
}
gi_probe->sdf_texture = RID();
gi_probe->octree_buffer = RID();
gi_probe->data_buffer = RID();
gi_probe->octree_buffer_size = 0;
gi_probe->data_buffer_size = 0;
gi_probe->cell_count = 0;
}
gi_probe->to_cell_xform = p_to_cell_xform;
@ -3600,7 +3578,7 @@ void RasterizerStorageRD::gi_probe_allocate(RID p_gi_probe, const Transform &p_t
gi_probe->octree_size = p_octree_size;
gi_probe->level_counts = p_level_counts;
if (p_octree_cells.size() && gi_probe->octree_buffer.is_null()) {
if (p_octree_cells.size()) {
ERR_FAIL_COND(p_octree_cells.size() % 32 != 0); //cells size must be a multiple of 32
uint32_t cell_count = p_octree_cells.size() / 32;
@ -3612,13 +3590,80 @@ void RasterizerStorageRD::gi_probe_allocate(RID p_gi_probe, const Transform &p_t
gi_probe->octree_buffer_size = p_octree_cells.size();
gi_probe->data_buffer = RD::get_singleton()->storage_buffer_create(p_data_cells.size(), p_data_cells);
gi_probe->data_buffer_size = p_data_cells.size();
data_version_changed = true;
{
{
RD::TextureFormat tf;
tf.format = RD::DATA_FORMAT_R8_UNORM;
tf.width = gi_probe->octree_size.x;
tf.height = gi_probe->octree_size.y;
tf.depth = gi_probe->octree_size.z;
tf.type = RD::TEXTURE_TYPE_3D;
tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
tf.shareable_formats.push_back(RD::DATA_FORMAT_R8_UNORM);
tf.shareable_formats.push_back(RD::DATA_FORMAT_R8_UINT);
gi_probe->sdf_texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
}
RID shared_tex;
{
RD::TextureView tv;
tv.format_override = RD::DATA_FORMAT_R8_UINT;
shared_tex = RD::get_singleton()->texture_create_shared(tv, gi_probe->sdf_texture);
}
//update SDF texture
Vector<RD::Uniform> uniforms;
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 1;
u.ids.push_back(gi_probe->octree_buffer);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 2;
u.ids.push_back(gi_probe->data_buffer);
uniforms.push_back(u);
}
{
RD::Uniform u;
u.type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 3;
u.ids.push_back(shared_tex);
uniforms.push_back(u);
}
RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, giprobe_sdf_shader_version_shader, 0);
{
uint32_t push_constant[4] = { 0, 0, 0, 0 };
for (int i = 0; i < gi_probe->level_counts.size() - 1; i++) {
push_constant[0] += gi_probe->level_counts[i];
}
push_constant[1] = push_constant[0] + gi_probe->level_counts[gi_probe->level_counts.size() - 1];
print_line("offset: " + itos(push_constant[0]));
print_line("size: " + itos(push_constant[1]));
//create SDF
RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, giprobe_sdf_shader_pipeline);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0);
RD::get_singleton()->compute_list_set_push_constant(compute_list, push_constant, sizeof(uint32_t) * 4);
RD::get_singleton()->compute_list_dispatch(compute_list, gi_probe->octree_size.x / 4, gi_probe->octree_size.y / 4, gi_probe->octree_size.z / 4);
RD::get_singleton()->compute_list_end();
}
RD::get_singleton()->free(uniform_set);
RD::get_singleton()->free(shared_tex);
}
}
gi_probe->version++;
if (data_version_changed) {
gi_probe->data_version++;
}
gi_probe->data_version++;
gi_probe->instance_dependency.instance_notify_changed(true, false);
}
@ -3794,6 +3839,13 @@ RID RasterizerStorageRD::gi_probe_get_data_buffer(RID p_gi_probe) const {
return gi_probe->data_buffer;
}
RID RasterizerStorageRD::gi_probe_get_sdf_texture(RID p_gi_probe) {
GIProbe *gi_probe = gi_probe_owner.getornull(p_gi_probe);
ERR_FAIL_COND_V(!gi_probe, RID());
return gi_probe->sdf_texture;
}
/* RENDER TARGET API */
void RasterizerStorageRD::_clear_render_target(RenderTarget *rt) {
@ -4583,110 +4635,121 @@ RasterizerStorageRD::RasterizerStorageRD() {
{
{ //vertex
PoolVector<uint8_t> buffer;
buffer.resize(sizeof(float) * 3);
{
PoolVector<uint8_t>::Write w = buffer.write();
float *fptr = (float *)w.ptr();
fptr[0] = 0.0;
fptr[1] = 0.0;
fptr[2] = 0.0;
}
mesh_default_rd_buffers[DEFAULT_RD_BUFFER_VERTEX] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
}
{ //normal
PoolVector<uint8_t> buffer;
buffer.resize(sizeof(float) * 3);
{
PoolVector<uint8_t>::Write w = buffer.write();
float *fptr = (float *)w.ptr();
fptr[0] = 1.0;
fptr[1] = 0.0;
fptr[2] = 0.0;
}
mesh_default_rd_buffers[DEFAULT_RD_BUFFER_NORMAL] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
}
{ //tangent
PoolVector<uint8_t> buffer;
buffer.resize(sizeof(float) * 4);
{
PoolVector<uint8_t>::Write w = buffer.write();
float *fptr = (float *)w.ptr();
fptr[0] = 1.0;
fptr[1] = 0.0;
fptr[2] = 0.0;
fptr[3] = 0.0;
}
mesh_default_rd_buffers[DEFAULT_RD_BUFFER_TANGENT] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
}
{ //color
PoolVector<uint8_t> buffer;
buffer.resize(sizeof(float) * 4);
{
PoolVector<uint8_t>::Write w = buffer.write();
float *fptr = (float *)w.ptr();
fptr[0] = 1.0;
fptr[1] = 1.0;
fptr[2] = 1.0;
fptr[3] = 1.0;
}
mesh_default_rd_buffers[DEFAULT_RD_BUFFER_COLOR] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
}
{ //tex uv 1
PoolVector<uint8_t> buffer;
buffer.resize(sizeof(float) * 2);
{
PoolVector<uint8_t>::Write w = buffer.write();
float *fptr = (float *)w.ptr();
fptr[0] = 0.0;
fptr[1] = 0.0;
}
mesh_default_rd_buffers[DEFAULT_RD_BUFFER_TEX_UV] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
}
{ //tex uv 2
PoolVector<uint8_t> buffer;
buffer.resize(sizeof(float) * 2);
{
PoolVector<uint8_t>::Write w = buffer.write();
float *fptr = (float *)w.ptr();
fptr[0] = 0.0;
fptr[1] = 0.0;
}
mesh_default_rd_buffers[DEFAULT_RD_BUFFER_TEX_UV2] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
}
{ //bones
PoolVector<uint8_t> buffer;
buffer.resize(sizeof(uint32_t) * 4);
{
PoolVector<uint8_t>::Write w = buffer.write();
uint32_t *fptr = (uint32_t *)w.ptr();
fptr[0] = 0;
fptr[1] = 0;
fptr[2] = 0;
fptr[3] = 0;
}
mesh_default_rd_buffers[DEFAULT_RD_BUFFER_BONES] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
}
{ //weights
PoolVector<uint8_t> buffer;
buffer.resize(sizeof(float) * 4);
{
PoolVector<uint8_t>::Write w = buffer.write();
float *fptr = (float *)w.ptr();
fptr[0] = 0.0;
fptr[1] = 0.0;
fptr[2] = 0.0;
fptr[3] = 0.0;
}
mesh_default_rd_buffers[DEFAULT_RD_BUFFER_WEIGHTS] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
}
PoolVector<uint8_t> buffer;
buffer.resize(sizeof(float) * 3);
{
PoolVector<uint8_t>::Write w = buffer.write();
float *fptr = (float *)w.ptr();
fptr[0] = 0.0;
fptr[1] = 0.0;
fptr[2] = 0.0;
}
mesh_default_rd_buffers[DEFAULT_RD_BUFFER_VERTEX] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
}
{ //normal
PoolVector<uint8_t> buffer;
buffer.resize(sizeof(float) * 3);
{
PoolVector<uint8_t>::Write w = buffer.write();
float *fptr = (float *)w.ptr();
fptr[0] = 1.0;
fptr[1] = 0.0;
fptr[2] = 0.0;
}
mesh_default_rd_buffers[DEFAULT_RD_BUFFER_NORMAL] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
}
{ //tangent
PoolVector<uint8_t> buffer;
buffer.resize(sizeof(float) * 4);
{
PoolVector<uint8_t>::Write w = buffer.write();
float *fptr = (float *)w.ptr();
fptr[0] = 1.0;
fptr[1] = 0.0;
fptr[2] = 0.0;
fptr[3] = 0.0;
}
mesh_default_rd_buffers[DEFAULT_RD_BUFFER_TANGENT] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
}
{ //color
PoolVector<uint8_t> buffer;
buffer.resize(sizeof(float) * 4);
{
PoolVector<uint8_t>::Write w = buffer.write();
float *fptr = (float *)w.ptr();
fptr[0] = 1.0;
fptr[1] = 1.0;
fptr[2] = 1.0;
fptr[3] = 1.0;
}
mesh_default_rd_buffers[DEFAULT_RD_BUFFER_COLOR] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
}
{ //tex uv 1
PoolVector<uint8_t> buffer;
buffer.resize(sizeof(float) * 2);
{
PoolVector<uint8_t>::Write w = buffer.write();
float *fptr = (float *)w.ptr();
fptr[0] = 0.0;
fptr[1] = 0.0;
}
mesh_default_rd_buffers[DEFAULT_RD_BUFFER_TEX_UV] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
}
{ //tex uv 2
PoolVector<uint8_t> buffer;
buffer.resize(sizeof(float) * 2);
{
PoolVector<uint8_t>::Write w = buffer.write();
float *fptr = (float *)w.ptr();
fptr[0] = 0.0;
fptr[1] = 0.0;
}
mesh_default_rd_buffers[DEFAULT_RD_BUFFER_TEX_UV2] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
}
{ //bones
PoolVector<uint8_t> buffer;
buffer.resize(sizeof(uint32_t) * 4);
{
PoolVector<uint8_t>::Write w = buffer.write();
uint32_t *fptr = (uint32_t *)w.ptr();
fptr[0] = 0;
fptr[1] = 0;
fptr[2] = 0;
fptr[3] = 0;
}
mesh_default_rd_buffers[DEFAULT_RD_BUFFER_BONES] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
}
{ //weights
PoolVector<uint8_t> buffer;
buffer.resize(sizeof(float) * 4);
{
PoolVector<uint8_t>::Write w = buffer.write();
float *fptr = (float *)w.ptr();
fptr[0] = 0.0;
fptr[1] = 0.0;
fptr[2] = 0.0;
fptr[3] = 0.0;
}
mesh_default_rd_buffers[DEFAULT_RD_BUFFER_WEIGHTS] = RD::get_singleton()->vertex_buffer_create(buffer.size(), buffer);
}
}
{
Vector<String> sdf_versions;
sdf_versions.push_back(""); //one only
giprobe_sdf_shader.initialize(sdf_versions);
giprobe_sdf_shader_version = giprobe_sdf_shader.version_create();
giprobe_sdf_shader.version_set_compute_code(giprobe_sdf_shader_version, "", "", "", Vector<String>());
giprobe_sdf_shader_version_shader = giprobe_sdf_shader.version_get_shader(giprobe_sdf_shader_version, 0);
giprobe_sdf_shader_pipeline = RD::get_singleton()->compute_pipeline_create(giprobe_sdf_shader_version_shader);
}
}
RasterizerStorageRD::~RasterizerStorageRD() {

View file

@ -35,6 +35,7 @@
#include "servers/visual/rasterizer.h"
#include "servers/visual/rasterizer_rd/rasterizer_effects_rd.h"
#include "servers/visual/rasterizer_rd/shader_compiler_rd.h"
#include "servers/visual/rasterizer_rd/shaders/giprobe_sdf.glsl.gen.h"
#include "servers/visual/rendering_device.h"
class RasterizerStorageRD : public RasterizerStorage {
@ -407,6 +408,7 @@ private:
RID octree_buffer;
RID data_buffer;
RID sdf_texture;
uint32_t octree_buffer_size = 0;
uint32_t data_buffer_size = 0;
@ -435,6 +437,11 @@ private:
RasterizerScene::InstanceDependency instance_dependency;
};
GiprobeSdfShaderRD giprobe_sdf_shader;
RID giprobe_sdf_shader_version;
RID giprobe_sdf_shader_version_shader;
RID giprobe_sdf_shader_pipeline;
mutable RID_Owner<GIProbe> gi_probe_owner;
/* RENDER TARGET */
@ -999,6 +1006,8 @@ public:
RID gi_probe_get_octree_buffer(RID p_gi_probe) const;
RID gi_probe_get_data_buffer(RID p_gi_probe) const;
RID gi_probe_get_sdf_texture(RID p_gi_probe);
/* LIGHTMAP CAPTURE */
void lightmap_capture_set_bounds(RID p_capture, const AABB &p_bounds) {}

View file

@ -13,4 +13,6 @@ if 'RD_GLSL' in env['BUILDERS']:
env.RD_GLSL('copy.glsl');
env.RD_GLSL('giprobe.glsl');
env.RD_GLSL('giprobe_debug.glsl');
env.RD_GLSL('giprobe_sdf.glsl');

View file

@ -4,8 +4,18 @@
VERSION_DEFINES
#ifdef MODE_DYNAMIC
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#else
layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
#endif
#ifndef MODE_DYNAMIC
#define NO_CHILDREN 0xFFFFFFFF
#define GREY_VEC vec3(0.33333,0.33333,0.33333)
@ -28,11 +38,14 @@ layout(set=0,binding=2,std430) buffer CellDataBuffer {
CellData data[];
} cell_data;
#endif // MODE DYNAMIC
#define LIGHT_TYPE_DIRECTIONAL 0
#define LIGHT_TYPE_OMNI 1
#define LIGHT_TYPE_SPOT 2
#ifdef MODE_COMPUTE_LIGHT
#if defined(MODE_COMPUTE_LIGHT) || defined(MODE_DYNAMIC_LIGHTING)
struct Light {
@ -64,7 +77,6 @@ layout(set=0,binding=3,std140) uniform Lights {
#ifdef MODE_SECOND_BOUNCE
layout (set=0,binding=5) uniform texture3D color_texture;
layout (set=0,binding=6) uniform sampler texture_sampler;
#ifdef MODE_ANISOTROPIC
layout (set=0,binding=7) uniform texture3D aniso_pos_texture;
@ -73,6 +85,7 @@ layout (set=0,binding=8) uniform texture3D aniso_neg_texture;
#endif // MODE_SECOND_BOUNCE
#ifndef MODE_DYNAMIC
layout(push_constant, binding = 0, std430) uniform Params {
@ -96,6 +109,11 @@ layout(set=0,binding=4,std430) buffer Outputs {
vec4 data[];
} outputs;
#endif // MODE DYNAMIC
layout (set=0,binding=9) uniform texture3D texture_sdf;
layout (set=0,binding=10) uniform sampler texture_sampler;
#ifdef MODE_WRITE_TEXTURE
layout (rgba8,set=0,binding=5) uniform restrict writeonly image3D color_tex;
@ -111,63 +129,97 @@ layout (r16ui,set=0,binding=7) uniform restrict writeonly uimage3D aniso_neg_tex
#endif
#ifdef MODE_COMPUTE_LIGHT
#ifdef MODE_DYNAMIC
uint raymarch(float distance,float distance_adv,vec3 from,vec3 direction) {
layout(push_constant, binding = 0, std430) uniform Params {
uint result = NO_CHILDREN;
ivec3 limits;
uint light_count;
ivec3 x_dir;
float z_base;
ivec3 y_dir;
float z_sign;
ivec3 z_dir;
float pos_multiplier;
ivec2 rect_pos;
ivec2 rect_size;
ivec2 prev_rect_ofs;
ivec2 prev_rect_size;
bool flip_x;
bool flip_y;
float dynamic_range;
bool keep_downsample_color;
ivec3 size = ivec3(max(max(params.limits.x,params.limits.y),params.limits.z));
} params;
while (distance > -distance_adv) { //use this to avoid precision errors
#ifdef MODE_DYNAMIC_LIGHTING
uint cell = 0;
layout (rgba8,set=0,binding=5) uniform restrict readonly image2D source_albedo;
layout (rgba8,set=0,binding=6) uniform restrict readonly image2D source_normal;
layout (rgba8,set=0,binding=7) uniform restrict readonly image2D source_orm;
//layout (set=0,binding=8) uniform texture2D source_depth;
layout (rgba16f,set=0,binding=11) uniform restrict image2D emission;
layout (r32f,set=0,binding=12) uniform restrict image2D depth;
ivec3 pos = ivec3(from);
#endif
if (all(greaterThanEqual(pos,ivec3(0))) && all(lessThan(pos,size))) {
#ifdef MODE_DYNAMIC_SHRINK
ivec3 ofs = ivec3(0);
ivec3 half_size = size / 2;
layout (rgba16f,set=0,binding=5) uniform restrict readonly image2D source_light;
layout (r32f,set=0,binding=6) uniform restrict readonly image2D source_depth;
for (int i = 0; i < params.stack_size - 1; i++) {
#ifdef MODE_DYNAMIC_SHRINK_WRITE
bvec3 greater = greaterThanEqual(pos,ofs+half_size);
layout (rgba16f,set=0,binding=7) uniform restrict writeonly image2D light;
layout (r32f,set=0,binding=8) uniform restrict writeonly image2D depth;
ofs += mix(ivec3(0),half_size,greater);
#endif // MODE_DYNAMIC_SHRINK_WRITE
uint child = 0; //wonder if this can be done faster
if (greater.x) {
child|=1;
}
if (greater.y) {
child|=2;
}
if (greater.z) {
child|=4;
}
#ifdef MODE_DYNAMIC_SHRINK_PLOT
cell = cell_children.data[cell].children[child];
if (cell == NO_CHILDREN)
break;
layout (rgba8,set=0,binding=11) uniform restrict image3D color_texture;
half_size >>= ivec3(1);
}
#ifdef MODE_ANISOTROPIC
if ( cell != NO_CHILDREN) {
return cell; //found cell!
}
layout (r16ui,set=0,binding=12) uniform restrict writeonly uimage3D aniso_pos_texture;
layout (r16ui,set=0,binding=13) uniform restrict writeonly uimage3D aniso_neg_texture;
#endif // MODE ANISOTROPIC
#endif //MODE_DYNAMIC_SHRINK_PLOT
#endif // MODE_DYNAMIC_SHRINK
//layout (rgba8,set=0,binding=5) uniform restrict writeonly image3D color_tex;
#endif // MODE DYNAMIC
#if defined(MODE_COMPUTE_LIGHT) || defined(MODE_DYNAMIC_LIGHTING)
float raymarch(float distance,float distance_adv,vec3 from,vec3 direction) {
vec3 cell_size = 1.0 / vec3(params.limits);
while (distance > 0.0) { //use this to avoid precision errors
float advance = texture(sampler3D(texture_sdf,texture_sampler),from * cell_size).r * 255.0 - 1.0;
if (advance<0.0) {
break;
}
from += direction * distance_adv;
distance -= distance_adv;
advance = max(distance_adv, advance - mod(advance, distance_adv)); //should always advance in multiples of distance_adv
from += direction * advance;
distance -= advance;
}
return NO_CHILDREN;
return max(0.0,distance);
}
bool compute_light_vector(uint light,uint cell, vec3 pos,out float attenuation, out vec3 light_pos) {
bool compute_light_vector(uint light, vec3 pos,out float attenuation, out vec3 light_pos) {
if (lights.data[light].type==LIGHT_TYPE_DIRECTIONAL) {
@ -226,13 +278,84 @@ float get_normal_advance(vec3 p_normal) {
return 1.0 / dot(normal,unorm);
}
#endif
void clip_segment(vec4 plane, vec3 begin, inout vec3 end) {
vec3 segment = begin - end;
float den = dot(plane.xyz,segment);
//printf("den is %i\n",den);
if (den < 0.0001) {
return;
}
float dist = (dot(plane.xyz,begin) - plane.w) / den;
if (dist < 0.0001 || dist > 1.0001) {
return;
}
end = begin + segment * -dist;
}
bool compute_light_at_pos(uint index, vec3 pos, vec3 normal, inout vec3 light, inout vec3 light_dir) {
float attenuation;
vec3 light_pos;
if (!compute_light_vector(index,pos,attenuation,light_pos)) {
return false;
}
light_dir = normalize(pos - light_pos);
if (attenuation < 0.01 || (length(normal) > 0.2 && dot(normal,light_dir)>=0)) {
return false; //not facing the light, or attenuation is near zero
}
if (lights.data[index].has_shadow) {
float distance_adv = get_normal_advance(light_dir);
vec3 to = pos;
to-= sign(light_dir)*0.45; //go near the edge towards the light direction to avoid self occlusion
//clip
clip_segment(mix(vec4(-1.0,0.0,0.0,0.0),vec4(1.0,0.0,0.0,float(params.limits.x-1)),bvec4(light_dir.x < 0.0)),to,light_pos);
clip_segment(mix(vec4(0.0,-1.0,0.0,0.0),vec4(0.0,1.0,0.0,float(params.limits.y-1)),bvec4(light_dir.y < 0.0)),to,light_pos);
clip_segment(mix(vec4(0.0,0.0,-1.0,0.0),vec4(0.0,0.0,1.0,float(params.limits.z-1)),bvec4(light_dir.z < 0.0)),to,light_pos);
float distance = length(to-light_pos);
if (distance < 0.1) {
return false; // hit
}
distance += distance_adv - mod(distance, distance_adv); //make it reach the center of the box always
light_pos = to - light_dir * distance;
//from -= sign(light_dir)*0.45; //go near the edge towards the light direction to avoid self occlusion
float dist = raymarch(distance,distance_adv,light_pos,light_dir);
if (dist > distance_adv) {
return false;
}
}
light = lights.data[index].color * attenuation * lights.data[index].energy;
return true;
}
#endif // MODE COMPUTE LIGHT
void main() {
#ifndef MODE_DYNAMIC
uint cell_index = gl_GlobalInvocationID.x;;
if (cell_index >= params.cell_count) {
return;
@ -242,6 +365,8 @@ void main() {
uvec3 posu = uvec3(cell_data.data[cell_index].position&0x7FF,(cell_data.data[cell_index].position>>11)&0x3FF,cell_data.data[cell_index].position>>21);
vec4 albedo = unpackUnorm4x8(cell_data.data[cell_index].albedo);
#endif
/////////////////COMPUTE LIGHT///////////////////////////////
#ifdef MODE_COMPUTE_LIGHT
@ -249,7 +374,7 @@ void main() {
vec3 pos = vec3(posu) + vec3(0.5);
vec3 emission = vec3(uvec3(cell_data.data[cell_index].emission & 0x1ff,(cell_data.data[cell_index].emission >> 9) & 0x1ff,(cell_data.data[cell_index].emission >> 18) & 0x1ff)) * pow(2.0, float(cell_data.data[cell_index].emission >> 27) - 15.0 - 9.0);
vec4 normal = unpackSnorm4x8(cell_data.data[cell_index].normal);
vec3 normal = unpackSnorm4x8(cell_data.data[cell_index].normal).xyz;
#ifdef MODE_ANISOTROPIC
vec3 accum[6]=vec3[](vec3(0.0),vec3(0.0),vec3(0.0),vec3(0.0),vec3(0.0),vec3(0.0));
@ -260,41 +385,13 @@ void main() {
for(uint i=0;i<params.light_count;i++) {
float attenuation;
vec3 light_pos;
if (!compute_light_vector(i,cell_index,pos,attenuation,light_pos)) {
vec3 light;
vec3 light_dir;
if (!compute_light_at_pos(i,pos,normal.xyz,light,light_dir)) {
continue;
}
vec3 light_dir = pos - light_pos;
float distance = length(light_dir);
light_dir=normalize(light_dir);
if (attenuation < 0.01 || (length(normal.xyz) > 0.2 && dot(normal.xyz,light_dir)>=0)) {
continue; //not facing the light, or attenuation is near zero
}
if (lights.data[i].has_shadow) {
float distance_adv = get_normal_advance(light_dir);
distance += distance_adv - mod(distance, distance_adv); //make it reach the center of the box always
vec3 from = pos - light_dir * distance; //approximate
from -= sign(light_dir)*0.45; //go near the edge towards the light direction to avoid self occlusion
uint result = raymarch(distance,distance_adv,from,light_dir);
if (result != cell_index) {
continue; //was occluded
}
}
vec3 light = lights.data[i].color * albedo.rgb * attenuation * lights.data[i].energy;
light*= albedo.rgb;
#ifdef MODE_ANISOTROPIC
for(uint j=0;j<6;j++) {
@ -302,11 +399,11 @@ void main() {
accum[j]+=max(0.0,dot(accum_dirs[j],-light_dir))*light;
}
#else
if (length(normal.xyz) > 0.2) {
accum+=max(0.0,dot(normal.xyz,-light_dir))*light;
if (length(normal) > 0.2) {
accum+=max(0.0,dot(normal,-light_dir))*light;
} else {
//all directions
accum+=light+emission;
accum+=light;
}
#endif
}
@ -314,12 +411,17 @@ void main() {
#ifdef MODE_ANISOTROPIC
outputs.data[cell_index*6+0]=vec4(accum[0] + emission,0.0);
outputs.data[cell_index*6+1]=vec4(accum[1] + emission,0.0);
outputs.data[cell_index*6+2]=vec4(accum[2] + emission,0.0);
outputs.data[cell_index*6+3]=vec4(accum[3] + emission,0.0);
outputs.data[cell_index*6+4]=vec4(accum[4] + emission,0.0);
outputs.data[cell_index*6+5]=vec4(accum[5] + emission,0.0);
for(uint i=0;i<6;i++) {
vec3 light = accum[i];
if (length(normal) > 0.2) {
light += max(0.0,dot(accum_dirs[i],-normal)) * emission;
} else {
light += emission;
}
outputs.data[cell_index*6+i] = vec4(light,0.0);
}
#else
outputs.data[cell_index]=vec4(accum + emission,0.0);
@ -540,4 +642,167 @@ void main() {
}
#endif
///////////////////DYNAMIC LIGHTING/////////////////////////////
#ifdef MODE_DYNAMIC
ivec2 pos_xy = ivec2(gl_GlobalInvocationID.xy);
if (any(greaterThanEqual(pos_xy,params.rect_size))) {
return; //out of bounds
}
ivec2 uv_xy = pos_xy;
if (params.flip_x) {
uv_xy.x = params.rect_size.x - pos_xy.x - 1;
}
if (params.flip_y) {
uv_xy.y = params.rect_size.y - pos_xy.y - 1;
}
#ifdef MODE_DYNAMIC_LIGHTING
{
float z = params.z_base + imageLoad(depth,uv_xy).x * params.z_sign;
ivec3 pos = params.x_dir * (params.rect_pos.x + pos_xy.x) + params.y_dir * (params.rect_pos.y + pos_xy.y) + abs(params.z_dir) * int(z);
vec3 normal = imageLoad(source_normal,uv_xy).xyz * 2.0 - 1.0;
normal = vec3(params.x_dir) * normal.x * mix(1.0,-1.0,params.flip_x) + vec3(params.y_dir) * normal.y * mix(1.0,-1.0,params.flip_y) - vec3(params.z_dir) * normal.z;
vec4 albedo = imageLoad(source_albedo,uv_xy);
//determine the position in space
vec3 accum = vec3(0.0);
for(uint i=0;i<params.light_count;i++) {
vec3 light;
vec3 light_dir;
if (!compute_light_at_pos(i,vec3(pos) * params.pos_multiplier,normal,light,light_dir)) {
continue;
}
light*= albedo.rgb;
accum+=max(0.0,dot(normal,-light_dir))*light;
}
accum+=imageLoad(emission,uv_xy).xyz;
imageStore(emission,uv_xy,vec4(accum,albedo.a));
imageStore(depth,uv_xy,vec4(z));
}
#endif // MODE DYNAMIC LIGHTING
#ifdef MODE_DYNAMIC_SHRINK
{
vec4 accum = vec4(0.0);
float accum_z = 0.0;
float count = 0.0;
for(int i=0;i<4;i++) {
ivec2 ofs = pos_xy*2 + ivec2(i&1,i>>1) - params.prev_rect_ofs;
if (any(lessThan(ofs,ivec2(0))) || any(greaterThanEqual(ofs,params.prev_rect_size))) {
continue;
}
if (params.flip_x) {
ofs.x = params.prev_rect_size.x - ofs.x - 1;
}
if (params.flip_y) {
ofs.y = params.prev_rect_size.y - ofs.y - 1;
}
vec4 light = imageLoad(source_light,ofs);
if (light.a==0.0) { //ignore empty
continue;
}
accum += light;
float z = imageLoad(source_depth,ofs).x;
accum_z += z*0.5; //shrink half too
count+=1.0;
}
accum/=4.0;
if (count==0.0) {
accum_z=0.0; //avoid nan
} else {
accum_z/=count;
}
#ifdef MODE_DYNAMIC_SHRINK_WRITE
imageStore(light,uv_xy,accum);
imageStore(depth,uv_xy,vec4(accum_z));
#endif
#ifdef MODE_DYNAMIC_SHRINK_PLOT
if (accum.a<0.001) {
return; //do not blit if alpha is too low
}
ivec3 pos = params.x_dir * (params.rect_pos.x + pos_xy.x) + params.y_dir * (params.rect_pos.y + pos_xy.y) + abs(params.z_dir) * int(accum_z);
float z_frac = fract(accum_z);
for(int i = 0; i< 2; i++) {
ivec3 pos3d = pos + abs(params.z_dir) * i;
if (any(lessThan(pos3d,ivec3(0))) || any(greaterThanEqual(pos3d,params.limits))) {
//skip if offlimits
continue;
}
vec4 color_blit = accum * (i==0 ? 1.0 - z_frac : z_frac );
vec4 color = imageLoad(color_texture,pos3d);
color.rgb *=params.dynamic_range;
#if 0
color.rgb = mix(color.rgb,color_blit.rgb,color_blit.a);
color.a+=color_blit.a;
#else
float sa = 1.0 - color_blit.a;
vec4 result;
result.a = color.a * sa + color_blit.a;
if (result.a==0.0) {
result = vec4(0.0);
} else {
result.rgb = (color.rgb * color.a * sa + color_blit.rgb * color_blit.a) / result.a;
color = result;
}
#endif
color.rgb /= params.dynamic_range;
imageStore(color_texture,pos3d,color);
//imageStore(color_texture,pos3d,vec4(1,1,1,1));
#ifdef MODE_ANISOTROPIC
//do not care about anisotropy for dynamic objects, just store full lit in all directions
imageStore(aniso_pos_texture,pos3d,uvec4(0xFFFF));
imageStore(aniso_neg_texture,pos3d,uvec4(0xFFFF));
#endif // ANISOTROPIC
}
#endif // MODE_DYNAMIC_SHRINK_PLOT
}
#endif
#endif // MODE DYNAMIC
}

View file

@ -32,6 +32,8 @@ layout(push_constant, binding = 0, std430) uniform Params {
float dynamic_range;
float alpha;
uint level;
ivec3 bounds;
uint pad;
} params;
@ -80,10 +82,13 @@ void main() {
vec3 vertex = cube_triangles[gl_VertexIndex] * 0.5 + 0.5;
#ifdef MODE_DEBUG_LIGHT_FULL
uvec3 posu = uvec3( gl_InstanceIndex % params.bounds.x, (gl_InstanceIndex / params.bounds.x) % params.bounds.y,gl_InstanceIndex / (params.bounds.y * params.bounds.x) );
#else
uint cell_index = gl_InstanceIndex + params.cell_offset;
uvec3 posu = uvec3(cell_data.data[cell_index].position&0x7FF,(cell_data.data[cell_index].position>>11)&0x3FF,cell_data.data[cell_index].position>>21);
#endif
#ifdef MODE_DEBUG_EMISSION
color_interp.xyz = vec3(uvec3(cell_data.data[cell_index].emission & 0x1ff,(cell_data.data[cell_index].emission >> 9) & 0x1ff,(cell_data.data[cell_index].emission >> 18) & 0x1ff)) * pow(2.0, float(cell_data.data[cell_index].emission >> 27) - 15.0 - 9.0);
@ -138,16 +143,24 @@ void main() {
color_interp.xyz *= strength;
#else
color_interp.xyz = texelFetch(sampler3D(color_tex,tex_sampler),ivec3(posu),int(params.level)).xyz * params.dynamic_range;
color_interp = texelFetch(sampler3D(color_tex,tex_sampler),ivec3(posu),int(params.level));
color_interp.xyz * params.dynamic_range;
#endif
#endif
float scale = (1<<params.level);
color_interp.a = params.alpha;
gl_Position = params.projection * vec4((vec3(posu)+vertex)*scale,1.0);
#ifdef MODE_DEBUG_LIGHT_FULL
if (color_interp.a == 0.0) {
gl_Position = vec4(0.0); //force clip and not draw
}
#else
color_interp.a = params.alpha;
#endif
}
[fragment]
@ -162,4 +175,35 @@ layout(location=0) out vec4 frag_color;
void main() {
frag_color = color_interp;
#ifdef MODE_DEBUG_LIGHT_FULL
//there really is no alpha, so use dither
int x = int(gl_FragCoord.x) % 4;
int y = int(gl_FragCoord.y) % 4;
int index = x + y * 4;
float limit = 0.0;
if (x < 8) {
if (index == 0) limit = 0.0625;
if (index == 1) limit = 0.5625;
if (index == 2) limit = 0.1875;
if (index == 3) limit = 0.6875;
if (index == 4) limit = 0.8125;
if (index == 5) limit = 0.3125;
if (index == 6) limit = 0.9375;
if (index == 7) limit = 0.4375;
if (index == 8) limit = 0.25;
if (index == 9) limit = 0.75;
if (index == 10) limit = 0.125;
if (index == 11) limit = 0.625;
if (index == 12) limit = 1.0;
if (index == 13) limit = 0.5;
if (index == 14) limit = 0.875;
if (index == 15) limit = 0.375;
}
if (frag_color.a < limit) {
discard;
}
#endif
}

View file

@ -0,0 +1,194 @@
[compute]
#version 450
VERSION_DEFINES
layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
#define MAX_DISTANCE 100000
#define NO_CHILDREN 0xFFFFFFFF
#define GREY_VEC vec3(0.33333,0.33333,0.33333)
struct CellChildren {
uint children[8];
};
layout(set=0,binding=1,std430) buffer CellChildrenBuffer {
CellChildren data[];
} cell_children;
struct CellData {
uint position; // xyz 10 bits
uint albedo; //rgb albedo
uint emission; //rgb normalized with e as multiplier
uint normal; //RGB normal encoded
};
layout(set=0,binding=2,std430) buffer CellDataBuffer {
CellData data[];
} cell_data;
layout (r8ui,set=0,binding=3) uniform restrict writeonly uimage3D sdf_tex;
layout(push_constant, binding = 0, std430) uniform Params {
uint offset;
uint end;
uint pad0;
uint pad1;
} params;
void main() {
vec3 pos = vec3(gl_GlobalInvocationID);
float closest_dist = 100000.0;
for(uint i=params.offset;i<params.end;i++) {
vec3 posu = vec3(uvec3(cell_data.data[i].position&0x7FF,(cell_data.data[i].position>>11)&0x3FF,cell_data.data[i].position>>21));
float dist = length(pos-posu);
if (dist < closest_dist) {
closest_dist = dist;
}
}
uint dist_8;
if (closest_dist<0.0001) { // same cell
dist_8=0; //equals to -1
} else {
dist_8 = clamp(uint(closest_dist),0,254) + 1; //conservative, 0 is 1, so <1 is considered solid
}
imageStore(sdf_tex,ivec3(gl_GlobalInvocationID),uvec4(dist_8));
//imageStore(sdf_tex,pos,uvec4(pos*2,0));
}
#if 0
layout(push_constant, binding = 0, std430) uniform Params {
ivec3 limits;
uint stack_size;
} params;
float distance_to_aabb(ivec3 pos, ivec3 aabb_pos, ivec3 aabb_size) {
vec3 delta = vec3(max(ivec3(0),max(aabb_pos - pos, pos - (aabb_pos + aabb_size - ivec3(1)))));
return length(delta);
}
void main() {
ivec3 pos = ivec3(gl_GlobalInvocationID);
uint stack[10]=uint[](0,0,0,0,0,0,0,0,0,0);
uint stack_indices[10]=uint[](0,0,0,0,0,0,0,0,0,0);
ivec3 stack_positions[10]=ivec3[](ivec3(0),ivec3(0),ivec3(0),ivec3(0),ivec3(0),ivec3(0),ivec3(0),ivec3(0),ivec3(0),ivec3(0));
const uint cell_orders[8]=uint[](
0x11f58d1,
0xe2e70a,
0xd47463,
0xbb829c,
0x8d11f5,
0x70ae2e,
0x463d47,
0x29cbb8
);
bool cell_found = false;
bool cell_found_exact = false;
ivec3 closest_cell_pos;
float closest_distance = MAX_DISTANCE;
int stack_pos = 0;
while(true) {
uint index = stack_indices[stack_pos]>>24;
if (index == 8) {
//go up
if (stack_pos==0) {
break; //done going through octree
}
stack_pos--;
continue;
}
stack_indices[stack_pos] = (stack_indices[stack_pos]&((1<<24)-1))|((index + 1)<<24);
uint cell_index = (stack_indices[stack_pos]>>(index*3))&0x7;
uint child_cell = cell_children.data[stack[stack_pos]].children[cell_index];
if (child_cell == NO_CHILDREN) {
continue;
}
ivec3 child_cell_size = params.limits >> (stack_pos+1);
ivec3 child_cell_pos = stack_positions[stack_pos];
child_cell_pos+=mix(ivec3(0),child_cell_size,bvec3(uvec3(index&1,index&2,index&4)!=uvec3(0)));
bool is_leaf = stack_pos == (params.stack_size-2);
if (child_cell_pos==pos && is_leaf) {
//we may actually end up in the exact cell.
//if this happens, just abort
cell_found_exact=true;
break;
}
if (cell_found) {
//discard by distance
float distance = distance_to_aabb(pos,child_cell_pos,child_cell_size);
if (distance >= closest_distance) {
continue; //pointless, just test next child
} else if (is_leaf) {
//closer than what we have AND end of stack, save and continue
closest_cell_pos = child_cell_pos;
closest_distance = distance;
continue;
}
} else if (is_leaf) {
//first solid cell we find, save and continue
closest_distance = distance_to_aabb(pos,child_cell_pos,child_cell_size);
closest_cell_pos = child_cell_pos;
cell_found=true;
continue;
}
bvec3 direction = greaterThan(( pos - ( child_cell_pos + (child_cell_size >>1) ) ) , ivec3(0) );
uint cell_order = 0;
cell_order|=mix(0,1,direction.x);
cell_order|=mix(0,2,direction.y);
cell_order|=mix(0,4,direction.z);
stack[stack_pos+1]=child_cell;
stack_indices[stack_pos+1]=cell_orders[cell_order]; //start counting
stack_positions[stack_pos+1]=child_cell_pos;
stack_pos++; //go up stack
}
uint dist_8;
if (cell_found_exact) {
dist_8=0; //equals to -1
} else {
float closest_distance = length(vec3(pos-closest_cell_pos));
dist_8 = clamp(uint(closest_distance),0,254) + 1; //conservative, 0 is 1, so <1 is considered solid
}
imageStore(sdf_tex,pos,uvec4(dist_8));
}
#endif

View file

@ -344,17 +344,30 @@ FRAGMENT_SHADER_GLOBALS
/* clang-format on */
#ifdef MODE_RENDER_DEPTH
#ifdef MODE_RENDER_MATERIAL
layout(location = 0) out vec4 albedo_output_buffer;
layout(location = 1) out vec4 normal_output_buffer;
layout(location = 2) out vec4 orm_output_buffer;
layout(location = 3) out vec4 emission_output_buffer;
layout(location = 4) out float depth_output_buffer;
#endif
#else // RENDER DEPTH
#ifdef MODE_MULTIPLE_RENDER_TARGETS
layout(location = 0) out vec4 diffuse_buffer; //diffuse (rgb) and roughness
layout(location = 1) out vec4 specular_buffer; //specular and SSS (subsurface scatter)
#else
#ifndef MODE_RENDER_DEPTH
layout(location = 0) out vec4 frag_color;
#endif
#endif
#endif // RENDER DEPTH
@ -1668,6 +1681,29 @@ FRAGMENT_SHADER_CODE
#ifdef MODE_RENDER_DEPTH
#ifdef MODE_RENDER_MATERIAL
albedo_output_buffer.rgb = albedo;
albedo_output_buffer.a = alpha;
normal_output_buffer.rgb = normal * 0.5 + 0.5;
normal_output_buffer.a = 0.0;
depth_output_buffer.r = -vertex.z;
#if defined(AO_USED)
orm_output_buffer.r = ao;
#else
orm_output_buffer.r = 0.0;
#endif
orm_output_buffer.g = roughness;
orm_output_buffer.b = metallic;
orm_output_buffer.a = sss_strength;
emission_output_buffer.rgb = emission;
emission_output_buffer.a = 0.0;
#endif
//nothing happens, so a tree-ssa optimizer will result in no fragment shader :)
#else

View file

@ -170,7 +170,12 @@ void *VisualServerScene::_instance_pair(void *p_self, OctreeElementID, Instance
pinfo.geometry = A;
pinfo.L = geom->gi_probes.push_back(B);
List<InstanceGIProbeData::PairInfo>::Element *E = gi_probe->geometries.push_back(pinfo);
List<InstanceGIProbeData::PairInfo>::Element *E;
if (A->dynamic_gi) {
E = gi_probe->dynamic_geometries.push_back(pinfo);
} else {
E = gi_probe->geometries.push_back(pinfo);
}
geom->gi_probes_dirty = true;
@ -240,7 +245,11 @@ void VisualServerScene::_instance_unpair(void *p_self, OctreeElementID, Instance
List<InstanceGIProbeData::PairInfo>::Element *E = reinterpret_cast<List<InstanceGIProbeData::PairInfo>::Element *>(udata);
geom->gi_probes.erase(E->get().L);
gi_probe->geometries.erase(E);
if (A->dynamic_gi) {
gi_probe->dynamic_geometries.erase(E);
} else {
gi_probe->geometries.erase(E);
}
geom->gi_probes_dirty = true;
@ -842,12 +851,32 @@ void VisualServerScene::instance_geometry_set_flag(RID p_instance, VS::InstanceF
Instance *instance = instance_owner.getornull(p_instance);
ERR_FAIL_COND(!instance);
ERR_FAIL_COND(((1 << instance->base_type) & VS::INSTANCE_GEOMETRY_MASK));
switch (p_flags) {
case VS::INSTANCE_FLAG_USE_BAKED_LIGHT: {
instance->baked_light = p_enabled;
} break;
case VS::INSTANCE_FLAG_USE_DYNAMIC_GI: {
if (p_enabled == instance->dynamic_gi) {
//bye, redundant
return;
}
if (instance->octree_id != 0) {
//remove from octree, it needs to be re-paired
instance->scenario->octree.erase(instance->octree_id);
instance->octree_id = 0;
_instance_queue_update(instance, true, true);
}
//once out of octree, can be changed
instance->dynamic_gi = p_enabled;
} break;
case VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE: {
@ -2431,7 +2460,7 @@ void VisualServerScene::render_probes() {
cache_count = idx;
}
bool update_probe = VSG::scene_render->gi_probe_needs_update(probe->probe_instance);
bool update_lights = VSG::scene_render->gi_probe_needs_update(probe->probe_instance);
if (cache_dirty) {
probe->light_cache.resize(cache_count);
@ -2490,13 +2519,37 @@ void VisualServerScene::render_probes() {
}
}
update_probe = true;
update_lights = true;
}
if (update_probe) {
VSG::scene_render->gi_probe_update(probe->probe_instance, probe->light_instances);
instance_cull_count = 0;
for (List<InstanceGIProbeData::PairInfo>::Element *E = probe->dynamic_geometries.front(); E; E = E->next()) {
if (instance_cull_count < MAX_INSTANCE_CULL) {
Instance *ins = E->get().geometry;
InstanceGeometryData *geom = (InstanceGeometryData *)ins->base_data;
if (geom->gi_probes_dirty) {
//giprobes may be dirty, so update
int l = 0;
//only called when reflection probe AABB enter/exit this geometry
ins->gi_probe_instances.resize(geom->gi_probes.size());
for (List<Instance *>::Element *F = geom->gi_probes.front(); F; F = F->next()) {
InstanceGIProbeData *gi_probe2 = static_cast<InstanceGIProbeData *>(F->get()->base_data);
ins->gi_probe_instances.write[l++] = gi_probe2->probe_instance;
}
geom->gi_probes_dirty = false;
}
instance_cull_result[instance_cull_count++] = E->get().geometry;
}
}
VSG::scene_render->gi_probe_update(probe->probe_instance, update_lights, probe->light_instances, instance_cull_count, (RasterizerScene::InstanceBase **)instance_cull_result);
gi_probe_update_list.remove(gi_probe);
gi_probe = next;

View file

@ -156,8 +156,6 @@ public:
SelfList<Instance> update_item;
AABB aabb;
AABB transformed_aabb;
AABB *custom_aabb; // <Zylann> would using aabb directly with a bool be better?
float extra_margin;
uint32_t object_id;
@ -316,6 +314,7 @@ public:
};
List<PairInfo> geometries;
List<PairInfo> dynamic_geometries;
Set<Instance *> lights;

View file

@ -834,6 +834,7 @@ public:
enum InstanceFlags {
INSTANCE_FLAG_USE_BAKED_LIGHT,
INSTANCE_FLAG_USE_DYNAMIC_GI,
INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE,
INSTANCE_FLAG_MAX
};