Skip to content

Commit

Permalink
Merge pull request #89919 from BlueCube3310/bicubic-lightmap
Browse files Browse the repository at this point in the history
Implement bicubic sampling for lightmaps
  • Loading branch information
akien-mga committed Aug 20, 2024
2 parents 333f0f9 + ef9bb1a commit 8acd82f
Show file tree
Hide file tree
Showing 30 changed files with 354 additions and 15 deletions.
4 changes: 4 additions & 0 deletions doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2604,6 +2604,10 @@
- Intel GPUs: SYCL libraries
If no GPU acceleration is configured on the system, multi-threaded CPU-based denoising will be performed instead. This CPU-based denoising is significantly slower than the JNLM denoiser in most cases.
</member>
<member name="rendering/lightmapping/lightmap_gi/use_bicubic_filter" type="bool" setter="" getter="" default="true">
If [code]true[/code], applies a bicubic filter during lightmap sampling. This makes lightmaps look much smoother, at a moderate performance cost.
[b]Note:[/b] The bicubic filter exaggerates the 'bleeding' effect that occurs when a lightmap's resolution is low enough.
</member>
<member name="rendering/lightmapping/primitive_meshes/texel_size" type="float" setter="" getter="" default="0.2">
The texel_size that is used to calculate the [member Mesh.lightmap_size_hint] on [PrimitiveMesh] resources if [member PrimitiveMesh.add_uv2] is enabled.
</member>
Expand Down
7 changes: 7 additions & 0 deletions doc/classes/RenderingServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2207,6 +2207,13 @@
Set the textures on the given [param lightmap] GI instance to the texture array pointed to by the [param light] RID. If the lightmap texture was baked with [member LightmapGI.directional] set to [code]true[/code], then [param uses_sh] must also be [code]true[/code].
</description>
</method>
<method name="lightmaps_set_bicubic_filter">
<return type="void" />
<param index="0" name="enable" type="bool" />
<description>
Toggles whether a bicubic filter should be used when lightmaps are sampled. This smoothens their appearance at a performance cost.
</description>
</method>
<method name="make_sphere_mesh">
<return type="RID" />
<param index="0" name="latitudes" type="int" />
Expand Down
14 changes: 14 additions & 0 deletions drivers/gles3/rasterizer_scene_gles3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3210,6 +3210,10 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
if (lm->uses_spherical_harmonics) {
spec_constants |= SceneShaderGLES3::USE_SH_LIGHTMAP;
}

if (lightmap_bicubic_upscale) {
spec_constants |= SceneShaderGLES3::LIGHTMAP_BICUBIC_FILTER;
}
} else if (inst->lightmap_sh) {
spec_constants |= SceneShaderGLES3::USE_LIGHTMAP_CAPTURE;
} else {
Expand Down Expand Up @@ -3352,6 +3356,11 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
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);

