Fix T103094: Cycles ignores small suns in Nishita sky

The background evaluation samples the sky discretely, so if the sun is
too small, it can be missed in the evaluation. To solve this, the sun is
ignored during the background evaluation and its contribution is
computed separately.
This commit is contained in:
Jeffrey Liu 2023-01-19 18:22:47 -06:00
parent 241d87e9f4
commit 05bdef7ce6
Notes: blender-bot 2023-04-08 14:30:39 +02:00
Referenced by issue #103094, Cycles: The sun lamp from Nitisha sky is not included in the light tree.
Referenced by issue #106293, Regression: World setup with two sky textures is lacking proper direct light sampling on the suns - 3.5+
Referenced by issue #106706, Regression: Cycles Nishita Sky Texture causes fireflies under certain conditions
12 changed files with 130 additions and 46 deletions

View File

@ -63,8 +63,9 @@ ccl_device void kernel_background_evaluate(KernelGlobals kg,
shader_setup_from_background(kg, &sd, ray_P, ray_D, ray_time);
/* Evaluate shader.
* This is being evaluated for all BSDFs, so path flag does not contain a specific type. */
const uint32_t path_flag = PATH_RAY_EMISSION;
* This is being evaluated for all BSDFs, so path flag does not contain a specific type.
* However, we want to flag the ray visibility to ignore the sun in the background map. */
const uint32_t path_flag = PATH_RAY_EMISSION | PATH_RAY_IMPORTANCE_BAKE;
surface_shader_eval<KERNEL_FEATURE_NODE_MASK_SURFACE_LIGHT &
~(KERNEL_FEATURE_NODE_RAYTRACE | KERNEL_FEATURE_NODE_LIGHT_PATH)>(
kg, INTEGRATOR_STATE_NULL, &sd, NULL, path_flag);

View File

@ -264,7 +264,6 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg,
/* Copy state from main path to shadow path. */
uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag);
shadow_flag |= (is_light) ? PATH_RAY_SHADOW_FOR_LIGHT : 0;
const Spectrum unlit_throughput = INTEGRATOR_STATE(state, path, throughput);
const Spectrum throughput = unlit_throughput * bsdf_eval_sum(&bsdf_eval);

View File

