Merge pull request #62054 from techiepriyansh/soft-shadows-lightmapper

Add support for soft shadows to the GPU lightmapper
This commit is contained in:
Rémi Verschelde 2022-06-24 22:29:39 +02:00 committed by GitHub
commit 60238226d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 104 additions and 51 deletions

View File

@ -51,7 +51,7 @@ void LightmapperRD::add_mesh(const MeshData &p_mesh) {
mesh_instances.push_back(mi); mesh_instances.push_back(mi);
} }
void LightmapperRD::add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance) { void LightmapperRD::add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance, float p_shadow_blur) {
Light l; Light l;
l.type = LIGHT_TYPE_DIRECTIONAL; l.type = LIGHT_TYPE_DIRECTIONAL;
l.direction[0] = p_direction.x; l.direction[0] = p_direction.x;
@ -62,11 +62,12 @@ void LightmapperRD::add_directional_light(bool p_static, const Vector3 &p_direct
l.color[2] = p_color.b; l.color[2] = p_color.b;
l.energy = p_energy; l.energy = p_energy;
l.static_bake = p_static; l.static_bake = p_static;
l.size = p_angular_distance; l.size = Math::tan(Math::deg2rad(p_angular_distance));
l.shadow_blur = p_shadow_blur;
lights.push_back(l); lights.push_back(l);
} }
void LightmapperRD::add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size) { void LightmapperRD::add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) {
Light l; Light l;
l.type = LIGHT_TYPE_OMNI; l.type = LIGHT_TYPE_OMNI;
l.position[0] = p_position.x; l.position[0] = p_position.x;
@ -80,10 +81,11 @@ void LightmapperRD::add_omni_light(bool p_static, const Vector3 &p_position, con
l.energy = p_energy; l.energy = p_energy;
l.static_bake = p_static; l.static_bake = p_static;
l.size = p_size; l.size = p_size;
l.shadow_blur = p_shadow_blur;
lights.push_back(l); lights.push_back(l);
} }
void LightmapperRD::add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size) { void LightmapperRD::add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) {
Light l; Light l;
l.type = LIGHT_TYPE_SPOT; l.type = LIGHT_TYPE_SPOT;
l.position[0] = p_position.x; l.position[0] = p_position.x;
@ -102,6 +104,7 @@ void LightmapperRD::add_spot_light(bool p_static, const Vector3 &p_position, con
l.energy = p_energy; l.energy = p_energy;
l.static_bake = p_static; l.static_bake = p_static;
l.size = p_size; l.size = p_size;
l.shadow_blur = p_shadow_blur;
lights.push_back(l); lights.push_back(l);
} }
@ -1140,6 +1143,23 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
RID light_uniform_set = rd->uniform_set_create(uniforms, compute_shader_primary, 1); RID light_uniform_set = rd->uniform_set_create(uniforms, compute_shader_primary, 1);
switch (p_quality) {
case BAKE_QUALITY_LOW: {
push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/low_quality_ray_count");
} break;
case BAKE_QUALITY_MEDIUM: {
push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/medium_quality_ray_count");
} break;
case BAKE_QUALITY_HIGH: {
push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/high_quality_ray_count");
} break;
case BAKE_QUALITY_ULTRA: {
push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/ultra_quality_ray_count");
} break;
}
push_constant.ray_count = CLAMP(push_constant.ray_count, 16u, 8192u);
RD::ComputeListID compute_list = rd->compute_list_begin(); RD::ComputeListID compute_list = rd->compute_list_begin();
rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_primary_pipeline); rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_primary_pipeline);
rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0); rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0);
@ -1230,23 +1250,6 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
uniforms.write[1].set_id(0, light_dest_tex); uniforms.write[1].set_id(0, light_dest_tex);
secondary_uniform_set[1] = rd->uniform_set_create(uniforms, compute_shader_secondary, 1); secondary_uniform_set[1] = rd->uniform_set_create(uniforms, compute_shader_secondary, 1);
switch (p_quality) {
case BAKE_QUALITY_LOW: {
push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/low_quality_ray_count");
} break;
case BAKE_QUALITY_MEDIUM: {
push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/medium_quality_ray_count");
} break;
case BAKE_QUALITY_HIGH: {
push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/high_quality_ray_count");
} break;
case BAKE_QUALITY_ULTRA: {
push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/ultra_quality_ray_count");
} break;
}
push_constant.ray_count = CLAMP(push_constant.ray_count, 16u, 8192u);
int max_region_size = nearest_power_of_2_templated(int(GLOBAL_GET("rendering/lightmapping/bake_performance/region_size"))); int max_region_size = nearest_power_of_2_templated(int(GLOBAL_GET("rendering/lightmapping/bake_performance/region_size")));
int max_rays = GLOBAL_GET("rendering/lightmapping/bake_performance/max_rays_per_pass"); int max_rays = GLOBAL_GET("rendering/lightmapping/bake_performance/max_rays_per_pass");

