Merge pull request #62054 from techiepriyansh/soft-shadows-lightmapper
Add support for soft shadows to the GPU lightmapper
This commit is contained in:
commit
60238226d3
|
@ -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");
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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++) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue