mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
Fixes the following misspellings: "equi-angular" -> "equiangular" "otheriwse" -> "otherwise" "implememted" -> "implemented" "tranmissitance" -> "transmittance" "derivates" -> "derivatives" "cauzed" -> "caused" "unsinged" -> "unsigned" "stochastical" -> "stochastic" "LighTree" -> "LightTree" "stdilb" -> "stdlib" "colospace" -> "colorspace" "evaulated" -> "evaluated" Pull Request: https://projects.blender.org/blender/blender/pulls/163850
266 lines
8.6 KiB
C++
266 lines
8.6 KiB
C++
/* SPDX-FileCopyrightText: 2011-2025 Blender Foundation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 */
|
|
|
|
#pragma once
|
|
|
|
#include "kernel/globals.h"
|
|
#include "kernel/sample/lcg.h"
|
|
|
|
#include "util/types_image.h"
|
|
|
|
#if !defined(__KERNEL_METAL__) && !defined(__KERNEL_ONEAPI__)
|
|
# ifdef WITH_NANOVDB
|
|
# include "kernel/util/nanovdb.h"
|
|
# endif
|
|
#endif
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
#ifndef __KERNEL_GPU__
|
|
/* Make template functions private so symbols don't conflict between kernels with different
|
|
* instruction sets. */
|
|
namespace {
|
|
#endif
|
|
|
|
#ifdef WITH_NANOVDB
|
|
|
|
/* Cubic interpolation weights. */
|
|
ccl_device_forceinline void fill_cubic_weights(float3 w[4], float3 t)
|
|
{
|
|
w[0] = (((-1.0f / 6.0f) * t + 0.5f) * t - 0.5f) * t + (1.0f / 6.0f);
|
|
w[1] = ((0.5f * t - 1.0f) * t) * t + (2.0f / 3.0f);
|
|
w[2] = ((-0.5f * t + 0.5f) * t + 0.5f) * t + (1.0f / 6.0f);
|
|
w[3] = (1.0f / 6.0f) * t * t * t;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** Return the sample position for stochastic one-tap sampling.
|
|
* From "Stochastic Texture Filtering": https://arxiv.org/abs/2305.05810
|
|
* \{ */
|
|
ccl_device_inline float3 interp_tricubic_stochastic(const float3 P, ccl_private float3 &rand)
|
|
{
|
|
const float3 p = floor(P);
|
|
const float3 t = P - p;
|
|
|
|
float3 w[4];
|
|
fill_cubic_weights(w, t);
|
|
|
|
/* For reservoir sampling, always accept the first in the stream. */
|
|
float3 total_weight = w[0];
|
|
float3 offset = make_float3(-1.0f);
|
|
|
|
for (int j = 1; j < 4; j++) {
|
|
total_weight += w[j];
|
|
const float3 thresh = w[j] / total_weight;
|
|
const auto mask = rand < thresh;
|
|
offset = select(mask, make_float3(float(j) - 1.0f), offset);
|
|
rand = select(mask, safe_divide(rand, thresh), safe_divide(rand - thresh, 1.0f - thresh));
|
|
}
|
|
|
|
return p + offset;
|
|
}
|
|
|
|
ccl_device_inline float3 interp_trilinear_stochastic(const float3 P, const float3 rand)
|
|
{
|
|
const float3 p = floor(P);
|
|
const float3 t = P - p;
|
|
return select(rand < t, p + 1.0f, p);
|
|
}
|
|
|
|
ccl_device_inline float3 interp_stochastic(const float3 P,
|
|
ccl_private InterpolationType &interpolation,
|
|
ccl_private float3 &rand)
|
|
{
|
|
float3 P_new = P;
|
|
if (interpolation == INTERPOLATION_CUBIC) {
|
|
P_new = interp_tricubic_stochastic(P, rand);
|
|
}
|
|
else if (interpolation == INTERPOLATION_LINEAR) {
|
|
P_new = interp_trilinear_stochastic(P, rand);
|
|
}
|
|
else {
|
|
kernel_assert(interpolation == INTERPOLATION_CLOSEST);
|
|
}
|
|
interpolation = INTERPOLATION_CLOSEST;
|
|
return P_new;
|
|
}
|
|
/** \} */
|
|
|
|
template<typename OutT, typename Acc>
|
|
ccl_device OutT kernel_image_interp_trilinear_nanovdb(ccl_private Acc &acc, const float3 P)
|
|
{
|
|
const float3 floor_P = floor(P);
|
|
const float3 t = P - floor_P;
|
|
const int3 index = make_int3(floor_P);
|
|
|
|
const int ix = index.x;
|
|
const int iy = index.y;
|
|
const int iz = index.z;
|
|
|
|
return mix(mix(mix(OutT(acc.getValue(make_int3(ix, iy, iz))),
|
|
OutT(acc.getValue(make_int3(ix, iy, iz + 1))),
|
|
t.z),
|
|
mix(OutT(acc.getValue(make_int3(ix, iy + 1, iz + 1))),
|
|
OutT(acc.getValue(make_int3(ix, iy + 1, iz))),
|
|
1.0f - t.z),
|
|
t.y),
|
|
mix(mix(OutT(acc.getValue(make_int3(ix + 1, iy + 1, iz))),
|
|
OutT(acc.getValue(make_int3(ix + 1, iy + 1, iz + 1))),
|
|
t.z),
|
|
mix(OutT(acc.getValue(make_int3(ix + 1, iy, iz + 1))),
|
|
OutT(acc.getValue(make_int3(ix + 1, iy, iz))),
|
|
1.0f - t.z),
|
|
1.0f - t.y),
|
|
t.x);
|
|
}
|
|
|
|
template<typename OutT, typename Acc>
|
|
ccl_device OutT kernel_image_interp_tricubic_nanovdb(ccl_private Acc &acc, const float3 P)
|
|
{
|
|
# if defined(__KERNEL_HIP__)
|
|
/* Explicitly unroll for HIP compiler to unroll the loop. Without this the render result is wrong
|
|
* on a specific platform/compiler combinations. ALso don't rely on the `unroll` hint as it has
|
|
* a performance impact. See #152126 and discussion/benchmark in !152321. */
|
|
|
|
const float3 floor_P = floor(P);
|
|
const float3 t = P - floor_P;
|
|
const int3 index = make_int3(floor_P);
|
|
|
|
const int xc[4] = {index.x - 1, index.x, index.x + 1, index.x + 2};
|
|
const int yc[4] = {index.y - 1, index.y, index.y + 1, index.y + 2};
|
|
const int zc[4] = {index.z - 1, index.z, index.z + 1, index.z + 2};
|
|
|
|
float3 weight[4];
|
|
fill_cubic_weights(weight, t);
|
|
|
|
# define DATA(x, y, z) (OutT(acc.getValue(make_int3(xc[x], yc[y], zc[z]))))
|
|
# define COL_TERM(col, row) \
|
|
(weight[col].y * (weight[0].x * DATA(0, col, row) + weight[1].x * DATA(1, col, row) + \
|
|
weight[2].x * DATA(2, col, row) + weight[3].x * DATA(3, col, row)))
|
|
# define ROW_TERM(row) \
|
|
(weight[row].z * (COL_TERM(0, row) + COL_TERM(1, row) + COL_TERM(2, row) + COL_TERM(3, row)))
|
|
|
|
/* Actual interpolation. */
|
|
return ROW_TERM(0) + ROW_TERM(1) + ROW_TERM(2) + ROW_TERM(3);
|
|
|
|
# undef COL_TERM
|
|
# undef ROW_TERM
|
|
# undef DATA
|
|
|
|
# else
|
|
|
|
const float3 floor_P = floor(P);
|
|
const float3 t = P - floor_P;
|
|
const int3 index = make_int3(floor_P) - make_int3(1);
|
|
|
|
float3 w[4];
|
|
fill_cubic_weights(w, t);
|
|
|
|
OutT result = make_zero<OutT>();
|
|
|
|
for (int k = 0; k < 4; k++) {
|
|
OutT col_term_acc = make_zero<OutT>();
|
|
for (int j = 0; j < 4; j++) {
|
|
col_term_acc += w[j].y * (w[0].x * (OutT(acc.getValue(index + make_int3(0, j, k)))) +
|
|
w[1].x * (OutT(acc.getValue(index + make_int3(1, j, k)))) +
|
|
w[2].x * (OutT(acc.getValue(index + make_int3(2, j, k)))) +
|
|
w[3].x * (OutT(acc.getValue(index + make_int3(3, j, k)))));
|
|
}
|
|
result += w[k].z * col_term_acc;
|
|
}
|
|
|
|
return result;
|
|
# endif
|
|
}
|
|
|
|
template<typename OutT, typename T>
|
|
# if defined(__KERNEL_METAL__)
|
|
__attribute__((noinline))
|
|
# else
|
|
ccl_device_noinline
|
|
# endif
|
|
OutT kernel_image_interp_nanovdb(const ccl_global KernelImageInfo &info,
|
|
float3 P,
|
|
const InterpolationType interp)
|
|
{
|
|
ccl_global nanovdb::NanoGrid<T> *const grid = (ccl_global nanovdb::NanoGrid<T> *)info.data;
|
|
|
|
if (interp == INTERPOLATION_CLOSEST) {
|
|
nanovdb::ReadAccessor<T> acc(grid->tree().root());
|
|
return OutT(acc.getValue(make_int3(floor(P))));
|
|
}
|
|
|
|
nanovdb::CachedReadAccessor<T> acc(grid->tree().root());
|
|
if (interp == INTERPOLATION_LINEAR) {
|
|
return kernel_image_interp_trilinear_nanovdb<OutT>(acc, P);
|
|
}
|
|
|
|
return kernel_image_interp_tricubic_nanovdb<OutT>(acc, P);
|
|
}
|
|
#endif /* WITH_NANOVDB */
|
|
|
|
ccl_device float4 kernel_image_interp_3d(KernelGlobals kg,
|
|
ccl_private ShaderData *sd,
|
|
const int image_texture_id,
|
|
float3 P,
|
|
InterpolationType interp,
|
|
const bool stochastic)
|
|
{
|
|
#ifdef WITH_NANOVDB
|
|
const ccl_global KernelImageTexture &tex = kernel_data_fetch(image_textures, image_texture_id);
|
|
const ccl_global KernelImageInfo &info = kernel_data_fetch(image_info, tex.image_info_id);
|
|
|
|
if (tex.use_transform_3d) {
|
|
P = transform_point(&tex.transform_3d, P);
|
|
}
|
|
|
|
InterpolationType interpolation = (interp == INTERPOLATION_NONE) ?
|
|
(InterpolationType)info.interpolation :
|
|
interp;
|
|
|
|
if (stochastic) {
|
|
float3 rand = lcg_step_float3(&sd->lcg_state);
|
|
P = interp_stochastic(P, interpolation, rand);
|
|
}
|
|
|
|
const ImageDataType data_type = (ImageDataType)info.data_type;
|
|
if (data_type == IMAGE_DATA_TYPE_NANOVDB_FLOAT) {
|
|
const float f = kernel_image_interp_nanovdb<float, float>(info, P, interpolation);
|
|
return make_float4(f, f, f, 1.0f);
|
|
}
|
|
if (data_type == IMAGE_DATA_TYPE_NANOVDB_FLOAT3) {
|
|
const float3 f = kernel_image_interp_nanovdb<float3, packed_float3>(info, P, interpolation);
|
|
return make_float4(f, 1.0f);
|
|
}
|
|
if (data_type == IMAGE_DATA_TYPE_NANOVDB_FLOAT4) {
|
|
return kernel_image_interp_nanovdb<float4, float4>(info, P, interpolation);
|
|
}
|
|
if (data_type == IMAGE_DATA_TYPE_NANOVDB_FPN) {
|
|
const float f = kernel_image_interp_nanovdb<float, nanovdb::FpN>(info, P, interpolation);
|
|
return make_float4(f, f, f, 1.0f);
|
|
}
|
|
if (data_type == IMAGE_DATA_TYPE_NANOVDB_FP16) {
|
|
const float f = kernel_image_interp_nanovdb<float, nanovdb::Fp16>(info, P, interpolation);
|
|
return make_float4(f, f, f, 1.0f);
|
|
}
|
|
if (data_type == IMAGE_DATA_TYPE_NANOVDB_EMPTY) {
|
|
return zero_float4();
|
|
}
|
|
#else
|
|
(void)kg;
|
|
(void)sd;
|
|
(void)image_texture_id;
|
|
(void)P;
|
|
(void)interp;
|
|
(void)stochastic;
|
|
#endif
|
|
|
|
return IMAGE_MISSING_RGBA;
|
|
}
|
|
|
|
#ifndef __KERNEL_GPU__
|
|
} /* Namespace. */
|
|
#endif
|
|
|
|
CCL_NAMESPACE_END
|