if (lightmap_bicubic_upscale) {
Vector2 light_texture_size(lm->light_texture_size.x, lm->light_texture_size.y);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_TEXTURE_SIZE, light_texture_size, 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);
Expand Down Expand Up @@ -4047,6 +4056,10 @@ void RasterizerSceneGLES3::decals_set_filter(RS::DecalFilter p_filter) {
void RasterizerSceneGLES3::light_projectors_set_filter(RS::LightProjectorFilter p_filter) {
}

void RasterizerSceneGLES3::lightmaps_set_bicubic_filter(bool p_enable) {
lightmap_bicubic_upscale = p_enable;
}

RasterizerSceneGLES3::RasterizerSceneGLES3() {
singleton = this;

Expand All @@ -4060,6 +4073,7 @@ RasterizerSceneGLES3::RasterizerSceneGLES3() {

positional_soft_shadow_filter_set_quality((RS::ShadowQuality)(int)GLOBAL_GET("rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality"));
directional_soft_shadow_filter_set_quality((RS::ShadowQuality)(int)GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality"));
lightmaps_set_bicubic_filter(GLOBAL_GET("rendering/lightmapping/lightmap_gi/use_bicubic_filter"));

{
// Setup Lights
Expand Down
3 changes: 3 additions & 0 deletions drivers/gles3/rasterizer_scene_gles3.h
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,8 @@ class RasterizerSceneGLES3 : public RendererSceneRender {
bool glow_bicubic_upscale = false;
RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::ENV_SSR_ROUGHNESS_QUALITY_LOW;

bool lightmap_bicubic_upscale = false;

/* Sky */

struct SkyGlobals {
Expand Down Expand Up @@ -863,6 +865,7 @@ class RasterizerSceneGLES3 : public RendererSceneRender {

void decals_set_filter(RS::DecalFilter p_filter) override;
void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override;
virtual void lightmaps_set_bicubic_filter(bool p_enable) override;

RasterizerSceneGLES3();
~RasterizerSceneGLES3();
Expand Down
78 changes: 78 additions & 0 deletions drivers/gles3/shaders/scene.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ ADDITIVE_OMNI = false
ADDITIVE_SPOT = false
RENDER_MATERIAL = false
SECOND_REFLECTION_PROBE = false
LIGHTMAP_BICUBIC_FILTER = false


#[vertex]
Expand Down Expand Up @@ -925,6 +926,10 @@ uniform lowp uint lightmap_slice;
uniform highp vec4 lightmap_uv_scale;
uniform float lightmap_exposure_normalization;

#ifdef LIGHTMAP_BICUBIC_FILTER
uniform highp vec2 lightmap_texture_size;
#endif

#ifdef USE_SH_LIGHTMAP
uniform mediump mat3 lightmap_normal_xform;
#endif // USE_SH_LIGHTMAP
Expand Down Expand Up @@ -1417,6 +1422,67 @@ void reflection_process(samplerCube reflection_map,

#endif // !MODE_RENDER_DEPTH

#ifdef LIGHTMAP_BICUBIC_FILTER
// w0, w1, w2, and w3 are the four cubic B-spline basis functions
float w0(float a) {
return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0);
}

float w1(float a) {
return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0);
}

float w2(float a) {
return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0);
}

float w3(float a) {
return (1.0 / 6.0) * (a * a * a);
}

// g0 and g1 are the two amplitude functions
float g0(float a) {
return w0(a) + w1(a);
}

float g1(float a) {
return w2(a) + w3(a);
}

// h0 and h1 are the two offset functions
float h0(float a) {
return -1.0 + w1(a) / (w0(a) + w1(a));
}

float h1(float a) {
return 1.0 + w3(a) / (w2(a) + w3(a));
}

vec4 textureArray_bicubic(sampler2DArray tex, vec3 uv, vec2 texture_size) {
vec2 texel_size = vec2(1.0) / texture_size;

uv.xy = uv.xy * texture_size + vec2(0.5);

vec2 iuv = floor(uv.xy);
vec2 fuv = fract(uv.xy);

float g0x = g0(fuv.x);
float g1x = g1(fuv.x);
float h0x = h0(fuv.x);
float h1x = h1(fuv.x);
float h0y = h0(fuv.y);
float h1y = h1(fuv.y);

vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5)) * texel_size;
vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5)) * texel_size;
vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5)) * texel_size;
vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5)) * texel_size;

return (g0(fuv.y) * (g0x * texture(tex, vec3(p0, uv.z)) + g1x * texture(tex, vec3(p1, uv.z)))) +
(g1(fuv.y) * (g0x * texture(tex, vec3(p2, uv.z)) + g1x * texture(tex, vec3(p3, uv.z))));
}
#endif //LIGHTMAP_BICUBIC_FILTER

