Fix #121969: Noise texture outputs 0 with coordinates beyond 8.40*10^6

One of the properties of Perlin noise is that it always evaluates to 0.0
when not normalized (or 0.5 when normalized) when the input consists of
only whole integers in all vector components.

Blender's Perlin noise implementation uses single precision floats with
a machine epsilon of 1.19e-07 meaning that for numbers that are greater
than 1/(1.19e-07) = 8.40e6 there mantissa doesn't have any bits left to
store a rational part of the number, effectively meaning that any number
greater than 8.40e6 is a whole integer as far as Blender is concerned.
Therefore when evaluating Perlin noise for any coordinates greater than
that it always results in 0.0 (or 0.5 when normalized).

This fix works as follows: If the original input number is larger than
1.0e6 it is offset by 0.5 after it underwent modulo, which always outputs
numbers in a [0.0, 1.0e5) range leaving the mantissa room for a rational
part. This way the quantization error still persists however the outputs
are random again instead of a constant 0.0.

Pull Request: https://projects.blender.org/blender/blender/pulls/122112
This commit is contained in:
Hoshinova
2024-05-27 12:48:15 +02:00
committed by Brecht Van Lommel
parent e7489664ce
commit de6ff6eb0a
4 changed files with 70 additions and 20 deletions
+28 -8
View File
@@ -9,78 +9,98 @@
float safe_noise(float co)
{
float precision_correction = 0.5 * float(fabs(co) >= 1000000.0);
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. */
float p = fmod(co, 100000.0);
float p = fmod(co, 100000.0) + precision_correction;
return noise("noise", p);
}
float safe_noise(vector2 co)
{
vector2 precision_correction = 0.5 * vector2(float(fabs(co.x) >= 1000000.0),
float(fabs(co.y) >= 1000000.0));
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0, however at such scales this
* usually shouldn't be noticeable. */
vector2 p = fmod(co, 100000.0);
vector2 p = fmod(co, 100000.0) + precision_correction;
return noise("noise", p.x, p.y);
}
float safe_noise(vector3 co)
{
vector3 precision_correction = 0.5 * vector3(float(fabs(co.x) >= 1000000.0),
float(fabs(co.y) >= 1000000.0),
float(fabs(co.z) >= 1000000.0));
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0, however at such scales this
* usually shouldn't be noticeable. */
vector3 p = fmod(co, 100000.0);
vector3 p = fmod(co, 100000.0) + precision_correction;
return noise("noise", p);
}
float safe_noise(vector4 co)
{
vector4 precision_correction = 0.5 * vector4(float(fabs(co.x) >= 1000000.0),
float(fabs(co.y) >= 1000000.0),
float(fabs(co.z) >= 1000000.0),
float(fabs(co.w) >= 1000000.0));
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0, however at such scales this
* usually shouldn't be noticeable. */
vector4 p = fmod(co, 100000.0);
vector4 p = fmod(co, 100000.0) + precision_correction;
return noise("noise", vector3(p.x, p.y, p.z), p.w);
}
float safe_snoise(float co)
{
float precision_correction = 0.5 * float(fabs(co) >= 1000000.0);
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. */
float p = fmod(co, 100000.0);
float p = fmod(co, 100000.0) + precision_correction;
return noise("snoise", p);
}
float safe_snoise(vector2 co)
{
vector2 precision_correction = 0.5 * vector2(float(fabs(co.x) >= 1000000.0),
float(fabs(co.y) >= 1000000.0));
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0, however at such scales this
* usually shouldn't be noticeable. */
vector2 p = fmod(co, 100000.0);
vector2 p = fmod(co, 100000.0) + precision_correction;
return noise("snoise", p.x, p.y);
}
float safe_snoise(vector3 co)
{
vector3 precision_correction = 0.5 * vector3(float(fabs(co.x) >= 1000000.0),
float(fabs(co.y) >= 1000000.0),
float(fabs(co.z) >= 1000000.0));
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0, however at such scales this
* usually shouldn't be noticeable. */
vector3 p = fmod(co, 100000.0);
vector3 p = fmod(co, 100000.0) + precision_correction;
return noise("snoise", p);
}
float safe_snoise(vector4 co)
{
vector4 precision_correction = 0.5 * vector4(float(fabs(co.x) >= 1000000.0),
float(fabs(co.y) >= 1000000.0),
float(fabs(co.z) >= 1000000.0),
float(fabs(co.w) >= 1000000.0));
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0, however at such scales this
* usually shouldn't be noticeable. */
vector4 p = fmod(co, 100000.0);
vector4 p = fmod(co, 100000.0) + precision_correction;
return noise("snoise", vector3(p.x, p.y, p.z), p.w);
}
+14 -4
View File
@@ -684,10 +684,11 @@ ccl_device_inline float noise_scale4(float result)
ccl_device_inline float snoise_1d(float p)
{
float precision_correction = 0.5f * float(fabsf(p) >= 1000000.0f);
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. */
/* The 1D variant of fmod is called fmodf. */
p = fmodf(p, 100000.0f);
p = fmodf(p, 100000.0f) + precision_correction;
return noise_scale1(perlin_1d(p));
}
@@ -699,10 +700,12 @@ ccl_device_inline float noise_1d(float p)
ccl_device_inline float snoise_2d(float2 p)
{
float2 precision_correction = 0.5f * make_float2(float(fabsf(p.x) >= 1000000.0f),
float(fabsf(p.y) >= 1000000.0f));
/* Repeat Perlin noise texture every 100000.0f on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0f, however at such scales
* this usually shouldn't be noticeable. */
p = fmod(p, 100000.0f);
p = fmod(p, 100000.0f) + precision_correction;
return noise_scale2(perlin_2d(p.x, p.y));
}
@@ -714,10 +717,13 @@ ccl_device_inline float noise_2d(float2 p)
ccl_device_inline float snoise_3d(float3 p)
{
float3 precision_correction = 0.5f * make_float3(float(fabsf(p.x) >= 1000000.0f),
float(fabsf(p.y) >= 1000000.0f),
float(fabsf(p.z) >= 1000000.0f));
/* Repeat Perlin noise texture every 100000.0f on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0f, however at such scales
* this usually shouldn't be noticeable. */
p = fmod(p, 100000.0f);
p = fmod(p, 100000.0f) + precision_correction;
return noise_scale3(perlin_3d(p.x, p.y, p.z));
}
@@ -729,10 +735,14 @@ ccl_device_inline float noise_3d(float3 p)
ccl_device_inline float snoise_4d(float4 p)
{
float4 precision_correction = 0.5f * make_float4(float(fabsf(p.x) >= 1000000.0f),
float(fabsf(p.y) >= 1000000.0f),
float(fabsf(p.z) >= 1000000.0f),
float(fabsf(p.w) >= 1000000.0f));
/* Repeat Perlin noise texture every 100000.0f on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0f, however at such scales
* this usually shouldn't be noticeable. */
p = fmod(p, 100000.0f);
p = fmod(p, 100000.0f) + precision_correction;
return noise_scale4(perlin_4d(p.x, p.y, p.z, p.w));
}
+14 -4
View File
@@ -490,39 +490,49 @@ BLI_INLINE float perlin_noise(float4 position)
float perlin_signed(float position)
{
float precision_correction = 0.5f * float(math::abs(position) >= 1000000.0f);
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. */
position = math::mod(position, 100000.0f);
position = math::mod(position, 100000.0f) + precision_correction;
return perlin_noise(position) * 0.2500f;
}
float perlin_signed(float2 position)
{
float2 precision_correction = 0.5f * float2(float(math::abs(position.x) >= 1000000.0f),
float(math::abs(position.y) >= 1000000.0f));
/* Repeat Perlin noise texture every 100000.0f on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0f, however at such scales
* this usually shouldn't be noticeable. */
position = math::mod(position, 100000.0f);
position = math::mod(position, 100000.0f) + precision_correction;
return perlin_noise(position) * 0.6616f;
}
float perlin_signed(float3 position)
{
float3 precision_correction = 0.5f * float3(float(math::abs(position.x) >= 1000000.0f),
float(math::abs(position.y) >= 1000000.0f),
float(math::abs(position.z) >= 1000000.0f));
/* Repeat Perlin noise texture every 100000.0f on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0f, however at such scales
* this usually shouldn't be noticeable. */
position = math::mod(position, 100000.0f);
position = math::mod(position, 100000.0f) + precision_correction;
return perlin_noise(position) * 0.9820f;
}
float perlin_signed(float4 position)
{
float4 precision_correction = 0.5f * float4(float(math::abs(position.x) >= 1000000.0f),
float(math::abs(position.y) >= 1000000.0f),
float(math::abs(position.z) >= 1000000.0f),
float(math::abs(position.w) >= 1000000.0f));
/* Repeat Perlin noise texture every 100000.0f on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0f, however at such scales
* this usually shouldn't be noticeable. */
position = math::mod(position, 100000.0f);
position = math::mod(position, 100000.0f) + precision_correction;
return perlin_noise(position) * 0.8344f;
}
@@ -261,9 +261,10 @@ float noise_scale4(float result)
float snoise(float p)
{
float precision_correction = 0.5 * float(abs(p) >= 1000000.0);
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. */
p = compatible_fmod(p, 100000.0);
p = compatible_fmod(p, 100000.0) + precision_correction;
return noise_scale1(noise_perlin(p));
}
@@ -275,10 +276,12 @@ float noise(float p)
float snoise(vec2 p)
{
vec2 precision_correction = 0.5 *
vec2(float(abs(p.x) >= 1000000.0), float(abs(p.y) >= 1000000.0));
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0, however at such scales this
* usually shouldn't be noticeable. */
p = compatible_fmod(p, 100000.0);
p = compatible_fmod(p, 100000.0) + precision_correction;
return noise_scale2(noise_perlin(p));
}
@@ -290,10 +293,13 @@ float noise(vec2 p)
float snoise(vec3 p)
{
vec3 precision_correction = 0.5 * vec3(float(abs(p.x) >= 1000000.0),
float(abs(p.y) >= 1000000.0),
float(abs(p.z) >= 1000000.0));
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0, however at such scales this
* usually shouldn't be noticeable. */
p = compatible_fmod(p, 100000.0);
p = compatible_fmod(p, 100000.0) + precision_correction;
return noise_scale3(noise_perlin(p));
}
@@ -305,10 +311,14 @@ float noise(vec3 p)
float snoise(vec4 p)
{
vec4 precision_correction = 0.5 * vec4(float(abs(p.x) >= 1000000.0),
float(abs(p.y) >= 1000000.0),
float(abs(p.z) >= 1000000.0),
float(abs(p.w) >= 1000000.0));
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0, however at such scales this
* usually shouldn't be noticeable. */
p = compatible_fmod(p, 100000.0);
p = compatible_fmod(p, 100000.0) + precision_correction;
return noise_scale4(noise_perlin(p));
}