From 749f60ee365a6d661795637838e41122aff23058 Mon Sep 17 00:00:00 2001 From: David Snopek Date: Sun, 19 Nov 2023 21:39:20 -0600 Subject: [PATCH] OpenGL: Implement rendering of lightmaps --- drivers/gles3/rasterizer_scene_gles3.cpp | 127 ++++++++++++++++++++--- drivers/gles3/rasterizer_scene_gles3.h | 14 ++- drivers/gles3/shaders/scene.glsl | 103 +++++++++++++++++- drivers/gles3/storage/light_storage.cpp | 118 +++++++++++++++++++-- drivers/gles3/storage/light_storage.h | 13 +++ scene/3d/lightmap_gi.cpp | 2 +- 6 files changed, 350 insertions(+), 27 deletions(-) diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index f4fdb5553b0..18d4ae35629 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -130,9 +130,27 @@ void RasterizerSceneGLES3::GeometryInstanceGLES3::_mark_dirty() { } void RasterizerSceneGLES3::GeometryInstanceGLES3::set_use_lightmap(RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) { + lightmap_instance = p_lightmap_instance; + lightmap_uv_scale = p_lightmap_uv_scale; + lightmap_slice_index = p_lightmap_slice_index; + + _mark_dirty(); } void RasterizerSceneGLES3::GeometryInstanceGLES3::set_lightmap_capture(const Color *p_sh9) { + if (p_sh9) { + if (lightmap_sh == nullptr) { + lightmap_sh = memnew(GeometryInstanceLightmapSH); + } + + memcpy(lightmap_sh->sh, p_sh9, sizeof(Color) * 9); + } else { + if (lightmap_sh != nullptr) { + memdelete(lightmap_sh); + lightmap_sh = nullptr; + } + } + _mark_dirty(); } void RasterizerSceneGLES3::_update_dirty_geometry_instances() { @@ -1271,12 +1289,15 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const int32_t shadow_id = GLES3::LightStorage::get_singleton()->light_instance_get_shadow_id(light_instance); if (GLES3::LightStorage::get_singleton()->light_has_shadow(light) && shadow_id >= 0) { - GeometryInstanceGLES3::LightPass pass; - pass.light_id = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance); - pass.shadow_id = shadow_id; - pass.light_instance_rid = light_instance; - pass.is_omni = true; - inst->light_passes.push_back(pass); + // Skip static lights when a lightmap is used. + if (!inst->lightmap_instance.is_valid() || GLES3::LightStorage::get_singleton()->light_get_bake_mode(light) != RenderingServer::LIGHT_BAKE_STATIC) { + GeometryInstanceGLES3::LightPass pass; + pass.light_id = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance); + pass.shadow_id = shadow_id; + pass.light_instance_rid = light_instance; + pass.is_omni = true; + inst->light_passes.push_back(pass); + } } else { // Lights without shadow can all go in base pass. inst->omni_light_gl_cache.push_back((uint32_t)GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance)); @@ -1294,11 +1315,14 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const int32_t shadow_id = GLES3::LightStorage::get_singleton()->light_instance_get_shadow_id(light_instance); if (GLES3::LightStorage::get_singleton()->light_has_shadow(light) && shadow_id >= 0) { - GeometryInstanceGLES3::LightPass pass; - pass.light_id = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance); - pass.shadow_id = shadow_id; - pass.light_instance_rid = light_instance; - inst->light_passes.push_back(pass); + // Skip static lights when a lightmap is used. + if (!inst->lightmap_instance.is_valid() || GLES3::LightStorage::get_singleton()->light_get_bake_mode(light) != RenderingServer::LIGHT_BAKE_STATIC) { + GeometryInstanceGLES3::LightPass pass; + pass.light_id = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance); + pass.shadow_id = shadow_id; + pass.light_instance_rid = light_instance; + inst->light_passes.push_back(pass); + } } else { // Lights without shadow can all go in base pass. inst->spot_light_gl_cache.push_back((uint32_t)GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance)); @@ -1610,6 +1634,8 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b light_data.direction[1] = direction.y; light_data.direction[2] = direction.z; + light_data.bake_mode = light_storage->light_get_bake_mode(base); + float sign = light_storage->light_is_negative(base) ? -1 : 1; light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY); @@ -1758,6 +1784,8 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b light_data.position[1] = pos.y; light_data.position[2] = pos.z; + light_data.bake_mode = light_storage->light_get_bake_mode(base); + float radius = MAX(0.001, light_storage->light_get_param(base, RS::LIGHT_PARAM_RANGE)); light_data.inv_radius = 1.0 / radius; @@ -2621,6 +2649,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, bool uses_additive_lighting = (inst->light_passes.size() + p_render_data->directional_shadow_count) > 0; uses_additive_lighting = uses_additive_lighting && !shader->unshaded; + // TODOS /* * Still a bug when atlas space is limited. Somehow need to evict light when it doesn't have a spot on the atlas, current check isn't enough @@ -2650,6 +2679,12 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, // Shadow wasn't able to get a spot on the atlas. So skip it. continue; } + } else if (pass > 0) { + uint32_t shadow_id = MAX_DIRECTIONAL_LIGHTS - 1 - (pass - int32_t(inst->light_passes.size())); + if (inst->lightmap_instance.is_valid() && scene_state.directional_lights[shadow_id].bake_mode == RenderingServer::LIGHT_BAKE_STATIC) { + // Skip shadows for static lights on meshes with a lightmap. + continue; + } } } @@ -2738,12 +2773,16 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, GLuint vertex_array_gl = 0; GLuint index_array_gl = 0; + uint64_t vertex_input_mask = shader->vertex_input_mask; + if (inst->lightmap_instance.is_valid()) { + vertex_input_mask |= 1 << RS::ARRAY_TEX_UV2; + } - //skeleton and blend shape + // Skeleton and blend shapes. if (surf->owner->mesh_instance.is_valid()) { - mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, shader->vertex_input_mask, vertex_array_gl); + mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, vertex_input_mask, vertex_array_gl); } else { - mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, shader->vertex_input_mask, vertex_array_gl); + mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, vertex_input_mask, vertex_array_gl); } index_array_gl = mesh_storage->mesh_surface_get_index_buffer(mesh_surface, surf->lod_index); @@ -2803,12 +2842,28 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, if (p_render_data->directional_light_count == p_render_data->directional_shadow_count) { spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL; } + + if (inst->lightmap_instance.is_valid()) { + spec_constants |= SceneShaderGLES3::USE_LIGHTMAP; + + GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance); + GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap); + + if (lm->uses_spherical_harmonics) { + spec_constants |= SceneShaderGLES3::USE_SH_LIGHTMAP; + } + } else if (inst->lightmap_sh) { + spec_constants |= SceneShaderGLES3::USE_LIGHTMAP_CAPTURE; + } else { + spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP; + } } else { // Only base pass uses the radiance map. spec_constants &= ~SceneShaderGLES3::USE_RADIANCE_MAP; spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_OMNI; spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_SPOT; spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL; + spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP; } if (uses_additive_lighting) { @@ -2831,6 +2886,10 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, // Render directional lights. uint32_t shadow_id = MAX_DIRECTIONAL_LIGHTS - 1 - (pass - int32_t(inst->light_passes.size())); + if (pass == 0 && inst->lightmap_instance.is_valid() && scene_state.directional_lights[shadow_id].bake_mode == RenderingServer::LIGHT_BAKE_STATIC) { + // Disable additive lighting with a static light and a lightmap. + spec_constants &= ~SceneShaderGLES3::USE_ADDITIVE_LIGHTING; + } if (scene_state.directional_shadows[shadow_id].shadow_split_offsets[0] == scene_state.directional_shadows[shadow_id].shadow_split_offsets[1]) { // Orthogonal, do nothing. } else if (scene_state.directional_shadows[shadow_id].shadow_split_offsets[1] == scene_state.directional_shadows[shadow_id].shadow_split_offsets[2]) { @@ -2920,6 +2979,46 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::SPOT_LIGHT_INDICES, shader->version, instance_variant, spec_constants), inst->spot_light_gl_cache.size(), inst->spot_light_gl_cache.ptr()); } + if (inst->lightmap_instance.is_valid()) { + GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance); + GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap); + + GLuint tex = GLES3::TextureStorage::get_singleton()->texture_get_texid(lm->light_texture); + glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 4); + glBindTexture(GL_TEXTURE_2D_ARRAY, tex); + + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_SLICE, inst->lightmap_slice_index, shader->version, instance_variant, spec_constants); + + Vector4 uv_scale(inst->lightmap_uv_scale.position.x, inst->lightmap_uv_scale.position.y, inst->lightmap_uv_scale.size.x, inst->lightmap_uv_scale.size.y); + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_UV_SCALE, uv_scale, shader->version, instance_variant, spec_constants); + + float exposure_normalization = 1.0; + if (p_render_data->camera_attributes.is_valid()) { + float enf = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + exposure_normalization = enf / lm->baked_exposure; + } + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_EXPOSURE_NORMALIZATION, exposure_normalization, shader->version, instance_variant, spec_constants); + + if (lm->uses_spherical_harmonics) { + Basis to_lm = li->transform.basis.inverse() * p_render_data->cam_transform.basis; + to_lm = to_lm.inverse().transposed(); + GLfloat matrix[9] = { + (GLfloat)to_lm.rows[0][0], + (GLfloat)to_lm.rows[1][0], + (GLfloat)to_lm.rows[2][0], + (GLfloat)to_lm.rows[0][1], + (GLfloat)to_lm.rows[1][1], + (GLfloat)to_lm.rows[2][1], + (GLfloat)to_lm.rows[0][2], + (GLfloat)to_lm.rows[1][2], + (GLfloat)to_lm.rows[2][2], + }; + glUniformMatrix3fv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::LIGHTMAP_NORMAL_XFORM, shader->version, instance_variant, spec_constants), 1, GL_FALSE, matrix); + } + } else if (inst->lightmap_sh) { + glUniform4fv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::LIGHTMAP_CAPTURES, shader->version, instance_variant, spec_constants), 9, reinterpret_cast(inst->lightmap_sh->sh)); + } + prev_inst = inst; } } diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index 7d3c8896dad..665cc5d8a37 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -170,6 +170,9 @@ private: float cos_spot_angle; float specular_amount; float shadow_opacity; + + float pad[3]; + uint32_t bake_mode; }; static_assert(sizeof(LightData) % 16 == 0, "LightData size must be a multiple of 16 bytes"); @@ -181,7 +184,7 @@ private: float size; uint32_t enabled; // For use by SkyShaders - float pad; + uint32_t bake_mode; float shadow_opacity; float specular; }; @@ -269,6 +272,10 @@ private: GeometryInstanceGLES3 *owner = nullptr; }; + struct GeometryInstanceLightmapSH { + Color sh[9]; + }; + class GeometryInstanceGLES3 : public RenderGeometryInstanceBase { public: //used during rendering @@ -296,6 +303,11 @@ private: LocalVector omni_light_gl_cache; LocalVector spot_light_gl_cache; + RID lightmap_instance; + Rect2 lightmap_uv_scale; + uint32_t lightmap_slice_index; + GeometryInstanceLightmapSH *lightmap_sh = nullptr; + // Used during setup. GeometryInstanceSurface *surface_caches = nullptr; SelfList dirty_list_element; diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 96e357e29e5..1d9ba623c45 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -1,7 +1,7 @@ /* clang-format off */ #[modes] -mode_color = +mode_color = mode_color_instancing = \n#define USE_INSTANCING mode_depth = #define MODE_RENDER_DEPTH mode_depth_instancing = #define MODE_RENDER_DEPTH \n#define USE_INSTANCING @@ -14,6 +14,9 @@ DISABLE_LIGHT_OMNI = false DISABLE_LIGHT_SPOT = false DISABLE_FOG = false USE_RADIANCE_MAP = true +USE_LIGHTMAP = false +USE_SH_LIGHTMAP = false +USE_LIGHTMAP_CAPTURE = false USE_MULTIVIEW = false RENDER_SHADOWS = false RENDER_SHADOWS_LINEAR = false @@ -676,6 +679,10 @@ multiview_data; /* clang-format on */ +#define LIGHT_BAKE_DISABLED 0u +#define LIGHT_BAKE_STATIC 1u +#define LIGHT_BAKE_DYNAMIC 2u + #ifndef MODE_RENDER_DEPTH // Directional light data. #if !defined(DISABLE_LIGHT_DIRECTIONAL) || (!defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT)) @@ -685,7 +692,8 @@ struct DirectionalLightData { mediump float energy; mediump vec3 color; mediump float size; - mediump vec2 pad; + lowp uint unused; + lowp uint bake_mode; mediump float shadow_opacity; mediump float specular; }; @@ -718,6 +726,9 @@ struct LightData { // This structure needs to be as packed as possible. mediump float cone_angle; mediump float specular_amount; mediump float shadow_opacity; + + lowp vec3 pad; + lowp uint bake_mode; }; #if !defined(DISABLE_LIGHT_OMNI) || defined(ADDITIVE_OMNI) @@ -834,6 +845,23 @@ float sample_shadow(highp sampler2DShadow shadow, float shadow_pixel_size, vec4 #endif // !MODE_RENDER_DEPTH +#ifndef DISABLE_LIGHTMAP +#ifdef USE_LIGHTMAP +uniform mediump sampler2DArray lightmap_textures; //texunit:-4 +uniform lowp uint lightmap_slice; +uniform highp vec4 lightmap_uv_scale; +uniform float lightmap_exposure_normalization; + +#ifdef USE_SH_LIGHTMAP +uniform mediump mat3 lightmap_normal_xform; +#endif // USE_SH_LIGHTMAP +#endif // USE_LIGHTMAP + +#ifdef USE_LIGHTMAP_CAPTURE +uniform mediump vec4[9] lightmap_captures; +#endif // USE_LIGHTMAP_CAPTURE +#endif // !DISABLE_LIGHTMAP + #ifdef USE_MULTIVIEW uniform highp sampler2DArray depth_buffer; // texunit:-6 uniform highp sampler2DArray color_buffer; // texunit:-5 @@ -1406,7 +1434,6 @@ void main() { #endif // Calculate Reflection probes - // Calculate Lightmaps #if defined(CUSTOM_RADIANCE_USED) specular_light = mix(specular_light, custom_radiance.rgb, custom_radiance.a); @@ -1431,6 +1458,61 @@ void main() { ambient_light = mix(ambient_light, custom_irradiance.rgb, custom_irradiance.a); #endif // CUSTOM_IRRADIANCE_USED +#ifndef DISABLE_LIGHTMAP +#ifdef USE_LIGHTMAP_CAPTURE + { + vec3 wnormal = mat3(scene_data.inv_view_matrix) * normal; + const float c1 = 0.429043; + const float c2 = 0.511664; + const float c3 = 0.743125; + const float c4 = 0.886227; + const float c5 = 0.247708; + ambient_light += (c1 * lightmap_captures[8].rgb * (wnormal.x * wnormal.x - wnormal.y * wnormal.y) + + c3 * lightmap_captures[6].rgb * wnormal.z * wnormal.z + + c4 * lightmap_captures[0].rgb - + c5 * lightmap_captures[6].rgb + + 2.0 * c1 * lightmap_captures[4].rgb * wnormal.x * wnormal.y + + 2.0 * c1 * lightmap_captures[7].rgb * wnormal.x * wnormal.z + + 2.0 * c1 * lightmap_captures[5].rgb * wnormal.y * wnormal.z + + 2.0 * c2 * lightmap_captures[3].rgb * wnormal.x + + 2.0 * c2 * lightmap_captures[1].rgb * wnormal.y + + 2.0 * c2 * lightmap_captures[2].rgb * wnormal.z) * + scene_data.emissive_exposure_normalization; + } +#else +#ifdef USE_LIGHTMAP + { + vec3 uvw; + uvw.xy = uv2 * lightmap_uv_scale.zw + lightmap_uv_scale.xy; + uvw.z = float(lightmap_slice); + +#ifdef USE_SH_LIGHTMAP + uvw.z *= 4.0; // SH textures use 4 times more data. + vec3 lm_light_l0 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; + vec3 lm_light_l1n1 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb; + vec3 lm_light_l1_0 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; + vec3 lm_light_l1p1 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; + + vec3 n = normalize(lightmap_normal_xform * normal); + + ambient_light += lm_light_l0 * 0.282095f; + ambient_light += lm_light_l1n1 * 0.32573 * n.y * lightmap_exposure_normalization; + ambient_light += lm_light_l1_0 * 0.32573 * n.z * lightmap_exposure_normalization; + ambient_light += lm_light_l1p1 * 0.32573 * n.x * lightmap_exposure_normalization; + if (metallic > 0.01) { // Since the more direct bounced light is lost, we can kind of fake it with this trick. + vec3 r = reflect(normalize(-vertex), normal); + specular_light += lm_light_l1n1 * 0.32573 * r.y * lightmap_exposure_normalization; + specular_light += lm_light_l1_0 * 0.32573 * r.z * lightmap_exposure_normalization; + specular_light += lm_light_l1p1 * 0.32573 * r.x * lightmap_exposure_normalization; + } +#else + ambient_light += textureLod(lightmap_textures, uvw, 0.0).rgb * lightmap_exposure_normalization; +#endif + } +#endif // USE_LIGHTMAP +#endif // USE_LIGHTMAP_CAPTURE +#endif // !DISABLE_LIGHTMAP + { #if defined(AMBIENT_LIGHT_DISABLED) ambient_light = vec3(0.0, 0.0, 0.0); @@ -1466,6 +1548,11 @@ void main() { #ifndef DISABLE_LIGHT_DIRECTIONAL for (uint i = uint(0); i < scene_data.directional_light_count; i++) { +#if defined(USE_LIGHTMAP) && !defined(DISABLE_LIGHTMAP) + if (directional_lights[i].bake_mode == LIGHT_BAKE_STATIC) { + continue; + } +#endif light_compute(normal, normalize(directional_lights[i].direction), normalize(view), directional_lights[i].size, directional_lights[i].color * directional_lights[i].energy, true, 1.0, f0, roughness, metallic, 1.0, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, @@ -1490,6 +1577,11 @@ void main() { if (i >= omni_light_count) { break; } +#if defined(USE_LIGHTMAP) && !defined(DISABLE_LIGHTMAP) + if (omni_lights[omni_light_indices[i]].bake_mode == LIGHT_BAKE_STATIC) { + continue; + } +#endif light_process_omni(omni_light_indices[i], vertex, view, normal, f0, roughness, metallic, 1.0, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, @@ -1513,6 +1605,11 @@ void main() { if (i >= spot_light_count) { break; } +#if defined(USE_LIGHTMAP) && !defined(DISABLE_LIGHTMAP) + if (spot_lights[spot_light_indices[i]].bake_mode == LIGHT_BAKE_STATIC) { + continue; + } +#endif light_process_spot(spot_light_indices[i], vertex, view, normal, f0, roughness, metallic, 1.0, albedo, alpha, #ifdef LIGHT_BACKLIGHT_USED backlight, diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp index 6d4d23bd10c..af8d629e5a2 100644 --- a/drivers/gles3/storage/light_storage.cpp +++ b/drivers/gles3/storage/light_storage.cpp @@ -569,69 +569,171 @@ void LightStorage::lightmap_initialize(RID p_rid) { void LightStorage::lightmap_free(RID p_rid) { Lightmap *lightmap = lightmap_owner.get_or_null(p_rid); + ERR_FAIL_NULL(lightmap); lightmap->dependency.deleted_notify(p_rid); lightmap_owner.free(p_rid); } void LightStorage::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) { + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lightmap); + lightmap->light_texture = p_light; + lightmap->uses_spherical_harmonics = p_uses_spherical_haromics; + + GLuint tex = GLES3::TextureStorage::get_singleton()->texture_get_texid(lightmap->light_texture); + glBindTexture(GL_TEXTURE_2D_ARRAY, tex); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); } void LightStorage::lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) { + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lightmap); + lightmap->bounds = p_bounds; } void LightStorage::lightmap_set_probe_interior(RID p_lightmap, bool p_interior) { + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lightmap); + lightmap->interior = p_interior; } void LightStorage::lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) { + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lightmap); + + if (p_points.size()) { + ERR_FAIL_COND(p_points.size() * 9 != p_point_sh.size()); + ERR_FAIL_COND((p_tetrahedra.size() % 4) != 0); + ERR_FAIL_COND((p_bsp_tree.size() % 6) != 0); + } + + lightmap->points = p_points; + lightmap->point_sh = p_point_sh; + lightmap->tetrahedra = p_tetrahedra; + lightmap->bsp_tree = p_bsp_tree; } void LightStorage::lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) { + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lightmap); + + lightmap->baked_exposure = p_exposure; } PackedVector3Array LightStorage::lightmap_get_probe_capture_points(RID p_lightmap) const { - return PackedVector3Array(); + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lightmap, PackedVector3Array()); + return lightmap->points; } PackedColorArray LightStorage::lightmap_get_probe_capture_sh(RID p_lightmap) const { - return PackedColorArray(); + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lightmap, PackedColorArray()); + return lightmap->point_sh; } PackedInt32Array LightStorage::lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const { - return PackedInt32Array(); + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lightmap, PackedInt32Array()); + return lightmap->tetrahedra; } PackedInt32Array LightStorage::lightmap_get_probe_capture_bsp_tree(RID p_lightmap) const { - return PackedInt32Array(); + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lightmap, PackedInt32Array()); + return lightmap->bsp_tree; } AABB LightStorage::lightmap_get_aabb(RID p_lightmap) const { - return AABB(); + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lightmap, AABB()); + return lightmap->bounds; } void LightStorage::lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, Color *r_sh) { + Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lm); + + for (int i = 0; i < 9; i++) { + r_sh[i] = Color(0, 0, 0, 0); + } + + if (!lm->points.size() || !lm->bsp_tree.size() || !lm->tetrahedra.size()) { + return; + } + + static_assert(sizeof(Lightmap::BSP) == 24); + + const Lightmap::BSP *bsp = (const Lightmap::BSP *)lm->bsp_tree.ptr(); + int32_t node = 0; + while (node >= 0) { + if (Plane(bsp[node].plane[0], bsp[node].plane[1], bsp[node].plane[2], bsp[node].plane[3]).is_point_over(p_point)) { +#ifdef DEBUG_ENABLED + ERR_FAIL_COND(bsp[node].over >= 0 && bsp[node].over < node); +#endif + + node = bsp[node].over; + } else { +#ifdef DEBUG_ENABLED + ERR_FAIL_COND(bsp[node].under >= 0 && bsp[node].under < node); +#endif + node = bsp[node].under; + } + } + + if (node == Lightmap::BSP::EMPTY_LEAF) { + return; // Nothing could be done. + } + + node = ABS(node) - 1; + + uint32_t *tetrahedron = (uint32_t *)&lm->tetrahedra[node * 4]; + Vector3 points[4] = { lm->points[tetrahedron[0]], lm->points[tetrahedron[1]], lm->points[tetrahedron[2]], lm->points[tetrahedron[3]] }; + const Color *sh_colors[4]{ &lm->point_sh[tetrahedron[0] * 9], &lm->point_sh[tetrahedron[1] * 9], &lm->point_sh[tetrahedron[2] * 9], &lm->point_sh[tetrahedron[3] * 9] }; + Color barycentric = Geometry3D::tetrahedron_get_barycentric_coords(points[0], points[1], points[2], points[3], p_point); + + for (int i = 0; i < 4; i++) { + float c = CLAMP(barycentric[i], 0.0, 1.0); + for (int j = 0; j < 9; j++) { + r_sh[j] += sh_colors[i][j] * c; + } + } } bool LightStorage::lightmap_is_interior(RID p_lightmap) const { - return false; + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lightmap, false); + return lightmap->interior; } void LightStorage::lightmap_set_probe_capture_update_speed(float p_speed) { + lightmap_probe_capture_update_speed = p_speed; } float LightStorage::lightmap_get_probe_capture_update_speed() const { - return 0; + return lightmap_probe_capture_update_speed; } /* LIGHTMAP INSTANCE */ RID LightStorage::lightmap_instance_create(RID p_lightmap) { - return RID(); + LightmapInstance li; + li.lightmap = p_lightmap; + return lightmap_instance_owner.make_rid(li); } void LightStorage::lightmap_instance_free(RID p_lightmap) { + lightmap_instance_owner.free(p_lightmap); } void LightStorage::lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) { + LightmapInstance *li = lightmap_instance_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(li); + li->transform = p_transform; } /* SHADOW ATLAS API */ diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h index 2fb4dcaeca1..7ab02860989 100644 --- a/drivers/gles3/storage/light_storage.h +++ b/drivers/gles3/storage/light_storage.h @@ -155,6 +155,11 @@ struct Lightmap { Dependency dependency; }; +struct LightmapInstance { + RID lightmap; + Transform3D transform; +}; + class LightStorage : public RendererLightStorage { public: enum ShadowAtlastQuadrant { @@ -179,9 +184,14 @@ private: /* LIGHTMAP */ Vector lightmap_textures; + float lightmap_probe_capture_update_speed = 4; mutable RID_Owner lightmap_owner; + /* LIGHTMAP INSTANCE */ + + mutable RID_Owner lightmap_instance_owner; + /* SHADOW ATLAS */ // Note: The ShadowAtlas in the OpenGL is virtual. Each light gets assigned its @@ -622,6 +632,9 @@ public: /* LIGHTMAP INSTANCE */ + LightmapInstance *get_lightmap_instance(RID p_rid) { return lightmap_instance_owner.get_or_null(p_rid); }; + bool owns_lightmap_instance(RID p_rid) { return lightmap_instance_owner.owns(p_rid); }; + virtual RID lightmap_instance_create(RID p_lightmap) override; virtual void lightmap_instance_free(RID p_lightmap) override; virtual void lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) override; diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 4ba039becd6..76933cd9561 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -1523,7 +1523,7 @@ PackedStringArray LightmapGI::get_configuration_warnings() const { PackedStringArray warnings = Node::get_configuration_warnings(); if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") { - warnings.push_back(RTR("LightmapGI nodes are not supported when using the GL Compatibility backend yet. Support will be added in a future release.")); + warnings.push_back(RTR("Lightmap cannot be baked when using the GL Compatibility backend yet. Support will be added in a future release.")); return warnings; }