void main() {
//lay out everything, whatever is unused is optimized away anyway
vec3 vertex = vertex_interp;
Expand Down Expand Up @@ -1732,10 +1798,18 @@ void main() {

#ifdef USE_SH_LIGHTMAP
uvw.z *= 4.0; // SH textures use 4 times more data.

#ifdef LIGHTMAP_BICUBIC_FILTER
vec3 lm_light_l0 = textureArray_bicubic(lightmap_textures, uvw + vec3(0.0, 0.0, 0.0), lightmap_texture_size).rgb;
vec3 lm_light_l1n1 = textureArray_bicubic(lightmap_textures, uvw + vec3(0.0, 0.0, 1.0), lightmap_texture_size).rgb;
vec3 lm_light_l1_0 = textureArray_bicubic(lightmap_textures, uvw + vec3(0.0, 0.0, 2.0), lightmap_texture_size).rgb;
vec3 lm_light_l1p1 = textureArray_bicubic(lightmap_textures, uvw + vec3(0.0, 0.0, 3.0), lightmap_texture_size).rgb;
#else
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;
#endif

vec3 n = normalize(lightmap_normal_xform * normal);

Expand All @@ -1749,8 +1823,12 @@ void main() {
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
#ifdef LIGHTMAP_BICUBIC_FILTER
ambient_light += textureArray_bicubic(lightmap_textures, uvw, lightmap_texture_size).rgb * lightmap_exposure_normalization;
#else
ambient_light += textureLod(lightmap_textures, uvw, 0.0).rgb * lightmap_exposure_normalization;
#endif
#endif
}
#endif // USE_LIGHTMAP
Expand Down
3 changes: 3 additions & 0 deletions drivers/gles3/storage/light_storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,9 @@ void LightStorage::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_use
lightmap->light_texture = p_light;
lightmap->uses_spherical_harmonics = p_uses_spherical_haromics;

Vector3i light_texture_size = GLES3::TextureStorage::get_singleton()->texture_get_size(lightmap->light_texture);
lightmap->light_texture_size = Vector2i(light_texture_size.x, light_texture_size.y);

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);
Expand Down
1 change: 1 addition & 0 deletions drivers/gles3/storage/light_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ struct Lightmap {
bool interior = false;
AABB bounds = AABB(Vector3(), Vector3(1, 1, 1));
float baked_exposure = 1.0;
Vector2i light_texture_size;
int32_t array_index = -1; //unassigned
PackedVector3Array points;
PackedColorArray point_sh;
Expand Down
8 changes: 8 additions & 0 deletions drivers/gles3/storage/texture_storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1680,6 +1680,14 @@ uint32_t TextureStorage::texture_get_texid(RID p_texture) const {
return texture->tex_id;
}

Vector3i TextureStorage::texture_get_size(RID p_texture) const {
Texture *texture = texture_owner.get_or_null(p_texture);

ERR_FAIL_NULL_V(texture, Vector3i(0, 0, 0));

return Vector3i(texture->width, texture->height, texture->depth);
}

uint32_t TextureStorage::texture_get_width(RID p_texture) const {
Texture *texture = texture_owner.get_or_null(p_texture);

Expand Down
1 change: 1 addition & 0 deletions drivers/gles3/storage/texture_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,7 @@ class TextureStorage : public RendererTextureStorage {
void texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer = 0);
virtual Image::Format texture_get_format(RID p_texture) const override;
uint32_t texture_get_texid(RID p_texture) const;
Vector3i texture_get_size(RID p_texture) const;
uint32_t texture_get_width(RID p_texture) const;
uint32_t texture_get_height(RID p_texture) const;
uint32_t texture_get_depth(RID p_texture) const;
Expand Down
1 change: 1 addition & 0 deletions editor/editor_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ void EditorNode::_update_from_settings() {

RS::get_singleton()->decals_set_filter(RS::DecalFilter(int(GLOBAL_GET("rendering/textures/decals/filter"))));
RS::get_singleton()->light_projectors_set_filter(RS::LightProjectorFilter(int(GLOBAL_GET("rendering/textures/light_projectors/filter"))));
RS::get_singleton()->lightmaps_set_bicubic_filter(GLOBAL_GET("rendering/lightmapping/lightmap_gi/use_bicubic_filter"));

SceneTree *tree = get_tree();
tree->set_debug_collisions_color(GLOBAL_GET("debug/shapes/collision/shape_color"));
Expand Down
1 change: 1 addition & 0 deletions servers/rendering/dummy/rasterizer_scene_dummy.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ class RasterizerSceneDummy : public RendererSceneRender {

virtual void decals_set_filter(RS::DecalFilter p_filter) override {}
virtual void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override {}
virtual void lightmaps_set_bicubic_filter(bool p_enable) override {}

RasterizerSceneDummy() {}
~RasterizerSceneDummy() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1103,9 +1103,17 @@ void RenderForwardClustered::_setup_lightmaps(const RenderDataRD *p_render_data,

RID lightmap = light_storage->lightmap_instance_get_lightmap(p_lightmaps[i]);

// Transform (for directional lightmaps).
Basis to_lm = light_storage->lightmap_instance_get_transform(p_lightmaps[i]).basis.inverse() * p_cam_transform.basis;
to_lm = to_lm.inverse().transposed(); //will transform normals
RendererRD::MaterialStorage::store_transform_3x3(to_lm, scene_state.lightmaps[i].normal_xform);

// Light texture size.
Vector2i lightmap_size = light_storage->lightmap_get_light_texture_size(lightmap);
scene_state.lightmaps[i].texture_size[0] = lightmap_size[0];
scene_state.lightmaps[i].texture_size[1] = lightmap_size[1];

// Exposure.
scene_state.lightmaps[i].exposure_normalization = 1.0;
if (p_render_data->camera_attributes.is_valid()) {
float baked_exposure = light_storage->lightmap_get_baked_exposure_normalization(lightmap);
Expand Down Expand Up @@ -4242,6 +4250,11 @@ void RenderForwardClustered::_update_shader_quality_settings() {

spec_constants.push_back(sc);

sc.constant_id = SPEC_CONSTANT_USE_LIGHTMAP_BICUBIC_FILTER;
sc.bool_value = lightmap_filter_bicubic_get();

spec_constants.push_back(sc);

scene_shader.set_default_specialization_constants(spec_constants);

base_uniforms_changed(); //also need this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class RenderForwardClustered : public RendererSceneRenderRD {
SPEC_CONSTANT_DECAL_FILTER = 10,
SPEC_CONSTANT_PROJECTOR_FILTER = 11,
SPEC_CONSTANT_USE_DEPTH_FOG = 12,
SPEC_CONSTANT_USE_LIGHTMAP_BICUBIC_FILTER = 13,
};

enum {
Expand Down Expand Up @@ -235,8 +236,9 @@ class RenderForwardClustered : public RendererSceneRenderRD {

struct LightmapData {
float normal_xform[12];
float pad[3];
float texture_size[2];
float exposure_normalization;
float pad;
};

struct LightmapCaptureData {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -563,9 +563,17 @@ void RenderForwardMobile::_setup_lightmaps(const RenderDataRD *p_render_data, co

RID lightmap = light_storage->lightmap_instance_get_lightmap(p_lightmaps[i]);

// Transform (for directional lightmaps).
Basis to_lm = light_storage->lightmap_instance_get_transform(p_lightmaps[i]).basis.inverse() * p_cam_transform.basis;
to_lm = to_lm.inverse().transposed(); //will transform normals
RendererRD::MaterialStorage::store_transform_3x3(to_lm, scene_state.lightmaps[i].normal_xform);

// Light texture size.
Vector2i lightmap_size = light_storage->lightmap_get_light_texture_size(lightmap);
scene_state.lightmaps[i].texture_size[0] = lightmap_size[0];
scene_state.lightmaps[i].texture_size[1] = lightmap_size[1];

// Exposure.
scene_state.lightmaps[i].exposure_normalization = 1.0;
if (p_render_data->camera_attributes.is_valid()) {
float baked_exposure = light_storage->lightmap_get_baked_exposure_normalization(lightmap);
Expand Down Expand Up @@ -2781,6 +2789,11 @@ void RenderForwardMobile::_update_shader_quality_settings() {

spec_constants.push_back(sc);

sc.constant_id = SPEC_CONSTANT_USE_LIGHTMAP_BICUBIC_FILTER;
sc.bool_value = lightmap_filter_bicubic_get();

spec_constants.push_back(sc);

scene_shader.set_default_specialization_constants(spec_constants);

base_uniforms_changed(); //also need this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class RenderForwardMobile : public RendererSceneRenderRD {
SPEC_CONSTANT_DISABLE_FOG = 14,
SPEC_CONSTANT_USE_DEPTH_FOG = 16,
SPEC_CONSTANT_IS_MULTIMESH = 17,

SPEC_CONSTANT_USE_LIGHTMAP_BICUBIC_FILTER = 18,
};

enum {
Expand Down Expand Up @@ -208,8 +208,9 @@ class RenderForwardMobile : public RendererSceneRenderRD {

struct LightmapData {
float normal_xform[12];
float pad[3];
float texture_size[2];
float exposure_normalization;
float pad;
};

struct LightmapCaptureData {
Expand Down
9 changes: 9 additions & 0 deletions servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1040,6 +1040,14 @@ void RendererSceneRenderRD::light_projectors_set_filter(RenderingServer::LightPr
_update_shader_quality_settings();
}

void RendererSceneRenderRD::lightmaps_set_bicubic_filter(bool p_enable) {
if (lightmap_filter_bicubic == p_enable) {
return;
}
lightmap_filter_bicubic = p_enable;
_update_shader_quality_settings();
}

int RendererSceneRenderRD::get_roughness_layers() const {
return sky.roughness_layers;
}
Expand Down Expand Up @@ -1483,6 +1491,7 @@ void RendererSceneRenderRD::init() {

decals_set_filter(RS::DecalFilter(int(GLOBAL_GET("rendering/textures/decals/filter"))));
light_projectors_set_filter(RS::LightProjectorFilter(int(GLOBAL_GET("rendering/textures/light_projectors/filter"))));
lightmaps_set_bicubic_filter(GLOBAL_GET("rendering/lightmapping/lightmap_gi/use_bicubic_filter"));

cull_argument.set_page_pool(&cull_argument_pool);

Expand Down
Loading

0 comments on commit 8acd82f

Please sign in to comment.