Add Chromium-only Blender WebEngine parity work
This commit is contained in:
53
blender-5.2.0/intern/cycles/kernel/sample/lcg.h
Normal file
53
blender-5.2.0/intern/cycles/kernel/sample/lcg.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/hash.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Linear Congruential Generator */
|
||||
|
||||
/* This is templated to handle multiple address spaces on Metal. */
|
||||
template<class T> ccl_device uint lcg_step_uint(T rng)
|
||||
{
|
||||
/* implicit mod 2^32 */
|
||||
*rng = (1103515245 * (*rng) + 12345);
|
||||
return *rng;
|
||||
}
|
||||
|
||||
/* This is templated to handle multiple address spaces on Metal. */
|
||||
template<class T> ccl_device float lcg_step_float(T rng)
|
||||
{
|
||||
/* implicit mod 2^32 */
|
||||
*rng = (1103515245 * (*rng) + 12345);
|
||||
return (float)*rng * (1.0f / (float)0xFFFFFFFF);
|
||||
}
|
||||
|
||||
template<class T> ccl_device float3 lcg_step_float3(T rng)
|
||||
{
|
||||
/* Make sure the random numbers are evaluated in order. */
|
||||
const float rand_x = lcg_step_float(rng);
|
||||
const float rand_y = lcg_step_float(rng);
|
||||
const float rand_z = lcg_step_float(rng);
|
||||
return make_float3(rand_x, rand_y, rand_z);
|
||||
}
|
||||
|
||||
ccl_device uint lcg_init(const uint seed)
|
||||
{
|
||||
uint rng = seed;
|
||||
lcg_step_uint(&rng);
|
||||
return rng;
|
||||
}
|
||||
|
||||
ccl_device_inline uint lcg_state_init(const uint rng_hash,
|
||||
const uint rng_offset,
|
||||
const uint sample,
|
||||
const uint scramble)
|
||||
{
|
||||
return hash_uint3(rng_hash ^ scramble, rng_offset, sample);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
255
blender-5.2.0/intern/cycles/kernel/sample/mapping.h
Normal file
255
blender-5.2.0/intern/cycles/kernel/sample/mapping.h
Normal file
@@ -0,0 +1,255 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 Sony Pictures Imageworks Inc., et al. All Rights Reserved.
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Adapted code from Open Shading Language. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/math.h"
|
||||
#include "util/projection.h"
|
||||
|
||||
#ifndef __KERNEL_GPU__
|
||||
# include <climits>
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Distribute 2D uniform random samples on [0, 1] over unit disk [-1, 1], with concentric mapping
|
||||
* to better preserve stratification for some RNG sequences. */
|
||||
ccl_device float2 sample_uniform_disk(const float2 rand)
|
||||
{
|
||||
float phi;
|
||||
float r;
|
||||
const float a = 2.0f * rand.x - 1.0f;
|
||||
const float b = 2.0f * rand.y - 1.0f;
|
||||
|
||||
if (a == 0.0f && b == 0.0f) {
|
||||
return zero_float2();
|
||||
}
|
||||
|
||||
if (a * a > b * b) {
|
||||
r = a;
|
||||
phi = M_PI_4_F * (b / a);
|
||||
}
|
||||
else {
|
||||
r = b;
|
||||
phi = M_PI_2_F - M_PI_4_F * (a / b);
|
||||
}
|
||||
|
||||
return polar_to_cartesian(r, phi);
|
||||
}
|
||||
|
||||
/* return an orthogonal tangent and bitangent given a normal and tangent that
|
||||
* may not be exactly orthogonal */
|
||||
ccl_device void make_orthonormals_tangent(const float3 N,
|
||||
const float3 T,
|
||||
ccl_private float3 *a,
|
||||
ccl_private float3 *b)
|
||||
{
|
||||
*b = normalize(cross(N, T));
|
||||
*a = cross(*b, N);
|
||||
}
|
||||
|
||||
ccl_device void make_orthonormals_safe_tangent(const float3 N,
|
||||
const float3 T,
|
||||
ccl_private float3 *a,
|
||||
ccl_private float3 *b)
|
||||
{
|
||||
*b = safe_normalize(cross(N, T));
|
||||
if (len_squared(*b) < 0.99f) {
|
||||
/* Normalization failed, so fall back to basic orthonormals. */
|
||||
make_orthonormals(N, a, b);
|
||||
}
|
||||
else {
|
||||
*a = cross(*b, N);
|
||||
}
|
||||
}
|
||||
|
||||
/* sample direction with cosine weighted distributed in hemisphere */
|
||||
ccl_device_inline void sample_cos_hemisphere(const float3 N,
|
||||
const float2 rand_in,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
const float2 rand = sample_uniform_disk(rand_in);
|
||||
const float costheta = safe_sqrtf(1.0f - len_squared(rand));
|
||||
|
||||
float3 T;
|
||||
float3 B;
|
||||
make_orthonormals(N, &T, &B);
|
||||
*wo = rand.x * T + rand.y * B + costheta * N;
|
||||
*pdf = costheta * M_1_PI_F;
|
||||
}
|
||||
|
||||
ccl_device_inline float pdf_cos_hemisphere(const float3 N, const float3 D)
|
||||
{
|
||||
const float cos_theta = dot(N, D);
|
||||
return cos_theta > 0 ? cos_theta * M_1_PI_F : 0.0f;
|
||||
}
|
||||
|
||||
/* sample direction uniformly distributed in hemisphere */
|
||||
ccl_device_inline void sample_uniform_hemisphere(const float3 N,
|
||||
const float2 rand,
|
||||
ccl_private float3 *wo,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
float2 xy = sample_uniform_disk(rand);
|
||||
const float z = 1.0f - len_squared(xy);
|
||||
|
||||
xy *= safe_sqrtf(z + 1.0f);
|
||||
|
||||
float3 T;
|
||||
float3 B;
|
||||
make_orthonormals(N, &T, &B);
|
||||
|
||||
*wo = xy.x * T + xy.y * B + z * N;
|
||||
*pdf = M_1_2PI_F;
|
||||
}
|
||||
|
||||
ccl_device_inline float pdf_uniform_cone(const float3 N, const float3 D, const float angle)
|
||||
{
|
||||
const float z = precise_angle(N, D);
|
||||
if (z < angle) {
|
||||
return M_1_2PI_F / one_minus_cos(angle);
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/* Uniformly sample a direction in a cone of given angle around `N`. Use concentric mapping to
|
||||
* better preserve stratification. Return the angle between `N` and the sampled direction as
|
||||
* `cos_theta`.
|
||||
* Pass `1 - cos(angle)` as argument instead of `angle` to alleviate precision issues at small
|
||||
* angles (see sphere light for reference). */
|
||||
ccl_device_inline float3 sample_uniform_cone(const float3 N,
|
||||
const float one_minus_cos_angle,
|
||||
const float2 rand,
|
||||
ccl_private float *cos_theta,
|
||||
ccl_private float *pdf)
|
||||
{
|
||||
if (one_minus_cos_angle > 0) {
|
||||
/* Remap radius to get a uniform distribution w.r.t. solid angle on the cone.
|
||||
* The logic to derive this mapping is as follows:
|
||||
*
|
||||
* Sampling a cone is comparable to sampling the hemisphere, we just restrict theta. Therefore,
|
||||
* the same trick of first sampling the unit disk and the projecting the result up towards the
|
||||
* hemisphere by calculating the appropriate z coordinate still works.
|
||||
*
|
||||
* However, by itself this results in cosine-weighted hemisphere sampling, so we need some kind
|
||||
* of remapping. Cosine-weighted hemisphere and uniform cone sampling have the same conditional
|
||||
* PDF for phi (both are constant), so we only need to think about theta, which corresponds
|
||||
* directly to the radius.
|
||||
*
|
||||
* To find this mapping, we consider the simplest sampling strategies for cosine-weighted
|
||||
* hemispheres and uniform cones. In both, phi is chosen as `2pi * random()`. For the former,
|
||||
* `r_disk(rand) = sqrt(rand)`. This is just naive disk sampling, since the projection to the
|
||||
* hemisphere doesn't change the radius.
|
||||
* For the latter, `r_cone(rand) = sin_from_cos(mix(cos_angle, 1, rand))`.
|
||||
*
|
||||
* So, to remap, we just invert r_disk `(-> rand(r_disk) = r_disk^2)` and insert it into
|
||||
* r_cone: `r_cone(r_disk) = r_cone(rand(r_disk)) = sin_from_cos(mix(cos_angle, 1, r_disk^2))`.
|
||||
* In practice, we need to replace `rand` with `1 - rand` to preserve the stratification,
|
||||
* but since it's uniform, that's fine. */
|
||||
float2 xy = sample_uniform_disk(rand);
|
||||
const float r2 = len_squared(xy);
|
||||
|
||||
/* Equivalent to `mix(cos_angle, 1.0f, 1.0f - r2)`. */
|
||||
*cos_theta = 1.0f - r2 * one_minus_cos_angle;
|
||||
|
||||
/* Remap disk radius to cone radius, equivalent to `xy *= sin_theta / sqrt(r2)`. */
|
||||
xy *= safe_sqrtf(one_minus_cos_angle * (2.0f - one_minus_cos_angle * r2));
|
||||
|
||||
*pdf = M_1_2PI_F / one_minus_cos_angle;
|
||||
|
||||
float3 T;
|
||||
float3 B;
|
||||
make_orthonormals(N, &T, &B);
|
||||
return xy.x * T + xy.y * B + *cos_theta * N;
|
||||
}
|
||||
|
||||
*cos_theta = 1.0f;
|
||||
*pdf = 1.0f;
|
||||
|
||||
return N;
|
||||
}
|
||||
|
||||
/* sample uniform point on the surface of a sphere */
|
||||
ccl_device float3 sample_uniform_sphere(const float2 rand)
|
||||
{
|
||||
const float z = 1.0f - 2.0f * rand.x;
|
||||
const float r = sin_from_cos(z);
|
||||
const float phi = M_2PI_F * rand.y;
|
||||
|
||||
return make_float3(polar_to_cartesian(r, phi), z);
|
||||
}
|
||||
|
||||
/* sample point in unit polygon with given number of corners and rotation */
|
||||
ccl_device float2 regular_polygon_sample(const float corners, float rotation, const float2 rand)
|
||||
{
|
||||
float u = rand.x;
|
||||
float v = rand.y;
|
||||
|
||||
/* sample corner number and reuse u */
|
||||
const float corner = floorf(u * corners);
|
||||
u = u * corners - corner;
|
||||
|
||||
/* uniform sampled triangle weights */
|
||||
u = sqrtf(u);
|
||||
v = v * u;
|
||||
u = 1.0f - u;
|
||||
|
||||
/* point in triangle */
|
||||
const float angle = M_PI_F / corners;
|
||||
const float2 p = make_float2((u + v) * cosf(angle), (u - v) * sinf(angle));
|
||||
|
||||
/* rotate */
|
||||
rotation += corner * 2.0f * angle;
|
||||
|
||||
const float cr = cosf(rotation);
|
||||
const float sr = sinf(rotation);
|
||||
|
||||
return make_float2(cr * p.x - sr * p.y, sr * p.x + cr * p.y);
|
||||
}
|
||||
|
||||
/* Generate random variable x following geometric distribution p(x) = r * (1 - r)^x, 0 <= p <= 1.
|
||||
* Also compute the probability mass function pmf.
|
||||
* The sampled order is truncated at `cut_off`. */
|
||||
ccl_device_inline int sample_geometric_distribution(const float rand,
|
||||
const float r,
|
||||
ccl_private float &pmf,
|
||||
const int cut_off = INT_MAX)
|
||||
{
|
||||
const int n = min(int(floorf(logf(rand) / logf(1.0f - r))), cut_off);
|
||||
pmf = (n == cut_off) ? powf(1.0f - r, n) : r * powf(1.0f - r, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Generate random variable x following exponential distribution p(x) = lambda * exp(-lambda * x),
|
||||
* where lambda > 0 is the rate parameter. */
|
||||
ccl_device_inline float sample_exponential_distribution(const float rand, const float lambda)
|
||||
{
|
||||
return -logf(1.0f - rand) / lambda;
|
||||
}
|
||||
|
||||
/* Generate random variable x following bounded exponential distribution
|
||||
* p(x) = lambda * exp(-lambda * x) / (exp(-lambda * t.min) - exp(-lambda * t.max)),
|
||||
* where lambda > 0 is the rate parameter.
|
||||
* The generated sample lies in (t.min, t.max). */
|
||||
ccl_device_inline float sample_exponential_distribution(const float rand,
|
||||
const float lambda,
|
||||
const Interval<float> t)
|
||||
{
|
||||
const float attenuation = 1.0f - expf(lambda * (t.min - t.max));
|
||||
return clamp(t.min - logf(1.0f - rand * attenuation) / lambda, t.min, t.max);
|
||||
}
|
||||
|
||||
ccl_device_inline Spectrum pdf_exponential_distribution(const float x,
|
||||
const Spectrum lambda,
|
||||
const Interval<float> t)
|
||||
{
|
||||
const Spectrum attenuation = exp(-lambda * t.min) - exp(-lambda * t.max);
|
||||
return safe_divide(lambda * exp(-lambda * clamp(x, t.min, t.max)), attenuation);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
41
blender-5.2.0/intern/cycles/kernel/sample/mis.h
Normal file
41
blender-5.2.0/intern/cycles/kernel/sample/mis.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2010 Sony Pictures Imageworks Inc., et al. All Rights Reserved.
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Adapted code from Open Shading Language. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/defines.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Multiple importance sampling utilities. */
|
||||
|
||||
ccl_device float balance_heuristic(const float a, const float b)
|
||||
{
|
||||
return (a) / (a + b);
|
||||
}
|
||||
|
||||
ccl_device float balance_heuristic_3(const float a, const float b, float c)
|
||||
{
|
||||
return (a) / (a + b + c);
|
||||
}
|
||||
|
||||
ccl_device float power_heuristic(const float a, const float b)
|
||||
{
|
||||
return (a * a) / (a * a + b * b);
|
||||
}
|
||||
|
||||
ccl_device float power_heuristic_3(const float a, const float b, float c)
|
||||
{
|
||||
return (a * a) / (a * a + b * b + c * c);
|
||||
}
|
||||
|
||||
ccl_device float max_heuristic(const float a, const float b)
|
||||
{
|
||||
return (a > b) ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
188
blender-5.2.0/intern/cycles/kernel/sample/pattern.h
Normal file
188
blender-5.2.0/intern/cycles/kernel/sample/pattern.h
Normal file
@@ -0,0 +1,188 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/globals.h"
|
||||
|
||||
#include "kernel/sample/sobol_burley.h"
|
||||
#include "kernel/sample/tabulated_sobol.h"
|
||||
|
||||
#include "util/hash.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Pseudo random numbers, uncomment this for debugging correlations. Only run
|
||||
* this single threaded on a CPU for repeatable results. */
|
||||
// #define __DEBUG_CORRELATION__
|
||||
|
||||
/*
|
||||
* The `path_rng_*()` functions below use a shuffled scrambled Sobol
|
||||
* sequence to generate their samples. Sobol samplers have a property
|
||||
* that is worth being aware of when choosing how to use the sample
|
||||
* dimensions:
|
||||
*
|
||||
* 1. In general, earlier sets of dimensions are better stratified. So
|
||||
* prefer e.g. x,y over y,z over z,w for the things that are most
|
||||
* important to sample well.
|
||||
* 2. As a rule of thumb, dimensions that are closer to each other are
|
||||
* better stratified than dimensions that are far. So prefer e.g.
|
||||
* x,y over x,z.
|
||||
*/
|
||||
|
||||
ccl_device_forceinline uint3 blue_noise_indexing(KernelGlobals kg,
|
||||
uint pixel_index,
|
||||
const uint sample)
|
||||
{
|
||||
if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_SOBOL_BURLEY) {
|
||||
/* One sequence per pixel, using the length mask optimization. */
|
||||
return make_uint3(sample, pixel_index, kernel_data.integrator.sobol_index_mask);
|
||||
}
|
||||
if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_BLUE_NOISE_PURE) {
|
||||
/* For blue-noise samples, we use a single sequence (seed 0) with each pixel receiving
|
||||
* a section of it.
|
||||
* The total length is expected to get very large (effectively pixel count times sample count),
|
||||
* so we don't use the length mask optimization here. */
|
||||
pixel_index *= kernel_data.integrator.blue_noise_sequence_length;
|
||||
return make_uint3(sample + pixel_index, 0, 0xffffffff);
|
||||
}
|
||||
if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_BLUE_NOISE_FIRST) {
|
||||
/* The "first" pattern uses a 1SPP blue-noise sequence for the first sample, and a separate
|
||||
* N-1 SPP sequence for the remaining pixels. The purpose of this is to get blue-noise
|
||||
* properties during viewport navigation, which will generally use 1 SPP.
|
||||
* Unfortunately using just the first sample of a full blue-noise sequence doesn't give
|
||||
* its benefits, so we combine the two as a tradeoff between quality at 1 SPP and full SPP. */
|
||||
if (sample == 0) {
|
||||
return make_uint3(pixel_index, 0x0cd0519f, 0xffffffff);
|
||||
}
|
||||
pixel_index *= kernel_data.integrator.blue_noise_sequence_length;
|
||||
return make_uint3((sample - 1) + pixel_index, 0, 0xffffffff);
|
||||
}
|
||||
kernel_assert(false);
|
||||
return make_uint3(0, 0, 0);
|
||||
}
|
||||
|
||||
ccl_device_forceinline float path_rng_1D(KernelGlobals kg,
|
||||
const uint rng_pixel,
|
||||
const uint sample,
|
||||
const int dimension)
|
||||
{
|
||||
#ifdef __DEBUG_CORRELATION__
|
||||
return (float)drand48();
|
||||
#endif
|
||||
|
||||
if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_TABULATED_SOBOL) {
|
||||
return tabulated_sobol_sample_1D(kg, sample, rng_pixel, dimension);
|
||||
}
|
||||
|
||||
const uint3 index = blue_noise_indexing(kg, rng_pixel, sample);
|
||||
return sobol_burley_sample_1D(index.x, dimension, index.y, index.z);
|
||||
}
|
||||
|
||||
ccl_device_forceinline float2 path_rng_2D(KernelGlobals kg,
|
||||
const uint rng_pixel,
|
||||
const int sample,
|
||||
const int dimension)
|
||||
{
|
||||
#ifdef __DEBUG_CORRELATION__
|
||||
return make_float2((float)drand48(), (float)drand48());
|
||||
#endif
|
||||
|
||||
if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_TABULATED_SOBOL) {
|
||||
return tabulated_sobol_sample_2D(kg, sample, rng_pixel, dimension);
|
||||
}
|
||||
|
||||
const uint3 index = blue_noise_indexing(kg, rng_pixel, sample);
|
||||
return sobol_burley_sample_2D(index.x, dimension, index.y, index.z);
|
||||
}
|
||||
|
||||
ccl_device_forceinline float3 path_rng_3D(KernelGlobals kg,
|
||||
const uint rng_pixel,
|
||||
const int sample,
|
||||
const int dimension)
|
||||
{
|
||||
#ifdef __DEBUG_CORRELATION__
|
||||
return make_float3((float)drand48(), (float)drand48(), (float)drand48());
|
||||
#endif
|
||||
|
||||
if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_TABULATED_SOBOL) {
|
||||
return tabulated_sobol_sample_3D(kg, sample, rng_pixel, dimension);
|
||||
}
|
||||
|
||||
const uint3 index = blue_noise_indexing(kg, rng_pixel, sample);
|
||||
return sobol_burley_sample_3D(index.x, dimension, index.y, index.z);
|
||||
}
|
||||
|
||||
ccl_device_forceinline float4 path_rng_4D(KernelGlobals kg,
|
||||
const uint rng_pixel,
|
||||
const int sample,
|
||||
const int dimension)
|
||||
{
|
||||
#ifdef __DEBUG_CORRELATION__
|
||||
return make_float4((float)drand48(), (float)drand48(), (float)drand48(), (float)drand48());
|
||||
#endif
|
||||
|
||||
if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_TABULATED_SOBOL) {
|
||||
return tabulated_sobol_sample_4D(kg, sample, rng_pixel, dimension);
|
||||
}
|
||||
|
||||
const uint3 index = blue_noise_indexing(kg, rng_pixel, sample);
|
||||
return sobol_burley_sample_4D(index.x, dimension, index.y, index.z);
|
||||
}
|
||||
|
||||
ccl_device_inline uint path_rng_pixel_init(KernelGlobals kg,
|
||||
const int /*sample*/,
|
||||
const int x,
|
||||
const int y)
|
||||
{
|
||||
const uint pattern = kernel_data.integrator.sampling_pattern;
|
||||
if (pattern == SAMPLING_PATTERN_TABULATED_SOBOL || pattern == SAMPLING_PATTERN_SOBOL_BURLEY) {
|
||||
|
||||
/* The white-noise samplers use a random per-pixel hash to generate independent sequences. */
|
||||
return hash_iqnt2d(x, y) ^ kernel_data.integrator.seed;
|
||||
}
|
||||
|
||||
/* The blue-noise samplers use a single sequence for all pixels, but offset the index within
|
||||
* the sequence for each pixel. We use a hierarchically shuffled 2D morton curve to determine
|
||||
* each pixel's offset along the sequence.
|
||||
*
|
||||
* Based on:
|
||||
* https://psychopath.io/post/2022_07_24_owen_scrambling_based_dithered_blue_noise_sampling.
|
||||
*
|
||||
* TODO(lukas): Use a precomputed Hilbert curve to avoid directionality bias in the noise
|
||||
* distribution. We can just precompute a small-ish tile and repeat it in morton code order.
|
||||
*/
|
||||
return nested_uniform_scramble_base4(morton2d(x, y), kernel_data.integrator.seed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits samples into two different classes, A and B, which can be
|
||||
* compared for variance estimation.
|
||||
*/
|
||||
ccl_device_inline bool sample_is_class_A(const int pattern, const int sample)
|
||||
{
|
||||
#if 0
|
||||
if (!(pattern == SAMPLING_PATTERN_TABULATED_SOBOL || pattern == SAMPLING_PATTERN_SOBOL_BURLEY)) {
|
||||
/* Fallback: assign samples randomly.
|
||||
* This is guaranteed to work "okay" for any sampler, but isn't good.
|
||||
* (NOTE: the seed constant is just a random number to guard against
|
||||
* possible interactions with other uses of the hash. There's nothing
|
||||
* special about it.)
|
||||
*/
|
||||
return hash_hp_seeded_uint(sample, 0xa771f873) & 1;
|
||||
}
|
||||
#else
|
||||
(void)pattern;
|
||||
#endif
|
||||
|
||||
/* This follows the approach from section 10.2.1 of "Progressive
|
||||
* Multi-Jittered Sample Sequences" by Christensen et al., but
|
||||
* implemented with efficient bit-fiddling.
|
||||
*
|
||||
* This approach also turns out to work equally well with Owen
|
||||
* scrambled and shuffled Sobol (see https://developer.blender.org/D15746#429471).
|
||||
*/
|
||||
return popcount(uint(sample) & 0xaaaaaaaa) & 1;
|
||||
}
|
||||
CCL_NAMESPACE_END
|
||||
203
blender-5.2.0/intern/cycles/kernel/sample/sobol_burley.h
Normal file
203
blender-5.2.0/intern/cycles/kernel/sample/sobol_burley.h
Normal file
@@ -0,0 +1,203 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/*
|
||||
* A shuffled, Owen-scrambled Sobol sampler, implemented with the
|
||||
* techniques from the paper "Practical Hash-based Owen Scrambling"
|
||||
* by Brent Burley, 2020, Journal of Computer Graphics Techniques.
|
||||
*
|
||||
* Note that unlike a standard high-dimensional Sobol sequence, this
|
||||
* Sobol sampler uses padding to achieve higher dimensions, as described
|
||||
* in Burley's paper.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kernel/tables.h"
|
||||
|
||||
#include "kernel/sample/util.h"
|
||||
|
||||
#include "util/hash.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/*
|
||||
* Computes a single dimension of a sample from an Owen-scrambled
|
||||
* Sobol sequence. This is used in the main sampling functions,
|
||||
* sobol_burley_sample_#D(), below.
|
||||
*
|
||||
* - rev_bit_index: the sample index, with reversed order bits.
|
||||
* - dimension: the sample dimension.
|
||||
* - scramble_seed: the Owen scrambling seed.
|
||||
*
|
||||
* Note that the seed must be well randomized before being
|
||||
* passed to this function.
|
||||
*/
|
||||
ccl_device_forceinline float sobol_burley(uint rev_bit_index,
|
||||
const uint dimension,
|
||||
const uint scramble_seed)
|
||||
{
|
||||
uint result = 0;
|
||||
|
||||
if (dimension == 0) {
|
||||
/* Fast-path for dimension 0, which is just Van der corput.
|
||||
* This makes a notable difference in performance since we reuse
|
||||
* dimensions for padding, and dimension 0 is reused the most. */
|
||||
result = reverse_integer_bits(rev_bit_index);
|
||||
}
|
||||
else {
|
||||
uint i = 0;
|
||||
while (rev_bit_index != 0) {
|
||||
const uint j = count_leading_zeros(rev_bit_index);
|
||||
result ^= sobol_burley_table[dimension][i + j];
|
||||
i += j + 1;
|
||||
|
||||
/* We can't do `<<= j + 1` because that can overflow the shift
|
||||
* operator, which doesn't do what we need, at least on x86. */
|
||||
rev_bit_index <<= j;
|
||||
rev_bit_index <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply Owen scrambling. */
|
||||
result = reverse_integer_bits(reversed_bit_owen(result, scramble_seed));
|
||||
|
||||
return uint_to_float_excl(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: the functions below intentionally produce samples that are
|
||||
* uncorrelated between functions. For example, a 1D sample and 2D
|
||||
* sample produced with the same index, dimension, and seed are
|
||||
* uncorrelated with each other. This allows more care-free usage
|
||||
* of the functions together, without having to worry about
|
||||
* e.g. 1D and 2D samples being accidentally correlated with each
|
||||
* other.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Computes a 1D Owen-scrambled and shuffled Sobol sample.
|
||||
*
|
||||
* `index` is the index of the sample in the sequence.
|
||||
*
|
||||
* `dimension` is which dimensions of the sample you want to fetch. Note
|
||||
* that different 1D dimensions are uncorrelated. For samples with > 1D
|
||||
* stratification, use the multi-dimensional sampling methods below.
|
||||
*
|
||||
* `seed`: different seeds produce statistically independent,
|
||||
* uncorrelated sequences.
|
||||
*
|
||||
* `shuffled_index_mask` limits the sample sequence length, improving
|
||||
* performance. It must be a string of binary 1 bits followed by a
|
||||
* string of binary 0 bits (e.g. 0xffff0000) for the sampler to operate
|
||||
* correctly. In general, `reverse_integer_bits(shuffled_index_mask)`
|
||||
* should be >= the maximum number of samples expected to be taken. A safe
|
||||
* default (but least performant) is 0xffffffff, for maximum sequence
|
||||
* length.
|
||||
*/
|
||||
ccl_device float sobol_burley_sample_1D(uint index,
|
||||
const uint dimension,
|
||||
uint seed,
|
||||
const uint shuffled_index_mask)
|
||||
{
|
||||
/* Include the dimension in the seed, so we get decorrelated
|
||||
* sequences for different dimensions via shuffling. */
|
||||
seed ^= hash_hp_uint(dimension);
|
||||
|
||||
/* Shuffle and mask. The masking is just for better
|
||||
* performance at low sample counts. */
|
||||
index = reversed_bit_owen(reverse_integer_bits(index), seed ^ 0xbff95bfe);
|
||||
index &= shuffled_index_mask;
|
||||
|
||||
return sobol_burley(index, 0, seed ^ 0x635c77bd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Computes a 2D Owen-scrambled and shuffled Sobol sample.
|
||||
*
|
||||
* `dimension_set` is which two dimensions of the sample you want to
|
||||
* fetch. For example, 0 is the first two, 1 is the second two, etc.
|
||||
* The dimensions within a single set are stratified, but different sets
|
||||
* are uncorrelated.
|
||||
*
|
||||
* See sobol_burley_sample_1D for further usage details.
|
||||
*/
|
||||
ccl_device float2 sobol_burley_sample_2D(uint index,
|
||||
const uint dimension_set,
|
||||
uint seed,
|
||||
const uint shuffled_index_mask)
|
||||
{
|
||||
/* Include the dimension set in the seed, so we get decorrelated
|
||||
* sequences for different dimension sets via shuffling. */
|
||||
seed ^= hash_hp_uint(dimension_set);
|
||||
|
||||
/* Shuffle and mask. The masking is just for better
|
||||
* performance at low sample counts. */
|
||||
index = reversed_bit_owen(reverse_integer_bits(index), seed ^ 0xf8ade99a);
|
||||
index &= shuffled_index_mask;
|
||||
|
||||
return make_float2(sobol_burley(index, 0, seed ^ 0xe0aaaf76),
|
||||
sobol_burley(index, 1, seed ^ 0x94964d4e));
|
||||
}
|
||||
|
||||
/*
|
||||
* Computes a 3D Owen-scrambled and shuffled Sobol sample.
|
||||
*
|
||||
* `dimension_set` is which three dimensions of the sample you want to
|
||||
* fetch. For example, 0 is the first three, 1 is the second three, etc.
|
||||
* The dimensions within a single set are stratified, but different sets
|
||||
* are uncorrelated.
|
||||
*
|
||||
* See sobol_burley_sample_1D for further usage details.
|
||||
*/
|
||||
ccl_device float3 sobol_burley_sample_3D(uint index,
|
||||
const uint dimension_set,
|
||||
uint seed,
|
||||
const uint shuffled_index_mask)
|
||||
{
|
||||
/* Include the dimension set in the seed, so we get decorrelated
|
||||
* sequences for different dimension sets via shuffling. */
|
||||
seed ^= hash_hp_uint(dimension_set);
|
||||
|
||||
/* Shuffle and mask. The masking is just for better
|
||||
* performance at low sample counts. */
|
||||
index = reversed_bit_owen(reverse_integer_bits(index), seed ^ 0xcaa726ac);
|
||||
index &= shuffled_index_mask;
|
||||
|
||||
return make_float3(sobol_burley(index, 0, seed ^ 0x9e78e391),
|
||||
sobol_burley(index, 1, seed ^ 0x67c33241),
|
||||
sobol_burley(index, 2, seed ^ 0x78c395c5));
|
||||
}
|
||||
|
||||
/*
|
||||
* Computes a 4D Owen-scrambled and shuffled Sobol sample.
|
||||
*
|
||||
* `dimension_set` is which four dimensions of the sample you want to
|
||||
* fetch. For example, 0 is the first four, 1 is the second four, etc.
|
||||
* The dimensions within a single set are stratified, but different sets
|
||||
* are uncorrelated.
|
||||
*
|
||||
* See sobol_burley_sample_1D for further usage details.
|
||||
*/
|
||||
ccl_device float4 sobol_burley_sample_4D(uint index,
|
||||
const uint dimension_set,
|
||||
uint seed,
|
||||
const uint shuffled_index_mask)
|
||||
{
|
||||
/* Include the dimension set in the seed, so we get decorrelated
|
||||
* sequences for different dimension sets via shuffling. */
|
||||
seed ^= hash_hp_uint(dimension_set);
|
||||
|
||||
/* Shuffle and mask. The masking is just for better
|
||||
* performance at low sample counts. */
|
||||
index = reversed_bit_owen(reverse_integer_bits(index), seed ^ 0xc2c1a055);
|
||||
index &= shuffled_index_mask;
|
||||
|
||||
return make_float4(sobol_burley(index, 0, seed ^ 0x39468210),
|
||||
sobol_burley(index, 1, seed ^ 0xe9d8a845),
|
||||
sobol_burley(index, 2, seed ^ 0x5f32b482),
|
||||
sobol_burley(index, 3, seed ^ 0x1524cc56));
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
178
blender-5.2.0/intern/cycles/kernel/sample/tabulated_sobol.h
Normal file
178
blender-5.2.0/intern/cycles/kernel/sample/tabulated_sobol.h
Normal file
@@ -0,0 +1,178 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "kernel/globals.h"
|
||||
|
||||
#include "kernel/sample/util.h"
|
||||
|
||||
#include "util/hash.h"
|
||||
|
||||
#pragma once
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
ccl_device uint tabulated_sobol_shuffled_sample_index(KernelGlobals kg,
|
||||
uint sample,
|
||||
const uint dimension,
|
||||
const uint seed)
|
||||
{
|
||||
const uint sample_count = kernel_data.integrator.tabulated_sobol_sequence_size;
|
||||
|
||||
/* Shuffle the pattern order and sample index to decorrelate
|
||||
* dimensions and make the most of the finite patterns we have.
|
||||
* The funky sample mask stuff is to ensure that we only shuffle
|
||||
* *within* the current sample pattern, which is necessary to avoid
|
||||
* early repeat pattern use. */
|
||||
const uint pattern_i = hash_shuffle_uint(dimension, NUM_TAB_SOBOL_PATTERNS, seed);
|
||||
/* sample_count should always be a power of two, so this results in a mask. */
|
||||
const uint sample_mask = sample_count - 1;
|
||||
const uint sample_shuffled = nested_uniform_scramble(sample,
|
||||
hash_wang_seeded_uint(dimension, seed));
|
||||
sample = (sample & ~sample_mask) | (sample_shuffled & sample_mask);
|
||||
|
||||
return ((pattern_i * sample_count) + sample) % (sample_count * NUM_TAB_SOBOL_PATTERNS);
|
||||
}
|
||||
|
||||
ccl_device float tabulated_sobol_sample_1D(KernelGlobals kg,
|
||||
const uint sample,
|
||||
const uint rng_hash,
|
||||
const uint dimension)
|
||||
{
|
||||
uint seed = rng_hash;
|
||||
|
||||
/* Use the same sample sequence seed for all pixels when using
|
||||
* scrambling distance. */
|
||||
if (kernel_data.integrator.scrambling_distance < 1.0f) {
|
||||
seed = kernel_data.integrator.seed;
|
||||
}
|
||||
|
||||
/* Fetch the sample. */
|
||||
const uint index = tabulated_sobol_shuffled_sample_index(kg, sample, dimension, seed);
|
||||
float x = kernel_data_fetch(sample_pattern_lut, index * NUM_TAB_SOBOL_DIMENSIONS);
|
||||
|
||||
/* Do limited Cranley-Patterson rotation when using scrambling distance. */
|
||||
if (kernel_data.integrator.scrambling_distance < 1.0f) {
|
||||
const float jitter_x = hash_wang_seeded_float(dimension, rng_hash) *
|
||||
kernel_data.integrator.scrambling_distance;
|
||||
x += jitter_x;
|
||||
x -= floorf(x);
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
ccl_device float2 tabulated_sobol_sample_2D(KernelGlobals kg,
|
||||
const uint sample,
|
||||
const uint rng_hash,
|
||||
const uint dimension)
|
||||
{
|
||||
uint seed = rng_hash;
|
||||
|
||||
/* Use the same sample sequence seed for all pixels when using
|
||||
* scrambling distance. */
|
||||
if (kernel_data.integrator.scrambling_distance < 1.0f) {
|
||||
seed = kernel_data.integrator.seed;
|
||||
}
|
||||
|
||||
/* Fetch the sample. */
|
||||
const uint index = tabulated_sobol_shuffled_sample_index(kg, sample, dimension, seed);
|
||||
float x = kernel_data_fetch(sample_pattern_lut, index * NUM_TAB_SOBOL_DIMENSIONS);
|
||||
float y = kernel_data_fetch(sample_pattern_lut, index * NUM_TAB_SOBOL_DIMENSIONS + 1);
|
||||
|
||||
/* Do limited Cranley-Patterson rotation when using scrambling distance. */
|
||||
if (kernel_data.integrator.scrambling_distance < 1.0f) {
|
||||
const float jitter_x = hash_wang_seeded_float(dimension, rng_hash) *
|
||||
kernel_data.integrator.scrambling_distance;
|
||||
const float jitter_y = hash_wang_seeded_float(dimension, rng_hash ^ 0xca0e1151) *
|
||||
kernel_data.integrator.scrambling_distance;
|
||||
x += jitter_x;
|
||||
y += jitter_y;
|
||||
x -= floorf(x);
|
||||
y -= floorf(y);
|
||||
}
|
||||
|
||||
return make_float2(x, y);
|
||||
}
|
||||
|
||||
ccl_device float3 tabulated_sobol_sample_3D(KernelGlobals kg,
|
||||
const uint sample,
|
||||
const uint rng_hash,
|
||||
const uint dimension)
|
||||
{
|
||||
uint seed = rng_hash;
|
||||
|
||||
/* Use the same sample sequence seed for all pixels when using
|
||||
* scrambling distance. */
|
||||
if (kernel_data.integrator.scrambling_distance < 1.0f) {
|
||||
seed = kernel_data.integrator.seed;
|
||||
}
|
||||
|
||||
/* Fetch the sample. */
|
||||
const uint index = tabulated_sobol_shuffled_sample_index(kg, sample, dimension, seed);
|
||||
float x = kernel_data_fetch(sample_pattern_lut, index * NUM_TAB_SOBOL_DIMENSIONS);
|
||||
float y = kernel_data_fetch(sample_pattern_lut, index * NUM_TAB_SOBOL_DIMENSIONS + 1);
|
||||
float z = kernel_data_fetch(sample_pattern_lut, index * NUM_TAB_SOBOL_DIMENSIONS + 2);
|
||||
|
||||
/* Do limited Cranley-Patterson rotation when using scrambling distance. */
|
||||
if (kernel_data.integrator.scrambling_distance < 1.0f) {
|
||||
const float jitter_x = hash_wang_seeded_float(dimension, rng_hash) *
|
||||
kernel_data.integrator.scrambling_distance;
|
||||
const float jitter_y = hash_wang_seeded_float(dimension, rng_hash ^ 0xca0e1151) *
|
||||
kernel_data.integrator.scrambling_distance;
|
||||
const float jitter_z = hash_wang_seeded_float(dimension, rng_hash ^ 0xbf604c5a) *
|
||||
kernel_data.integrator.scrambling_distance;
|
||||
x += jitter_x;
|
||||
y += jitter_y;
|
||||
z += jitter_z;
|
||||
x -= floorf(x);
|
||||
y -= floorf(y);
|
||||
z -= floorf(z);
|
||||
}
|
||||
|
||||
return make_float3(x, y, z);
|
||||
}
|
||||
|
||||
ccl_device float4 tabulated_sobol_sample_4D(KernelGlobals kg,
|
||||
const uint sample,
|
||||
const uint rng_hash,
|
||||
const uint dimension)
|
||||
{
|
||||
uint seed = rng_hash;
|
||||
|
||||
/* Use the same sample sequence seed for all pixels when using
|
||||
* scrambling distance. */
|
||||
if (kernel_data.integrator.scrambling_distance < 1.0f) {
|
||||
seed = kernel_data.integrator.seed;
|
||||
}
|
||||
|
||||
/* Fetch the sample. */
|
||||
const uint index = tabulated_sobol_shuffled_sample_index(kg, sample, dimension, seed);
|
||||
float x = kernel_data_fetch(sample_pattern_lut, index * NUM_TAB_SOBOL_DIMENSIONS);
|
||||
float y = kernel_data_fetch(sample_pattern_lut, index * NUM_TAB_SOBOL_DIMENSIONS + 1);
|
||||
float z = kernel_data_fetch(sample_pattern_lut, index * NUM_TAB_SOBOL_DIMENSIONS + 2);
|
||||
float w = kernel_data_fetch(sample_pattern_lut, index * NUM_TAB_SOBOL_DIMENSIONS + 3);
|
||||
|
||||
/* Do limited Cranley-Patterson rotation when using scrambling distance. */
|
||||
if (kernel_data.integrator.scrambling_distance < 1.0f) {
|
||||
const float jitter_x = hash_wang_seeded_float(dimension, rng_hash) *
|
||||
kernel_data.integrator.scrambling_distance;
|
||||
const float jitter_y = hash_wang_seeded_float(dimension, rng_hash ^ 0xca0e1151) *
|
||||
kernel_data.integrator.scrambling_distance;
|
||||
const float jitter_z = hash_wang_seeded_float(dimension, rng_hash ^ 0xbf604c5a) *
|
||||
kernel_data.integrator.scrambling_distance;
|
||||
const float jitter_w = hash_wang_seeded_float(dimension, rng_hash ^ 0x99634d1d) *
|
||||
kernel_data.integrator.scrambling_distance;
|
||||
x += jitter_x;
|
||||
y += jitter_y;
|
||||
z += jitter_z;
|
||||
w += jitter_w;
|
||||
x -= floorf(x);
|
||||
y -= floorf(y);
|
||||
z -= floorf(z);
|
||||
w -= floorf(w);
|
||||
}
|
||||
|
||||
return make_float4(x, y, z, w);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
79
blender-5.2.0/intern/cycles/kernel/sample/util.h
Normal file
79
blender-5.2.0/intern/cycles/kernel/sample/util.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/math.h"
|
||||
#include "util/types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/*
|
||||
* Performs base-2 Owen scrambling on a reversed-bit unsigned integer.
|
||||
*
|
||||
* This is equivalent to the Laine-Karras permutation, but much higher
|
||||
* quality. See https://psychopath.io/post/2021_01_30_building_a_better_lk_hash
|
||||
*/
|
||||
ccl_device_inline uint reversed_bit_owen(uint n, const uint seed)
|
||||
{
|
||||
n ^= n * 0x3d20adea;
|
||||
n += seed;
|
||||
n *= (seed >> 16) | 1;
|
||||
n ^= n * 0x05526c56;
|
||||
n ^= n * 0x53a22864;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs base-4 Owen scrambling on a reversed-bit unsigned integer.
|
||||
*
|
||||
* See https://psychopath.io/post/2022_08_14_a_fast_hash_for_base_4_owen_scrambling
|
||||
*/
|
||||
|
||||
ccl_device_inline uint reversed_bit_owen_base4(uint n, const uint seed)
|
||||
{
|
||||
n ^= n * 0x3d20adea;
|
||||
n ^= (n >> 1) & (n << 1) & 0x55555555;
|
||||
n += seed;
|
||||
n *= (seed >> 16) | 1;
|
||||
n ^= (n >> 1) & (n << 1) & 0x55555555;
|
||||
n ^= n * 0x05526c56;
|
||||
n ^= n * 0x53a22864;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs base-2 Owen scrambling on an unsigned integer.
|
||||
*/
|
||||
ccl_device_inline uint nested_uniform_scramble(const uint i, const uint seed)
|
||||
{
|
||||
return reverse_integer_bits(reversed_bit_owen(reverse_integer_bits(i), seed));
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs base-4 Owen scrambling on an unsigned integer.
|
||||
*/
|
||||
ccl_device_inline uint nested_uniform_scramble_base4(const uint i, const uint seed)
|
||||
{
|
||||
return reverse_integer_bits(reversed_bit_owen_base4(reverse_integer_bits(i), seed));
|
||||
}
|
||||
|
||||
ccl_device_inline uint expand_bits(uint x)
|
||||
{
|
||||
x &= 0x0000ffff;
|
||||
x = (x ^ (x << 8)) & 0x00ff00ff;
|
||||
x = (x ^ (x << 4)) & 0x0f0f0f0f;
|
||||
x = (x ^ (x << 2)) & 0x33333333;
|
||||
x = (x ^ (x << 1)) & 0x55555555;
|
||||
return x;
|
||||
}
|
||||
|
||||
ccl_device_inline uint morton2d(const uint x, const uint y)
|
||||
{
|
||||
return (expand_bits(x) << 1) | expand_bits(y);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
Reference in New Issue
Block a user