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
12 changed files with 130 additions and 46 deletions

@ -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);

@ -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);

@ -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) {

@ -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]);

@ -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);

@ -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);

@ -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. */

@ -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++)

@ -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]);

@ -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);

@ -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