View File

@ -57,8 +57,9 @@ class LightmapperRD : public Lightmapper {
float attenuation = 0.0; float attenuation = 0.0;
float cos_spot_angle = 0.0; float cos_spot_angle = 0.0;
float inv_spot_attenuation = 0.0; float inv_spot_attenuation = 0.0;
float shadow_blur = 0.0;
uint32_t static_bake = 0; uint32_t static_bake = 0;
uint32_t pad[3] = {}; uint32_t pad[2] = {};
bool operator<(const Light &p_light) const { bool operator<(const Light &p_light) const {
return type < p_light.type; return type < p_light.type;
@ -236,9 +237,9 @@ class LightmapperRD : public Lightmapper {
public: public:
virtual void add_mesh(const MeshData &p_mesh) override; virtual void add_mesh(const MeshData &p_mesh) override;
virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance) override; virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance, float p_shadow_blur) override;
virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size) override; virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) override;
virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size) override; virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) override;
virtual void add_probe(const Vector3 &p_position) override; virtual void add_probe(const Vector3 &p_position) override;
virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_bake_userdata = nullptr) override; virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_bake_userdata = nullptr) override;

View File

@ -51,8 +51,9 @@ struct Light {
float cos_spot_angle; float cos_spot_angle;
float inv_spot_attenuation; float inv_spot_attenuation;
float shadow_blur;
bool static_bake; bool static_bake;
uint pad[3]; uint pad[2];
}; };
layout(set = 0, binding = 4, std430) restrict readonly buffer Lights { layout(set = 0, binding = 4, std430) restrict readonly buffer Lights {

View File

@ -316,19 +316,24 @@ void main() {
for (uint i = 0; i < params.light_count; i++) { for (uint i = 0; i < params.light_count; i++) {
vec3 light_pos; vec3 light_pos;
float dist;
float attenuation; float attenuation;
float soft_shadowing_disk_size;
if (lights.data[i].type == LIGHT_TYPE_DIRECTIONAL) { if (lights.data[i].type == LIGHT_TYPE_DIRECTIONAL) {
vec3 light_vec = lights.data[i].direction; vec3 light_vec = lights.data[i].direction;
light_pos = position - light_vec * length(params.world_size); light_pos = position - light_vec * length(params.world_size);
dist = length(params.world_size);
attenuation = 1.0; attenuation = 1.0;
soft_shadowing_disk_size = lights.data[i].size;
} else { } else {
light_pos = lights.data[i].position; light_pos = lights.data[i].position;
float d = distance(position, light_pos); dist = distance(position, light_pos);
if (d > lights.data[i].range) { if (dist > lights.data[i].range) {
continue; continue;
} }
soft_shadowing_disk_size = lights.data[i].size / dist;
attenuation = get_omni_attenuation(d, 1.0 / lights.data[i].range, lights.data[i].attenuation); attenuation = get_omni_attenuation(dist, 1.0 / lights.data[i].range, lights.data[i].attenuation);
if (lights.data[i].type == LIGHT_TYPE_SPOT) { if (lights.data[i].type == LIGHT_TYPE_SPOT) {
vec3 rel = normalize(position - light_pos); vec3 rel = normalize(position - light_pos);
@ -352,27 +357,70 @@ void main() {
continue; //no need to do anything continue; //no need to do anything
} }
if (trace_ray(position + light_dir * params.bias, light_pos) == RAY_MISS) { float penumbra = 0.0;
vec3 light = lights.data[i].color * lights.data[i].energy * attenuation; if (lights.data[i].size > 0.0) {
if (lights.data[i].static_bake) { vec3 light_to_point = -light_dir;
static_light += light; vec3 aux = light_to_point.y < 0.777 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
vec3 light_to_point_tan = normalize(cross(light_to_point, aux));
vec3 light_to_point_bitan = normalize(cross(light_to_point, light_to_point_tan));
const uint shadowing_rays_check_penumbra_denom = 2;
uint shadowing_ray_count = params.ray_count;
uint hits = 0;
uint noise = random_seed(ivec3(atlas_pos, 43573547 /* some prime */));
vec3 light_disk_to_point = light_to_point;
for (uint j = 0; j < shadowing_ray_count; j++) {
// Optimization:
// Once already traced an important proportion of rays, if all are hits or misses,
// assume we're not in the penumbra so we can infer the rest would have the same result
if (j == shadowing_ray_count / shadowing_rays_check_penumbra_denom) {
if (hits == j) {
// Assume totally lit
hits = shadowing_ray_count;
break;
} else if (hits == 0) {
// Assume totally dark
hits = 0;
break;
}
}
float r = randomize(noise);
float a = randomize(noise) * 2.0 * PI;
vec2 disk_sample = (r * vec2(cos(a), sin(a))) * soft_shadowing_disk_size * lights.data[i].shadow_blur;
light_disk_to_point = normalize(light_to_point + disk_sample.x * light_to_point_tan + disk_sample.y * light_to_point_bitan);
if (trace_ray(position - light_disk_to_point * params.bias, position - light_disk_to_point * dist) == RAY_MISS) {
hits++;
}
}
penumbra = float(hits) / float(shadowing_ray_count);
} else {
if (trace_ray(position + light_dir * params.bias, light_pos) == RAY_MISS) {
penumbra = 1.0;
}
}
vec3 light = lights.data[i].color * lights.data[i].energy * attenuation * penumbra;
if (lights.data[i].static_bake) {
static_light += light;
#ifdef USE_SH_LIGHTMAPS #ifdef USE_SH_LIGHTMAPS
float c[4] = float[]( float c[4] = float[](
0.282095, //l0 0.282095, //l0
0.488603 * light_dir.y, //l1n1 0.488603 * light_dir.y, //l1n1
0.488603 * light_dir.z, //l1n0 0.488603 * light_dir.z, //l1n0
0.488603 * light_dir.x //l1p1 0.488603 * light_dir.x //l1p1
); );
for (uint j = 0; j < 4; j++) { for (uint j = 0; j < 4; j++) {
sh_accum[j].rgb += light * c[j] * (1.0 / 3.0); sh_accum[j].rgb += light * c[j] * (1.0 / 3.0);
} }
#endif #endif
} else { } else {
dynamic_light += light; dynamic_light += light;
}
} }
} }

View File

@ -979,13 +979,13 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
Color linear_color = light->get_color().srgb_to_linear(); Color linear_color = light->get_color().srgb_to_linear();
if (Object::cast_to<DirectionalLight3D>(light)) { if (Object::cast_to<DirectionalLight3D>(light)) {
DirectionalLight3D *l = Object::cast_to<DirectionalLight3D>(light); DirectionalLight3D *l = Object::cast_to<DirectionalLight3D>(light);
lightmapper->add_directional_light(light->get_bake_mode() == Light3D::BAKE_STATIC, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_SIZE)); lightmapper->add_directional_light(light->get_bake_mode() == Light3D::BAKE_STATIC, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR));
} else if (Object::cast_to<OmniLight3D>(light)) { } else if (Object::cast_to<OmniLight3D>(light)) {
OmniLight3D *l = Object::cast_to<OmniLight3D>(light); OmniLight3D *l = Object::cast_to<OmniLight3D>(light);
lightmapper->add_omni_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SIZE)); lightmapper->add_omni_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR));
} else if (Object::cast_to<SpotLight3D>(light)) { } else if (Object::cast_to<SpotLight3D>(light)) {
SpotLight3D *l = Object::cast_to<SpotLight3D>(light); SpotLight3D *l = Object::cast_to<SpotLight3D>(light);
lightmapper->add_spot_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SPOT_ANGLE), l->get_param(Light3D::PARAM_SPOT_ATTENUATION), l->get_param(Light3D::PARAM_SIZE)); lightmapper->add_spot_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SPOT_ANGLE), l->get_param(Light3D::PARAM_SPOT_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR));
} }
} }
for (int i = 0; i < probes_found.size(); i++) { for (int i = 0; i < probes_found.size(); i++) {

View File

@ -176,9 +176,9 @@ public:
}; };
virtual void add_mesh(const MeshData &p_mesh) = 0; virtual void add_mesh(const MeshData &p_mesh) = 0;
virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance) = 0; virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance, float p_shadow_blur) = 0;
virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size) = 0; virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) = 0;
virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size) = 0; virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) = 0;
virtual void add_probe(const Vector3 &p_position) = 0; virtual void add_probe(const Vector3 &p_position) = 0;
virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_step_userdata = nullptr) = 0; virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_step_userdata = nullptr) = 0;