Refactor: Cycles: add helper struct Interval

To improve readability

Pull Request: https://projects.blender.org/blender/blender/pulls/130156
This commit is contained in:
Weizhen Huang
2024-11-12 12:06:09 +01:00
committed by Weizhen Huang
parent 675e8173fa
commit 93a34b1077
6 changed files with 64 additions and 49 deletions

View File

@@ -66,7 +66,7 @@ typedef struct VolumeShaderCoefficients {
typedef struct EquiangularCoefficients {
float3 P;
float2 t_range;
Interval<float> t_range;
} EquiangularCoefficients;
/* Evaluate shader to get extinction coefficient at P. */
@@ -273,8 +273,8 @@ ccl_device float volume_equiangular_sample(ccl_private const Ray *ccl_restrict r
*pdf = 0.0f;
return 0.0f;
}
const float tmin = coeffs.t_range.x;
const float tmax = coeffs.t_range.y;
const float tmin = coeffs.t_range.min;
const float tmax = coeffs.t_range.max;
const float theta_a = atan2f(tmin - delta, D);
const float theta_b = atan2f(tmax - delta, D);
const float t_ = D * tanf((xi * theta_b) + (1 - xi) * theta_a);
@@ -297,8 +297,8 @@ ccl_device float volume_equiangular_pdf(ccl_private const Ray *ccl_restrict ray,
return 0.0f;
}
const float tmin = coeffs.t_range.x;
const float tmax = coeffs.t_range.y;
const float tmin = coeffs.t_range.min;
const float tmax = coeffs.t_range.max;
const float t_ = sample_t - delta;
const float theta_a = atan2f(tmin - delta, D);
@@ -315,7 +315,7 @@ ccl_device float volume_equiangular_pdf(ccl_private const Ray *ccl_restrict ray,
ccl_device_inline bool volume_equiangular_valid_ray_segment(KernelGlobals kg,
const float3 ray_P,
const float3 ray_D,
ccl_private float2 *t_range,
ccl_private Interval<float> *t_range,
const ccl_private LightSample *ls)
{
if (ls->type == LIGHT_SPOT) {
@@ -368,9 +368,8 @@ ccl_device Spectrum volume_emission_integrate(ccl_private VolumeShaderCoefficien
/* Volume Integration */
typedef struct VolumeIntegrateState {
/* Volume segment extents. */
float tmin;
float tmax;
/* Current active segment. */
Interval<float> t;
/* If volume is absorption-only up to this point, and no probabilistic
* scattering or termination has been used yet. */
@@ -405,10 +404,8 @@ ccl_device_forceinline void volume_integrate_step_scattering(
/* Equiangular sampling for direct lighting. */
if (vstate.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR && !result.direct_scatter) {
if (result.direct_t >= vstate.tmin && result.direct_t <= vstate.tmax &&
vstate.equiangular_pdf > VOLUME_SAMPLE_PDF_CUTOFF)
{
const float new_dt = result.direct_t - vstate.tmin;
if (vstate.t.contains(result.direct_t) && vstate.equiangular_pdf > VOLUME_SAMPLE_PDF_CUTOFF) {
const float new_dt = result.direct_t - vstate.t.min;
const Spectrum new_transmittance = volume_color_transmittance(coeff.sigma_t, new_dt);
result.direct_scatter = true;
@@ -438,7 +435,7 @@ ccl_device_forceinline void volume_integrate_step_scattering(
/* compute sampling distance */
const float sample_sigma_t = volume_channel_get(coeff.sigma_t, channel);
const float new_dt = -logf(1.0f - vstate.rscatter) / sample_sigma_t;
const float new_t = vstate.tmin + new_dt;
const float new_t = vstate.t.min + new_dt;
/* transmittance and pdf */
const Spectrum new_transmittance = volume_color_transmittance(coeff.sigma_t, new_dt);
@@ -517,8 +514,8 @@ ccl_device_forceinline void volume_integrate_heterogeneous(
/* Initialize volume integration state. */
VolumeIntegrateState vstate ccl_optional_struct_init;
vstate.tmin = ray->tmin;
vstate.tmax = ray->tmin;
vstate.t.min = ray->tmin;
vstate.t.max = ray->tmin;
vstate.absorption_only = true;
vstate.rscatter = path_state_rng_1D(kg, rng_state, PRNG_VOLUME_SCATTER_DISTANCE);
vstate.rchannel = path_state_rng_1D(kg, rng_state, PRNG_VOLUME_COLOR_CHANNEL);
@@ -562,8 +559,8 @@ ccl_device_forceinline void volume_integrate_heterogeneous(
for (int i = 0; i < max_steps; i++) {
/* Advance to new position */
vstate.tmax = min(ray->tmax, ray->tmin + (i + steps_offset) * step_size);
const float shade_t = vstate.tmin + (vstate.tmax - vstate.tmin) * step_shade_offset;
vstate.t.max = min(ray->tmax, ray->tmin + (i + steps_offset) * step_size);
const float shade_t = vstate.t.min + (vstate.t.max - vstate.t.min) * step_shade_offset;
sd->P = ray->P + ray->D * shade_t;
/* compute segment */
@@ -572,7 +569,7 @@ ccl_device_forceinline void volume_integrate_heterogeneous(
const int closure_flag = sd->flag;
/* Evaluate transmittance over segment. */
const float dt = (vstate.tmax - vstate.tmin);
const float dt = (vstate.t.max - vstate.t.min);
const Spectrum transmittance = (closure_flag & SD_EXTINCTION) ?
volume_color_transmittance(coeff.sigma_t, dt) :
one_spectrum();
@@ -630,8 +627,8 @@ ccl_device_forceinline void volume_integrate_heterogeneous(
}
/* Stop if at the end of the volume. */
vstate.tmin = vstate.tmax;
if (vstate.tmin == ray->tmax) {
vstate.t.min = vstate.t.max;
if (vstate.t.min == ray->tmax) {
break;
}
}
@@ -970,7 +967,7 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
LightSample ls ccl_optional_struct_init;
const bool need_light_sample = !(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_TERMINATE);
EquiangularCoefficients equiangular_coeffs = {zero_float3(), make_float2(ray->tmin, ray->tmax)};
EquiangularCoefficients equiangular_coeffs = {zero_float3(), {ray->tmin, ray->tmax}};
const bool have_equiangular_sample =
need_light_sample && integrate_volume_equiangular_sample_light(

View File

@@ -454,7 +454,7 @@ ccl_device_forceinline float area_light_max_extent(const ccl_global KernelAreaLi
ccl_device_inline bool area_light_valid_ray_segment(const ccl_global KernelAreaLight *light,
float3 P,
float3 D,
ccl_private float2 *t_range)
ccl_private Interval<float> *t_range)
{
bool valid;
const float tan_half_spread = light->tan_half_spread;

View File

@@ -268,7 +268,7 @@ ccl_device_inline bool spot_light_sample_from_intersection(const ccl_global Kern
ccl_device_inline bool spot_light_valid_ray_segment(const ccl_global KernelLight *klight,
const float3 P,
const float3 D,
ccl_private float2 *t_range)
ccl_private Interval<float> *t_range)
{
/* Convert to local space of the spot light. */
const Transform itfm = klight->itfm;

View File

@@ -273,7 +273,7 @@ ccl_device_forceinline bool triangle_light_sample(KernelGlobals kg,
ccl_device_inline bool triangle_light_valid_ray_segment(KernelGlobals kg,
const float3 P,
const float3 D,
ccl_private float2 *t_range,
ccl_private Interval<float> *t_range,
const ccl_private LightSample *ls)
{
const int shader_flag = kernel_data_fetch(shaders, ls->shader & SHADER_MASK).flags;

View File

@@ -1055,16 +1055,6 @@ ccl_device_inline uint32_t reverse_integer_bits(uint32_t x)
#endif
}
/* Check if intervals (first->x, first->y) and (second.x, second.y) intersect, and replace the
* first interval with their intersection. */
ccl_device_inline bool intervals_intersect(ccl_private float2 *first, const float2 second)
{
first->x = fmaxf(first->x, second.x);
first->y = fminf(first->y, second.y);
return first->x < first->y;
}
/* Solve quadratic equation a*x^2 + b*x + c = 0, adapted from Mitsuba 3
* The solution is ordered so that x1 <= x2.
* Returns true if at least one solution is found. */
@@ -1095,6 +1085,30 @@ ccl_device_inline bool solve_quadratic(
return (valid_linear || valid_quadratic);
}
/* Defines a closed interval [min, max]. */
template<typename T> struct Interval {
T min;
T max;
bool is_empty() const
{
return min >= max;
}
bool contains(T value) const
{
return value >= min && value <= max;
}
};
/* Computes the intersection of two intervals. */
template<typename T>
ccl_device_inline Interval<T> intervals_intersection(ccl_private const Interval<T> &first,
ccl_private const Interval<T> &second)
{
return {max(first.min, second.min), min(first.max, second.max)};
}
CCL_NAMESPACE_END
#endif /* __UTIL_MATH_H__ */

View File

@@ -307,7 +307,7 @@ ccl_device bool ray_quad_intersect(float3 ray_P,
ccl_device bool ray_plane_intersect(const float3 N,
const float3 P,
const float3 ray_D,
ccl_private float2 *t_range)
ccl_private Interval<float> *t_range)
{
const float DN = dot(ray_D, N);
@@ -316,13 +316,13 @@ ccl_device bool ray_plane_intersect(const float3 N,
/* Limit the range to the positive side. */
if (DN > 0.0f) {
t_range->x = fmaxf(t_range->x, t);
t_range->min = fmaxf(t_range->min, t);
}
else {
t_range->y = fminf(t_range->y, t);
t_range->max = fminf(t_range->max, t);
}
return t_range->x < t_range->y;
return !t_range->is_empty();
}
/* Find the ray segment inside an axis-aligned bounding box. */
@@ -330,7 +330,7 @@ ccl_device bool ray_aabb_intersect(const float3 bbox_min,
const float3 bbox_max,
const float3 ray_P,
const float3 ray_D,
ccl_private float2 *t_range)
ccl_private Interval<float> *t_range)
{
const float3 inv_ray_D = rcp(ray_D);
@@ -339,16 +339,16 @@ ccl_device bool ray_aabb_intersect(const float3 bbox_min,
const float3 t_upper = (bbox_max - ray_P) * inv_ray_D;
/* The four t-intervals (for x-/y-/z-slabs, and ray p(t)). */
const float4 tmins = float3_to_float4(min(t_lower, t_upper), t_range->x);
const float4 tmaxes = float3_to_float4(max(t_lower, t_upper), t_range->y);
const float4 tmins = float3_to_float4(min(t_lower, t_upper), t_range->min);
const float4 tmaxes = float3_to_float4(max(t_lower, t_upper), t_range->max);
/* Max of mins and min of maxes. */
const float tmin = reduce_max(tmins);
const float tmax = reduce_min(tmaxes);
*t_range = make_float2(tmin, tmax);
*t_range = {tmin, tmax};
return tmin < tmax;
return !t_range->is_empty();
}
/* Find the segment of a ray defined by P + D * t that lies inside a cylinder defined by
@@ -357,7 +357,7 @@ ccl_device_inline bool ray_infinite_cylinder_intersect(const float3 P,
const float3 D,
const float len_u,
const float len_v,
ccl_private float2 *t_range)
ccl_private Interval<float> *t_range)
{
/* Convert to a 2D problem. */
const float2 inv_len = 1.0f / make_float2(len_u, len_v);
@@ -379,7 +379,9 @@ ccl_device_inline bool ray_infinite_cylinder_intersect(const float3 P,
float tmin, tmax;
const bool valid = solve_quadratic(a, 2.0f * b, c, tmin, tmax);
return valid && intervals_intersect(t_range, make_float2(tmin, tmax) + t_mid);
*t_range = intervals_intersection(*t_range, {tmin + t_mid, tmax + t_mid});
return valid && !t_range->is_empty();
}
/* *
@@ -389,7 +391,7 @@ ccl_device_inline bool ray_infinite_cylinder_intersect(const float3 P,
* \param P: the vector pointing from the cone apex to the ray origin
* \param D: the direction of the ray, does not need to have unit-length
* \param cos_angle_sq: `sqr(cos(half_aperture_of_the_cone))`
* \param t_range: the lower and upper bounds between which the ray lies inside the cone
* \param t_range: the ray segment that lies inside the cone
* \return whether the intersection exists and is in the provided range
*
* See https://www.geometrictools.com/Documentation/IntersectionLineCone.pdf for illustration
@@ -398,7 +400,7 @@ ccl_device_inline bool ray_cone_intersect(const float3 axis,
const float3 P,
float3 D,
const float cos_angle_sq,
ccl_private float2 *t_range)
ccl_private Interval<float> *t_range)
{
if (cos_angle_sq < 1e-4f) {
/* The cone is nearly a plane. */
@@ -433,7 +435,9 @@ ccl_device_inline bool ray_cone_intersect(const float3 axis,
tmax = FLT_MAX;
}
return valid && intervals_intersect(t_range, make_float2(tmin, tmax) * inv_len);
*t_range = intervals_intersection(*t_range, {tmin * inv_len, tmax * inv_len});
return valid && !t_range->is_empty();
}
CCL_NAMESPACE_END