From de6ff6eb0a4c9bc405fc16709e470d68bb4fec25 Mon Sep 17 00:00:00 2001 From: Hoshinova Date: Mon, 27 May 2024 12:48:15 +0200 Subject: [PATCH] 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 --- intern/cycles/kernel/osl/shaders/node_noise.h | 36 ++++++++++++++----- intern/cycles/kernel/svm/noise.h | 18 +++++++--- source/blender/blenlib/intern/noise.cc | 18 +++++++--- .../material/gpu_shader_material_noise.glsl | 18 +++++++--- 4 files changed, 70 insertions(+), 20 deletions(-) diff --git a/intern/cycles/kernel/osl/shaders/node_noise.h b/intern/cycles/kernel/osl/shaders/node_noise.h index 21d5f0fd28e..5384a37c7b1 100644 --- a/intern/cycles/kernel/osl/shaders/node_noise.h +++ b/intern/cycles/kernel/osl/shaders/node_noise.h @@ -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); } diff --git a/intern/cycles/kernel/svm/noise.h b/intern/cycles/kernel/svm/noise.h index cb6355a558a..295ad69f120 100644 --- a/intern/cycles/kernel/svm/noise.h +++ b/intern/cycles/kernel/svm/noise.h @@ -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)); } diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc index c0403191ad8..87c813a2fa0 100644 --- a/source/blender/blenlib/intern/noise.cc +++ b/source/blender/blenlib/intern/noise.cc @@ -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; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl index 9cb201c0017..8b009b06e85 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl @@ -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)); }