Content:
- Short notes-some info about image with information.
- Main reason why result of fract-hash is not same on GPU compare to CPU.
- How broken sin-hash.
- Good GPU-hash-functions links.
- Two examples of how GPU hash and noise is non-consistent.
- Links to gifs hash-Bugs-comparison.
Short notes to Summary-image:
Conistent:
Means — result will be the same.
And CPU-GPU and on different GPUs will be the same.
Example — in pixel [0,0]
result of hash([self_pos]+[0,1])
should be equal to result in pixel [0,1]
of hash([self_pos])
.
Then this hash is Consistent.
Equal:
Float-s generated from different sources can not be equal.
By “Equal” — I mean “very close”.
Example — 0.12300001
generated by a hash in the[0,0]
pixel is equal to 0.1230000101
generated in the[0,1]
pixel.
Most stable, best hash and noise:
Is texture — but “as hash” it bad idea to use texture because huge cost in time of texture-memory reading.
For something like a height-map — use texture to have the same height-map on every GPU, and CPU side will have the same.
Texture as noise — is best noise.
Because you can pre-generate very complex noise instead of waiting for real-time procedural-noise generation, which will be much slower than reading a pre-generated texture pixel.
Link to Summary-image.
Main reason why result of fract-hash is not same on GPU compare to CPU:
Because result of:
// CPU
floor((900./37.)*1000.)/1000. // = 24.323999
// GPU
floor((900./37.+min(iTime,0.))*1000.)/1000. // = 24.324001
Result of floor((900./37.)*1000.)/1000.
is:
24.323999 on CPU
24.324001 on GPU
And use of fract-hash just explode difference.
Look this shader https://www.shadertoy.com/view/mdfSz8
How broken sin-hash-noise:
sin-hash and noise are completely broken:
Noise-alhorithm is — “interpolation of hash by generating/getting values of hash in neighbor pixels by adding neighbor shift to position”.
And as you see on image — expected is what on left side smooth interpolation.
But because sin-hash can not generate “values of neighbor” for every pixel around — you see broken tiles, randomly broken.
Good GPU hash:
fract hash — https://www.shadertoy.com/view/4djSRW
Example of fract-hash:
// not always needed, and not always fix
// #define FIX_FRACT_HASH 1000.
float hash12(vec2 p)
{
#ifdef FIX_FRACT_HASH
p = sign(p)*(floor(abs(p))+floor(fract(abs(p))*FIX_FRACT_HASH)/FIX_FRACT_HASH);
#endif
vec3 p3 = fract(vec3(p.xyx) * .1031);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
Notice — #define FIX_FRACT_HASH
this is a “bad” fix I found to fix fract hash, look examples below, this fix is not always needed.
Only if you actually have a bugged visual look that comes from a hash — you can try this fix.
int-hash — there multiple shaders with good int-hash:
- https://www.shadertoy.com/view/WttXWX — FabriceNeyret2 “Best” Integer Hash.
This hash made by Chris Wellons — Prospecting for Hash Functions. - https://www.shadertoy.com/view/XlGcRh — markjarzynski Comparing different hash functions for GPU Rendering. Paper link.
Remember that int-hash is multiple times slower than fract-hash.
If the source of random is float value — result of int-hash will be “non consistent” same as fract-hash so just use fract-hash.
Example int-hash code from FabriceNeyret2 shader:
Hash by Chris Wellons — Prospecting for Hash Functions.
#define hashi(x) lowbias32(x)
#define hash(x) ( float( hashi(x) ) / float( 0xffffffffU ) )
//bias: 0.17353355999581582 ( very probably the best of its kind )
uint lowbias32(uint x)
{
x ^= x >> 16;
x *= 0x7feb352dU;
x ^= x >> 15;
x *= 0x846ca68bU;
x ^= x >> 16;
return x;
}
int-seed-hash:
Use any int-hash and do instead of uint x
as input — use seed
that you generate with hash and do seed++
after every hash.
Example from Godot Particle shader code:
float rand_from_seed(inout uint seed) {
int k;
int s = int(seed);
if (s == 0)
s = 305420679;
k = s / 127773;
s = 16807 * (s - k * 127773) - 2836 * k;
if (s < 0)
s += 2147483647;
seed = uint(s);
return float(seed % uint(65536)) / 65535.0;
}
float rand_from_seed_m1_p1(inout uint seed) {
return rand_from_seed(seed) * 2.0 - 1.0;
}
uint hash(uint x) {
x = ((x >> uint(16)) ^ x) * uint(73244475);
x = ((x >> uint(16)) ^ x) * uint(73244475);
x = (x >> uint(16)) ^ x;
return x;
}
// correct usage:
// uint seed = hash(some_INDEX); // particle index for example
// float random_hash = rand_from_seed(seed);
//... latter in code
// float random_hash2 = rand_from_seed(seed);
Noise:
Use texture-noise.
Examples of how Hash and procedural Noise is non consistent:
Example 1— City shader https://www.shadertoy.com/view/Ntcyz7
A screenshot shows what is broken.
The logic of the shader is — I generate “height map — heigh of building and number of rooms in each building” in hash — very simple get_heightField
function in Common around line 133.
To trigger a bug — look for#define show_hash_bug
in Common.
Bug — some lighting in the building is “broken”, like they have different heights in some parts of logic.
I think, can be wrong — because some parts of logic being pre-calculated on CPU and those parts use the hash22 function.
Look in Commonget_heightField
line pos+=hash_zero_GPU;
Fix — just forsing all calls to get_heightField
to be on GPU by adding hash_zero_GPU
that is hash_zero_GPU=min(float(iFrame),0.);
Replacing fract-hash with int-hash — bug still here:
Look in Common #define show_hash_bug_use_uint_random
The ultimate fix in this City shader case would be — just use a height-map in the texture.
Or — generate height-voxel-map from uint-hash once at shader code start — but it huge overhead generating entire voxel map in every pixel.
Example 2 — Rough Seas shader https://www.shadertoy.com/view/Xc23DW
Note — I use the Rough Seas shader to show bug in my fork of original.
Original by Dave_Hoskins — https://www.shadertoy.com/view/dtXGW4
This screenshot from Nvidia-Vulkan.
chrome.exe --use-angle=vulkan --enable-features=Vulkan,DefaultANGLEVulkan,VulkanFromANGLE
If you see same bug as on screenshot — look #define USE_FIXED_FLOAT_RANGE_FIX_HASH
To test uint hash #define TEST_UINT_HASH
And to see more int-hash bugs #define MORE_UINT_HASH_BUGS
For this screenshot I move camera-mouse little left from the middle.
It looks similar to what fract-hash shows.
This can show that — if source of random is a float — any hash is not consistent.
I made a comparison-gif of this Rough Seas shader on Nvidia-AMD GPUs, you can see the difference. Look below.
Links to gifs hash-Bugs-comparison:
Made in this shader — https://www.shadertoy.com/view/Xc23DW
Comparison between Nvidia and AMD GPU visual results.
On GIFs square on the left top — Nvidia is green — AMD is red.
- No changes to code — original gif.
- With
#define USE_FIXED_FLOAT_RANGE_FIX_HASH
— fixed gif.
Notice — since used fract hash — if frach-hash were “consistent” — there should be no difference in screenshots, but even “pixel-hash pattern on water” is different, also “clouds on water” and some small geometry diferences. - With
#define TEST_UINT_HASH
— uint hash result gif.
Notice — same as above — is uint-hash were consistent images should be exact same, but they not exact same. - With
#define TEST_UINT_HASH
and#define USE_FIXED_FLOAT_RANGE_FIX_HASH
— fixed uint gif.
Notice — huge line on middle is fixed because “float fix”.
But “pixel noise” not exact same — still not consistent. - With
#define TEST_UINT_HASH
and#define MORE_UINT_HASH_BUGS
— just for fun more bugs gif. - With
#define TEST_TEXTURE_NOISE
— texture gif.
Notice — texture read withtextureLod
no mimpaps involved, only interpolation — and image still not exact same.
That all.