Implementing Noise with Hash functions for Graphics Processing Units
Matias Valdenegro-Toro, Hector Pincheira

TL;DR
This paper introduces a method to generate noise on GPUs using hash functions instead of textures, reducing memory bandwidth at the cost of increased computation, and demonstrates its implementation with several hash functions.
Contribution
It presents a novel approach to generate Perlin noise using hash functions on GPUs, eliminating the need for texture lookups and enabling efficient noise computation.
Findings
Hash-based noise performs comparably to texture-based methods.
Modified hash functions enable texture-free noise generation.
Trade-off between computation and memory bandwidth is demonstrated.
Abstract
We propose a modification to Perlin noise which use computable hash functions instead of textures as lookup tables. We implemented the FNV1, Jenkins and Murmur hashes on Shader Model 4.0 Graphics Processing Units for noise generation. Modified versions of the FNV1 and Jenkins hashes provide very close performance compared to a texture based Perlin noise implementation. Our noise modification enables noise function evaluation without any texture fetches, trading computational power for memory bandwidth.
Peer Reviews
No public reviews on file for this paper yet. If you reviewed it on a platform where reviews are public (OpenReview, ICLR, NeurIPS, ICML), you can paste yours below so the community can read it here.
Code & Models
Videos
No videos yet. Explain this paper in a talk, walkthrough, or lecture? Add one.
Taxonomy
TopicsVideo Analysis and Summarization · Advanced Image and Video Retrieval Techniques · Image Processing and 3D Reconstruction
Implementing Noise with Hash functions for Graphics Processing Units
Matias Valdenegro-Toro
Departamento de Informatica
Universidad Tecnologica Metropolitana
Santiago, Chile
Hector Pincheira Conejeros
Departamento de Informatica
Universidad Tecnologica Metropolitana
Santiago, Chile
Abstract
We propose a modification to Perlin noise which use computable hash functions instead of textures as lookup tables. We implemented the FNV1, Jenkins and Murmur hashes on Shader Model 4.0 Graphics Processing Units for noise generation. Modified versions of the FNV1 and Jenkins hashes provide very close performance compared to a texture based Perlin noise implementation. Our noise modification enables noise function evaluation without any texture fetches, trading computational power for memory bandwidth.
Index Terms:
Computer Graphics; Graphics Processors; Perlin Noise
I Introduction
Noise is a primitive function used in computer graphics to create real-looking procedural content and textures. It was introduced by Perlin [1] and it is the standard implementation for noise. The noise function returns a pseudorandom deterministic scalar output based on its n-dimensional input.
A noise function has some desirable features [2], such as:
Continuous in its domain. 2. 2.
A defined output domain, usually [-1, 1]. 3. 3.
An average of zero. 4. 4.
Statistically invariant to transformations on its domain. 5. 5.
Band limited in frequency.
The original Perlin Noise algorithm was suited to a CPU implementation, and uses two lookup tables. A permutation table is used as a hashing function and a gradient table. Accessing them in Graphics Processing Unit (GPU) or massive parallel architectures can be a bottleneck, as the noise function can be used several times per processed fragment.
Removing the dependency on lookup tables is a difficult matter, as they provide the necessary entropy to generate a pseudorandom output. A pure computable noise function would be valuable for a hardware and/or a GPU implementation.
This paper proposes modifications to Perlin Noise which makes it purely computable, replacing both lookup tables with functions computed at runtime. This enables a fast GPU implementation, using the OpenGL Shading Language (GLSL).
II Previous Work
On [3], Perlin introduced modifications to its classic Perlin Noise, using higher order interpolants remove discontinuities in the second derivate, which produced artifacts, and a new gradient distribution which hides some lattice-aligned artifacts.
There are several GPU implementations of Perlin Noise, such as baking a 1D/2D/3D noise texture and sampling to get noise values [4], or implementing the complete algorithm, using textures to store the lookup tables [5].
Olano [4] proposed a modification to Perlin Noise using a LCG-based hash function, and an alternate gradient distribution, which produced noise with a period of 61 units. His proposal was aimed to a GPU Shader Assembly implementation.
III Generalized Perlin Noise
Perlin noise uses a function to assign every point in a integer lattice space a gradient vector of the same dimension. Perlin calculates this gradient using a precalculated gradient table (), indexed with the aid of a precalculated permutation table ():
int permute(int x, int y, int z) { int px = permTable[x]; int py = permTable[y + px];
return permTable[py + z];
}
vec3 gradient(int x, int y, int z) { return gradTable[permute(x, y, z)]; }
There is no dependency between this gradient generation methodology and the Perlin noise algorithm [1]. Any other random generation method should be enough.
IV Modern Graphics Processing Units
GPUs have evolved from a fixed function programming model to a programmable model, in which the developer can execute code in defined stages of the graphics pipeline to achieve different effects.
GPU features are defined by shader model versions, defined between Microsoft and Hardware Vendors. The most recent version as this writing is Shader Model 4.0, which supports several features [6] useful for this paper:
Full support for signed and unsigned integers, and bit operations on them. 2. 2.
Unfiltered texture fetches. 3. 3.
Texture fetches with pixel coordinates. 4. 4.
Unlimited number of executed instructions.
Integer and bit operation support is required to implement any common hashing function. This functionality is accessed through the OpenGL Shading Language.
The GL_EXT_gpu_shader4 [7] OpenGL extension exposes this funcionality for Shader Model 4.0 hardware. This extension has been integrated into the core OpenGL specification in version 3.0 [8].
V Proposed changes to Perlin Noise
We propose changing Perlin’s gradient generation with a real hashing function, evaluated at runtime without lookup tables.
Using a hash function , it is evaluated on each component of the noise function input, but linked to the previous component evaluation in a similar way Perlin linked to its permutation evaluation. Then a n-dimensional integer vector is constructed, and used to evaluate a trigonometric function, converting the integer vector into a floatin point vector, finally yielding the n-dimensional gradient.
vec2 gradient(ivec2 p) { int x = hash(p.x); int y = hash(x + p.y);
return sin(vec2(x + y, y + y));
}
vec3 gradient(ivec3 p) { int x = hash(p.x); int y = hash(x + p.y); int z = hash(y + p.z);
return sin(vec3(z + x, z + y, z + z));
}
Later the gradient is used normally with the Perlin Noise algorithm. For the hash function we chose 3 candidates, the Fowler-Noll-Vo-1 (FNV1), Murmur and Jenkins hashes. Criteria for the hash function selection is:
- •
Small code footprint.
- •
Small execution time.
- •
Not a cryptographycally secure hash (due to execution time constraints).
V-A The FNV1 hash
FNV is a hash function created by Fowler, Noll, and Vo [9]. The hash is defined for power of two output bitsizes, starting from 32 bits to 1024 bits. It uses two magic numbers, the FNV offset basis and the FNV prime, both dependant on the output size. The pseudocode for the hash follows:
int hash(int input) { int ret = fnvOffsetBasis;
for each byte i in input {
ret = ret * fnvPrime;
ret = ret ^ i;
}
return ret;
}
V-B The Murmur Hash
Murmur is a hash function created by Appleby [10], which claims to have a excellent distribution, excellent avalanche and excellent collision resistance. It processes 32-bit blocks and has output size of 32 bits. The pseudocode for the hash follows:
const int m = 1540483477;
int hash(int[] k, int length) { int h = k ^ length;
for(int i = 0; i < length; i++) {
k[i] *= m;
k[i] ^= k[i] >> 24;
k[i] *= m;
h *= m;
h ^= k[i];
}
return h;
}
V-C The Jenkins Hash
Jenkins hash is a family of hash function by Jenkins [11], but we refer specifically to the “one-at-a-time” version. It processes the input in 8-bit blocks, and doesn’t use any magic numbers. The pseudocode for the hash follows:
int hash(int input) { int ret = 0;
for each byte i in input {
ret += i;
ret += (ret << 10);
ret ^= (ret >> 6);
}
ret += (ret << 3);
ret ^= (ret >> 11);
ret += (ret << 15);
return ret;
}
VI Hash implementations on the GPU
Each hash can be implemented in a shader for direct evaluation. The only problem is generated by hashes which operate in blocks smaller than 32-bits, because the extension specification only allows 32-bit integers.
To overcome this limitation we split the input into 8-bit blocks stored in 32-bit integers, using bit operations, and process those blocks as they were 8-bit integers. This wastes some computational power in the process of splitting and processing bigger integers than it is necessary.
Each hash implementation using GLSL can be seen in Figures 1, 2 and 3. All hashes are evaluated for a 32-bit input.
VII Partial Hashing
Initial performance measures using the three chosen hashes showed that the implementation is significantly slower than texture based Perlin Noise. To improve performance, we modified the FNV1 and Jenkins hashes to operate directly in 32-bit integers, instead on 8-bit integers. For the Jenkins hash, we found that sufficient randomness is generated using only one iteration of the inner loop, but for the FNV1 hash, two iterations are required to get smooth noise. We call this modified hashes “Partial” versions. Implementations are shown in Figures 4 and 5.
VIII Statistical Properties
Our proposed noise functions generate pseudorandom numbers in , with an average of . Perlin noise has a approximate uniform distribution [1], but changing the gradient generation might produce a different distribution. We found through simulation that all proposed functions have gaussian-like distributions. Partial hashes produce the same gaussian distribution in the noise output.
Classic Perlin noise has a period of 256 units, which is limited by the size of the lookup tables. Our proposed noise functions don’t have a period set by the algorithm, but we choose to limit the period to to avoid artifacts because of integer to floating point convertion.
IX Implementation
The proposed modifications were implemented in OpenGL 3.0, using the OpenGL Shading Language v1.30. To measure performance, we rendered a texture mapped quad, using a texture coordinate as input to the noise function; the scalar result was propagated to the rgb components to achieve a grayscale output.
X Performance
Performance measures were made using a Dell XPS m1330 laptop, with a GeForce 8400M GS GPU with 180.37.05 drivers on ArchLinux i686. To get instruction counts, we used NVIDIA’s Cg Compiler, which can compile GLSL code to NVfp4 Assembly.
To get comparable results, an already implemented Perlin Noise function was used. This function is implemented using textures to store the permutation and gradient tables. Two versions of this function were used, one implemented using floating point mathematic (Perlin/Float), and other using integer arithmetic (Perlin/Integer).
Performance was measured using the render time in milliseconds as metric, at different resolutions for 2D and 3D Noise.
XI Results
Example renders are shown in Figure 6. The first column is a render of the noise function, the second column is a render of the turbulence funciton using the same noise function, and the third column is a render of a proocedural cloud texture using the same noise function. Performance measures are shown in Figure 7.
Our performance data showed that the proposed implementations are slower than regular texture-based Perlin Noise, only Perlin/PartialFNV1 and Perlin/PartialJenkins are close enough to Perlin/Float to be considered an alternative implementation.
The tradeoff between speed and period is alleviated in Perlin/PartialFNV1 and Perlin/PartialJenkins. Both can be considered alternatives because of their “cheap” cost and considerable large period.
Noise generated by our proposed functions is of comparable quality when compared to Perlin/Float and Perlin/Integer.
On great advantage of our implementation is that Modern and newer GPUs can execute more ALU instructions per texture fetches than older processors [12], and therefore a developer needs to use more computational power to hide the latency of texture fetching. Our noise functions moves workload from texture bandwith to ALU units, and can help balance the workload between different GPU components.
XII Future Work
In the future, we would like to implement other noise functions on GPUs, such as Worley’s cellular noise. But more important, is to demonstrate the advantage of hashing functions over precomputed tables in memory bandwith limited applications.
XIII Conclusion
We researched alternate implementations of noise for modern graphics processing units, using hash functions to replace lookup tables with runtime computable data.
We expect that with faster noise implementations its usage in realtime applications such as commercial games, will grow. We recommend using Perlin/PartialFNV1 and/or Perlin/PartialJenkins as they have a large period ( units) and its performance is acceptable.
There is still room for improvement. A function can never be fast enough for real time applications. Perlin’s Simplex noise could be modified in the same way, but it would only require contributions from neighbours, as opposed by contributions needed for Perlin noise.
A mix of Simplex noise and hash functions could lead to a silicon hardware implementation. The OpenGL Shading Language specification requires a noise function, but there’s no major hardware implementation. The availability of fast noise would push its adoption in the industry.
Acknowledgments
The authors would like to thank Sebastian Machuca and Gonzalo Gaete.
The reference list from the paper itself. Each links out to its DOI / PubMed record.
- 1[1] K. Perlin, “An image synthesizer,” SIGGRAPH Comput. Graph. , vol. 19, no. 3, pp. 287–296, 1985.
- 2[2] K. Group, Open GL Shading Language Specification, version 1.40 revision 5 , 2009.
- 3[3] K. Perlin, “Improving noise,” in SIGGRAPH ’02: Proceedings of the 29th annual conference on Computer graphics and interactive techniques . ACM, 2002, pp. 681–682.
- 4[4] M. Olano, “Modified noise for evaluation on graphics hardware,” in HWWS ’05: Proceedings of the ACM SIGGRAPH/EUROGRAPHICS conference on Graphics hardware . New York, NY, USA: ACM, 2005, pp. 105–110.
- 5[5] S. Green, “Implementing improved perlin noise,” GPU Gems 2 , 2005.
- 6[6] D. Blythe, “The Direct 3D 10 system,” ACM Trans. Graph. , vol. 25, no. 3, pp. 724–734, 2006.
- 7[7] NVIDIA and Others, “GL_EXT_gpu_shader 4 Open GL extension,” 2006.
- 8[8] K. Group, The Open GL Graphics System: A Specification, Version 3.0 , 2008.
