This patch implements a new Gabor noise node based on [1] but with the
improvements from [2] and the phasor formulation from [3].
We compare with the most popular existing implementation, that of OSL,
from the user's point of view:
- This implementation produces C1 continuous noise as opposed to the
non continuous OSL implementation, so it can be used for bump
mapping and is generally smother. This is achieved by windowing the
Gabor kernel using a Hann window.
- The Bandwidth input of OSL was hard-coded to 1 and was replaced with
a frequency input, which OSL hard codes to 2, since frequency is
more natural to control. This is even more true now that that Gabor
kernel is windowed as opposed to truncated, which means increasing
the bandwidth will just turn the Gaussian component of the Gabor
into a Hann window. While decreasing the bandwidth will eliminate
the harmonic from the Gabor kernel, which is the point of Gabor
noise.
- OSL had three discrete modes of operation for orienting the kernel.
Anisotropic, Isotropic, and a hybrid mode. While this implementation
provides a continuous Anisotropy parameter which users are already
familiar with from the Glossy BSDF node.
- This implementation provides not just the Gabor noise value, but
also its phase and intensity components. The Gabor noise value is
basically sin(phase) * intensity, but the phase is arguably more
useful since it does not suffer from the low contrast issues that
Gabor suffers from. While the intensity is useful to hide the
singularities in the phase.
- This implementation converges faster that OSL's relative to the
impulse count, so we fix the impulses count to 8 for simplicitly.
- This implementation does not implement anisotropic filtering.
Future improvements to the node includes implementing surface noise and
filtering. As well as extending the spectral control of the noise,
either by providing specialized kernels as was done in #110802, or by
providing some more procedural control over the frequencies of the
Gabor.
References:
[1]: Lagae, Ares, et al. "Procedural noise using sparse Gabor
convolution." ACM Transactions on Graphics (TOG) 28.3 (2009): 1-10.
[2]: Tavernier, Vincent, et al. "Making gabor noise fast and
normalized." Eurographics 2019-40th Annual Conference of the European
Association for Computer Graphics. 2019.
[3]: Tricard, Thibault, et al. "Procedural phasor noise." ACM
Transactions on Graphics (TOG) 38.4 (2019): 1-13.
Pull Request: https://projects.blender.org/blender/blender/pulls/121820
252 lines
4.9 KiB
GLSL
252 lines
4.9 KiB
GLSL
/* SPDX-FileCopyrightText: 2019-2022 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/* ***** Jenkins Lookup3 Hash Functions ***** */
|
|
|
|
/* Source: http://burtleburtle.net/bob/c/lookup3.c */
|
|
|
|
#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
|
|
|
|
#define mix(a, b, c) \
|
|
{ \
|
|
a -= c; \
|
|
a ^= rot(c, 4); \
|
|
c += b; \
|
|
b -= a; \
|
|
b ^= rot(a, 6); \
|
|
a += c; \
|
|
c -= b; \
|
|
c ^= rot(b, 8); \
|
|
b += a; \
|
|
a -= c; \
|
|
a ^= rot(c, 16); \
|
|
c += b; \
|
|
b -= a; \
|
|
b ^= rot(a, 19); \
|
|
a += c; \
|
|
c -= b; \
|
|
c ^= rot(b, 4); \
|
|
b += a; \
|
|
}
|
|
|
|
#define final(a, b, c) \
|
|
{ \
|
|
c ^= b; \
|
|
c -= rot(b, 14); \
|
|
a ^= c; \
|
|
a -= rot(c, 11); \
|
|
b ^= a; \
|
|
b -= rot(a, 25); \
|
|
c ^= b; \
|
|
c -= rot(b, 16); \
|
|
a ^= c; \
|
|
a -= rot(c, 4); \
|
|
b ^= a; \
|
|
b -= rot(a, 14); \
|
|
c ^= b; \
|
|
c -= rot(b, 24); \
|
|
}
|
|
|
|
uint hash_uint(uint kx)
|
|
{
|
|
uint a, b, c;
|
|
a = b = c = 0xdeadbeefu + (1u << 2u) + 13u;
|
|
|
|
a += kx;
|
|
final(a, b, c);
|
|
|
|
return c;
|
|
}
|
|
|
|
uint hash_uint2(uint kx, uint ky)
|
|
{
|
|
uint a, b, c;
|
|
a = b = c = 0xdeadbeefu + (2u << 2u) + 13u;
|
|
|
|
b += ky;
|
|
a += kx;
|
|
final(a, b, c);
|
|
|
|
return c;
|
|
}
|
|
|
|
uint hash_uint3(uint kx, uint ky, uint kz)
|
|
{
|
|
uint a, b, c;
|
|
a = b = c = 0xdeadbeefu + (3u << 2u) + 13u;
|
|
|
|
c += kz;
|
|
b += ky;
|
|
a += kx;
|
|
final(a, b, c);
|
|
|
|
return c;
|
|
}
|
|
|
|
uint hash_uint4(uint kx, uint ky, uint kz, uint kw)
|
|
{
|
|
uint a, b, c;
|
|
a = b = c = 0xdeadbeefu + (4u << 2u) + 13u;
|
|
|
|
a += kx;
|
|
b += ky;
|
|
c += kz;
|
|
mix(a, b, c);
|
|
|
|
a += kw;
|
|
final(a, b, c);
|
|
|
|
return c;
|
|
}
|
|
|
|
#undef rot
|
|
#undef final
|
|
#undef mix
|
|
|
|
uint hash_int(int kx)
|
|
{
|
|
return hash_uint(uint(kx));
|
|
}
|
|
|
|
uint hash_int2(int kx, int ky)
|
|
{
|
|
return hash_uint2(uint(kx), uint(ky));
|
|
}
|
|
|
|
uint hash_int3(int kx, int ky, int kz)
|
|
{
|
|
return hash_uint3(uint(kx), uint(ky), uint(kz));
|
|
}
|
|
|
|
uint hash_int4(int kx, int ky, int kz, int kw)
|
|
{
|
|
return hash_uint4(uint(kx), uint(ky), uint(kz), uint(kw));
|
|
}
|
|
|
|
/* Hashing uint or uint[234] into a float in the range [0, 1]. */
|
|
|
|
float hash_uint_to_float(uint kx)
|
|
{
|
|
return float(hash_uint(kx)) / float(0xFFFFFFFFu);
|
|
}
|
|
|
|
float hash_uint2_to_float(uint kx, uint ky)
|
|
{
|
|
return float(hash_uint2(kx, ky)) / float(0xFFFFFFFFu);
|
|
}
|
|
|
|
float hash_uint3_to_float(uint kx, uint ky, uint kz)
|
|
{
|
|
return float(hash_uint3(kx, ky, kz)) / float(0xFFFFFFFFu);
|
|
}
|
|
|
|
float hash_uint4_to_float(uint kx, uint ky, uint kz, uint kw)
|
|
{
|
|
return float(hash_uint4(kx, ky, kz, kw)) / float(0xFFFFFFFFu);
|
|
}
|
|
|
|
/* Hashing float or vec[234] into a float in the range [0, 1]. */
|
|
|
|
float hash_float_to_float(float k)
|
|
{
|
|
return hash_uint_to_float(floatBitsToUint(k));
|
|
}
|
|
|
|
float hash_vec2_to_float(vec2 k)
|
|
{
|
|
return hash_uint2_to_float(floatBitsToUint(k.x), floatBitsToUint(k.y));
|
|
}
|
|
|
|
float hash_vec3_to_float(vec3 k)
|
|
{
|
|
return hash_uint3_to_float(floatBitsToUint(k.x), floatBitsToUint(k.y), floatBitsToUint(k.z));
|
|
}
|
|
|
|
float hash_vec4_to_float(vec4 k)
|
|
{
|
|
return hash_uint4_to_float(
|
|
floatBitsToUint(k.x), floatBitsToUint(k.y), floatBitsToUint(k.z), floatBitsToUint(k.w));
|
|
}
|
|
|
|
/* Hashing vec[234] into vec[234] of components in the range [0, 1]. */
|
|
|
|
vec2 hash_vec2_to_vec2(vec2 k)
|
|
{
|
|
return vec2(hash_vec2_to_float(k), hash_vec3_to_float(vec3(k, 1.0)));
|
|
}
|
|
|
|
vec3 hash_vec3_to_vec3(vec3 k)
|
|
{
|
|
return vec3(
|
|
hash_vec3_to_float(k), hash_vec4_to_float(vec4(k, 1.0)), hash_vec4_to_float(vec4(k, 2.0)));
|
|
}
|
|
|
|
vec4 hash_vec4_to_vec4(vec4 k)
|
|
{
|
|
return vec4(hash_vec4_to_float(k.xyzw),
|
|
hash_vec4_to_float(k.wxyz),
|
|
hash_vec4_to_float(k.zwxy),
|
|
hash_vec4_to_float(k.yzwx));
|
|
}
|
|
|
|
/* Hashing float or vec[234] into vec3 of components in range [0, 1]. */
|
|
|
|
vec3 hash_float_to_vec3(float k)
|
|
{
|
|
return vec3(
|
|
hash_float_to_float(k), hash_vec2_to_float(vec2(k, 1.0)), hash_vec2_to_float(vec2(k, 2.0)));
|
|
}
|
|
|
|
vec3 hash_vec2_to_vec3(vec2 k)
|
|
{
|
|
return vec3(
|
|
hash_vec2_to_float(k), hash_vec3_to_float(vec3(k, 1.0)), hash_vec3_to_float(vec3(k, 2.0)));
|
|
}
|
|
|
|
vec3 hash_vec4_to_vec3(vec4 k)
|
|
{
|
|
return vec3(hash_vec4_to_float(k.xyzw), hash_vec4_to_float(k.zxwy), hash_vec4_to_float(k.wzyx));
|
|
}
|
|
|
|
/* Hashing float or vec[234] into vec2 of components in range [0, 1]. */
|
|
|
|
vec2 hash_float_to_vec2(float k)
|
|
{
|
|
return vec2(hash_float_to_float(k), hash_vec2_to_float(vec2(k, 1.0)));
|
|
}
|
|
|
|
vec2 hash_vec3_to_vec2(vec3 k)
|
|
{
|
|
return vec2(hash_vec3_to_float(k.xyz), hash_vec3_to_float(k.zxy));
|
|
}
|
|
|
|
vec2 hash_vec4_to_vec2(vec4 k)
|
|
{
|
|
return vec2(hash_vec4_to_float(k.xyzw), hash_vec4_to_float(k.zxwy));
|
|
}
|
|
|
|
/* Other Hash Functions */
|
|
|
|
float integer_noise(int n)
|
|
{
|
|
/* Integer bit-shifts for these calculations can cause precision problems on macOS.
|
|
* Using uint resolves these issues. */
|
|
uint nn;
|
|
nn = (uint(n) + 1013u) & 0x7fffffffu;
|
|
nn = (nn >> 13u) ^ nn;
|
|
nn = (uint(nn * (nn * nn * 60493u + 19990303u)) + 1376312589u) & 0x7fffffffu;
|
|
return 0.5 * (float(nn) / 1073741824.0);
|
|
}
|
|
|
|
float wang_hash_noise(uint s)
|
|
{
|
|
s = (s ^ 61u) ^ (s >> 16u);
|
|
s *= 9u;
|
|
s = s ^ (s >> 4u);
|
|
s *= 0x27d4eb2du;
|
|
s = s ^ (s >> 15u);
|
|
|
|
return fract(float(s) / 4294967296.0);
|
|
}
|