@ -838,7 +838,6 @@ ccl_device_forceinline void integrate_volume_direct_light(
const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce);
const uint16_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce);
uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag);
shadow_flag |= (is_light) ? PATH_RAY_SHADOW_FOR_LIGHT : 0;
const Spectrum throughput_phase = throughput * bsdf_eval_sum(&phase_eval);
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {

View File

@ -135,8 +135,9 @@ color sky_radiance_nishita(vector dir, float nishita_data[10], string filename)
float half_angular = angular_diameter / 2.0;
float dir_elevation = M_PI_2 - direction[0];
/* if ray inside sun disc render it, otherwise render sky */
if (sun_dir_angle < half_angular && sun_disc == 1) {
/* if ray inside sun disc render it, otherwise render sky.
* alternatively, ignore the sun if we're evaluating the background texture. */
if (sun_dir_angle < half_angular && sun_disc == 1 && raytype("importance_bake") != 1) {
/* get 2 pixels data */
color pixel_bottom = color(nishita_data[0], nishita_data[1], nishita_data[2]);
color pixel_top = color(nishita_data[3], nishita_data[4], nishita_data[5]);

View File

@ -118,6 +118,7 @@ ccl_device float3 geographical_to_direction(float lat, float lon)
ccl_device float3 sky_radiance_nishita(KernelGlobals kg,
float3 dir,
uint32_t path_flag,
float3 pixel_bottom,
float3 pixel_top,
ccl_private float *nishita_data,
@ -140,8 +141,9 @@ ccl_device float3 sky_radiance_nishita(KernelGlobals kg,
float half_angular = angular_diameter / 2.0f;
float dir_elevation = M_PI_2_F - direction.x;
/* if ray inside sun disc render it, otherwise render sky */
if (sun_disc && sun_dir_angle < half_angular) {
/* if ray inside sun disc render it, otherwise render sky.
* alternatively, ignore the sun if we're evaluating the background texture. */
if (sun_disc && sun_dir_angle < half_angular && !(path_flag & PATH_RAY_IMPORTANCE_BAKE)) {
/* get 2 pixels data */
float y;
@ -197,8 +199,12 @@ ccl_device float3 sky_radiance_nishita(KernelGlobals kg,
return xyz_to_rgb_clamped(kg, xyz);
}
ccl_device_noinline int svm_node_tex_sky(
KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset)
ccl_device_noinline int svm_node_tex_sky(KernelGlobals kg,
ccl_private ShaderData *sd,
uint32_t path_flag,
ccl_private float *stack,
uint4 node,
int offset)
{
/* Load data */
uint dir_offset = node.y;
@ -310,7 +316,8 @@ ccl_device_noinline int svm_node_tex_sky(
uint texture_id = __float_as_uint(data.z);
/* Compute Sky */
f = sky_radiance_nishita(kg, dir, pixel_bottom, pixel_top, nishita_data, texture_id);
f = sky_radiance_nishita(
kg, dir, path_flag, pixel_bottom, pixel_top, nishita_data, texture_id);
}
stack_store_float3(stack, out_offset, f);

View File

@ -463,7 +463,7 @@ ccl_device void svm_eval_nodes(KernelGlobals kg,
svm_node_tex_environment(kg, sd, stack, node);
break;
SVM_CASE(NODE_TEX_SKY)
offset = svm_node_tex_sky(kg, sd, stack, node, offset);
offset = svm_node_tex_sky(kg, sd, path_flag, stack, node, offset);
break;
SVM_CASE(NODE_TEX_GRADIENT)
svm_node_tex_gradient(sd, stack, node);

View File

@ -206,23 +206,24 @@ enum PathRayFlag : uint32_t {
PATH_RAY_SINGULAR = (1U << 5U),
PATH_RAY_TRANSPARENT = (1U << 6U),
PATH_RAY_VOLUME_SCATTER = (1U << 7U),
PATH_RAY_IMPORTANCE_BAKE = (1U << 8U),
/* Shadow ray visibility. */
PATH_RAY_SHADOW_OPAQUE = (1U << 8U),
PATH_RAY_SHADOW_TRANSPARENT = (1U << 9U),
PATH_RAY_SHADOW_OPAQUE = (1U << 9U),
PATH_RAY_SHADOW_TRANSPARENT = (1U << 10U),
PATH_RAY_SHADOW = (PATH_RAY_SHADOW_OPAQUE | PATH_RAY_SHADOW_TRANSPARENT),
/* Subset of flags used for ray visibility for intersection.
*
* NOTE: SHADOW_CATCHER macros below assume there are no more than
* 16 visibility bits. */
PATH_RAY_ALL_VISIBILITY = ((1U << 10U) - 1U),
PATH_RAY_ALL_VISIBILITY = ((1U << 11U) - 1U),
/* Special flag to tag unaligned BVH nodes.
* Only set and used in BVH nodes to distinguish how to interpret bounding box information stored
* in the node (either it should be intersected as AABB or as OBBU).
* So this can overlap with path flags. */
PATH_RAY_NODE_UNALIGNED = (1U << 10U),
PATH_RAY_NODE_UNALIGNED = (1U << 11U),
/* --------------------------------------------------------------------
* Path flags.
@ -230,60 +231,59 @@ enum PathRayFlag : uint32_t {
/* Surface had transmission component at previous bounce. Used for light tree
* traversal and culling to be consistent with MIS PDF at the next bounce. */
PATH_RAY_MIS_HAD_TRANSMISSION = (1U << 10U),
PATH_RAY_MIS_HAD_TRANSMISSION = (1U << 11U),
/* Don't apply multiple importance sampling weights to emission from
* lamp or surface hits, because they were not direct light sampled. */
PATH_RAY_MIS_SKIP = (1U << 11U),
PATH_RAY_MIS_SKIP = (1U << 12U),
/* Diffuse bounce earlier in the path, skip SSS to improve performance
* and avoid branching twice with disk sampling SSS. */
PATH_RAY_DIFFUSE_ANCESTOR = (1U << 12U),
PATH_RAY_DIFFUSE_ANCESTOR = (1U << 13U),
/* Single pass has been written. */
PATH_RAY_SINGLE_PASS_DONE = (1U << 13U),
PATH_RAY_SINGLE_PASS_DONE = (1U << 14U),
/* Zero background alpha, for camera or transparent glass rays. */
PATH_RAY_TRANSPARENT_BACKGROUND = (1U << 14U),
PATH_RAY_TRANSPARENT_BACKGROUND = (1U << 15U),
/* Terminate ray immediately at next bounce. */
PATH_RAY_TERMINATE_ON_NEXT_SURFACE = (1U << 15U),
PATH_RAY_TERMINATE_IN_NEXT_VOLUME = (1U << 16U),
PATH_RAY_TERMINATE_ON_NEXT_SURFACE = (1U << 16U),
PATH_RAY_TERMINATE_IN_NEXT_VOLUME = (1U << 17U),
/* Ray is to be terminated, but continue with transparent bounces and
* emission as long as we encounter them. This is required to make the
* MIS between direct and indirect light rays match, as shadow rays go
* through transparent surfaces to reach emission too. */
PATH_RAY_TERMINATE_AFTER_TRANSPARENT = (1U << 17U),
PATH_RAY_TERMINATE_AFTER_TRANSPARENT = (1U << 18U),
/* Terminate ray immediately after volume shading. */
PATH_RAY_TERMINATE_AFTER_VOLUME = (1U << 18U),
PATH_RAY_TERMINATE_AFTER_VOLUME = (1U << 19U),
/* Ray is to be terminated. */
PATH_RAY_TERMINATE = (PATH_RAY_TERMINATE_ON_NEXT_SURFACE | PATH_RAY_TERMINATE_IN_NEXT_VOLUME |
PATH_RAY_TERMINATE_AFTER_TRANSPARENT | PATH_RAY_TERMINATE_AFTER_VOLUME),
/* Path and shader is being evaluated for direct lighting emission. */
PATH_RAY_EMISSION = (1U << 19U),
PATH_RAY_EMISSION = (1U << 20U),
/* Perform subsurface scattering. */
PATH_RAY_SUBSURFACE_RANDOM_WALK = (1U << 20U),
PATH_RAY_SUBSURFACE_DISK = (1U << 21U),
PATH_RAY_SUBSURFACE_USE_FRESNEL = (1U << 22U),
PATH_RAY_SUBSURFACE_BACKFACING = (1U << 23U),
PATH_RAY_SUBSURFACE_RANDOM_WALK = (1U << 21U),
PATH_RAY_SUBSURFACE_DISK = (1U << 22U),
PATH_RAY_SUBSURFACE_USE_FRESNEL = (1U << 23U),
PATH_RAY_SUBSURFACE_BACKFACING = (1U << 24U),
PATH_RAY_SUBSURFACE = (PATH_RAY_SUBSURFACE_RANDOM_WALK | PATH_RAY_SUBSURFACE_DISK |
PATH_RAY_SUBSURFACE_USE_FRESNEL | PATH_RAY_SUBSURFACE_BACKFACING),
/* Contribute to denoising features. */
PATH_RAY_DENOISING_FEATURES = (1U << 24U),
PATH_RAY_DENOISING_FEATURES = (1U << 25U),
/* Render pass categories. */
PATH_RAY_SURFACE_PASS = (1U << 25U),
PATH_RAY_VOLUME_PASS = (1U << 26U),
PATH_RAY_SURFACE_PASS = (1U << 26U),
PATH_RAY_VOLUME_PASS = (1U << 27U),
PATH_RAY_ANY_PASS = (PATH_RAY_SURFACE_PASS | PATH_RAY_VOLUME_PASS),
/* Shadow ray is for a light or surface, or AO. */
PATH_RAY_SHADOW_FOR_LIGHT = (1U << 27U),
/* Shadow ray is for AO. */
PATH_RAY_SHADOW_FOR_AO = (1U << 28U),
/* A shadow catcher object was hit and the path was split into two. */

View File

@ -721,6 +721,7 @@ void LightManager::device_update_background(Device *device,
int2 environment_res = make_int2(0, 0);
Shader *shader = scene->background->get_shader(scene);
int num_suns = 0;
float sun_average_radiance = 0.0f;
foreach (ShaderNode *node, shader->graph->nodes) {
if (node->type == EnvironmentTextureNode::get_node_type()) {
EnvironmentTextureNode *env = (EnvironmentTextureNode *)node;
@ -762,6 +763,7 @@ void LightManager::device_update_background(Device *device,
/* empirical value */
kbackground->sun_weight = 4.0f;
sun_average_radiance = sky->get_sun_average_radiance();
environment_res.x = max(environment_res.x, 512);
environment_res.y = max(environment_res.y, 256);
num_suns++;
@ -830,7 +832,18 @@ void LightManager::device_update_background(Device *device,
float cdf_total = marg_cdf[res.y - 1].y + marg_cdf[res.y - 1].x / res.y;
marg_cdf[res.y].x = cdf_total;
background_light->set_average_radiance(cdf_total * M_PI_2_F);
float map_average_radiance = cdf_total * M_PI_2_F;
if (sun_average_radiance > 0.0f) {
/* The weighting here is just a heuristic that was empirically determined.
* The sun's average radiance is much higher than the map's average radiance,
* but we don't want to weight the background light too much becaused
* visibility is not accounted for anyways. */
background_light->set_average_radiance(0.8f * map_average_radiance +
0.2f * sun_average_radiance);
}
else {
background_light->set_average_radiance(map_average_radiance);
}
if (cdf_total > 0.0f)
for (int i = 1; i < res.y; i++)

View File

@ -305,14 +305,15 @@ void OSLShaderManager::shading_system_init()
/* our own ray types */
static const char *raytypes[] = {
"camera", /* PATH_RAY_CAMERA */
"reflection", /* PATH_RAY_REFLECT */
"refraction", /* PATH_RAY_TRANSMIT */
"diffuse", /* PATH_RAY_DIFFUSE */
"glossy", /* PATH_RAY_GLOSSY */
"singular", /* PATH_RAY_SINGULAR */
"transparent", /* PATH_RAY_TRANSPARENT */
"volume_scatter", /* PATH_RAY_VOLUME_SCATTER */
"camera", /* PATH_RAY_CAMERA */
"reflection", /* PATH_RAY_REFLECT */
"refraction", /* PATH_RAY_TRANSMIT */
"diffuse", /* PATH_RAY_DIFFUSE */
"glossy", /* PATH_RAY_GLOSSY */
"singular", /* PATH_RAY_SINGULAR */
"transparent", /* PATH_RAY_TRANSPARENT */
"volume_scatter", /* PATH_RAY_VOLUME_SCATTER */
"importance_bake", /* PATH_RAY_IMPORTANCE_BAKE */
"shadow", /* PATH_RAY_SHADOW_OPAQUE */
"shadow", /* PATH_RAY_SHADOW_TRANSPARENT */
@ -341,7 +342,6 @@ void OSLShaderManager::shading_system_init()
"__unused__",
"__unused__",
"__unused__",
"__unused__",
};
const int nraytypes = sizeof(raytypes) / sizeof(raytypes[0]);

View File

@ -779,6 +779,68 @@ static void sky_texture_precompute_nishita(SunSky *sunsky,
sunsky->nishita_data[9] = sun_intensity;
}
float SkyTextureNode::get_sun_average_radiance()
{
float clamped_altitude = clamp(altitude, 1.0f, 59999.0f);
float angular_diameter = get_sun_size();
float pix_bottom[3];
float pix_top[3];
SKY_nishita_skymodel_precompute_sun(sun_elevation,
angular_diameter,
clamped_altitude,
air_density,
dust_density,
pix_bottom,
pix_top);
/* Approximate the direction's elevation as the sun's elevation. */
float dir_elevation = sun_elevation;
float half_angular = angular_diameter / 2.0f;
float3 pixel_bottom = make_float3(pix_bottom[0], pix_bottom[1], pix_bottom[2]);
float3 pixel_top = make_float3(pix_top[0], pix_top[1], pix_top[2]);
/* Same code as in the sun evaluation shader. */
float3 xyz = make_float3(0.0f, 0.0f, 0.0f);
float y = 0.0f;
if (sun_elevation - half_angular > 0.0f) {
if (sun_elevation + half_angular > 0.0f) {
y = ((dir_elevation - sun_elevation) / angular_diameter) + 0.5f;
xyz = interp(pixel_bottom, pixel_top, y) * sun_intensity;
}
}
else {
if (sun_elevation + half_angular > 0.0f) {
y = dir_elevation / (sun_elevation + half_angular);
xyz = interp(pixel_bottom, pixel_top, y) * sun_intensity;
}
}
/* We first approximate the sun's contribution by
* multiplying the evaluated point by the square of the angular diameter.
* Then we scale the approximation using a piecewise function (determined empirically). */
float sun_contribution = average(xyz) * sqr(angular_diameter);
float first_point = 0.8f / 180.0f * M_PI_F;
float second_point = 1.0f / 180.0f * M_PI_F;
float third_point = M_PI_2_F;
if (angular_diameter < first_point) {
sun_contribution *= 1.0f;
}
else if (angular_diameter < second_point) {
float diff = angular_diameter - first_point;
float slope = (0.8f - 1.0f) / (second_point - first_point);
sun_contribution *= 1.0f + slope * diff;
}
else {
float diff = angular_diameter - 1.0f / 180.0f * M_PI_F;
float slope = (0.45f - 0.8f) / (third_point - second_point);
sun_contribution *= 0.8f + slope * diff;
}
return sun_contribution;
}
NODE_DEFINE(SkyTextureNode)
{
NodeType *type = NodeType::add("sky_texture", create, NodeType::SHADER);

View File

@ -174,6 +174,8 @@ class SkyTextureNode : public TextureNode {
/* Clamping for numerical precision. */
return fmaxf(sun_size, 0.0005f);
}
float get_sun_average_radiance();
};
class OutputNode : public ShaderNode {

@ -1 +1 @@
Subproject commit 6fcd157f2497d9ba4ba82191cb2abf3de11a0394
Subproject commit c0a678d3686a591eb3041cc72b60aec2857d389a