GLSL 的随机/噪声函数

发布于 2024-10-02 15:57:25 字数 816 浏览 0 评论 0 原文

由于 GPU 驱动程序供应商通常不会费心在 GLSL 中实现 noiseX,因此我正在寻找一个“图形随机化瑞士军刀” 实用函数集,最好优化为在 GPU 着色器中使用。我更喜欢 GLSL,但是任何语言的代码都适合我,我可以自己将其翻译为 GLSL。

具体来说,我期望:

a)伪随机函数 - N 维,在 [-1,1] 或 [0,1] 上均匀分布,从 M 维种子计算(理想情况下)可以是任何值,但我可以将种子限制为 0..1 以实现均匀的结果分布)。类似于:

float random  (T seed);
vec2  random2 (T seed);
vec3  random3 (T seed);
vec4  random4 (T seed);
// T being either float, vec2, vec3, vec4 - ideally.

b) 连续噪声,如 Perlin 噪声 - 再次,N 维,+- 均匀分布,具有受约束的值集,并且看起来不错(一些用于配置外观的选项,例如 Perlin 级别也可能有用)。我期望这样的签名:

float noise  (T coord, TT seed);
vec2  noise2 (T coord, TT seed);
// ...

我不太喜欢随机数生成理论,所以我最热切地寻求预制解决方案,但我也很欣赏像 “这是一个非常好的、高效的一维 rand(),让我向您解释如何在它的基础上创建一个好的 N 维 rand()...”

As the GPU driver vendors don't usually bother to implement noiseX in GLSL, I'm looking for a "graphics randomization swiss army knife" utility function set, preferably optimised to use within GPU shaders. I prefer GLSL, but code any language will do for me, I'm ok with translating it on my own to GLSL.

Specifically, I'd expect:

a) Pseudo-random functions - N-dimensional, uniform distribution over [-1,1] or over [0,1], calculated from M-dimensional seed (ideally being any value, but I'm OK with having the seed restrained to, say, 0..1 for uniform result distribution). Something like:

float random  (T seed);
vec2  random2 (T seed);
vec3  random3 (T seed);
vec4  random4 (T seed);
// T being either float, vec2, vec3, vec4 - ideally.

b) Continous noise like Perlin Noise - again, N-dimensional, +- uniform distribution, with constrained set of values and, well, looking good (some options to configure the appearance like Perlin levels could be useful too). I'd expect signatures like:

float noise  (T coord, TT seed);
vec2  noise2 (T coord, TT seed);
// ...

I'm not very much into random number generation theory, so I'd most eagerly go for a pre-made solution, but I'd also appreciate answers like "here's a very good, efficient 1D rand(), and let me explain you how to make a good N-dimensional rand() on top of it..." .

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

不乱于心 2024-10-09 15:57:25

对于非常简单的伪随机外观的东西,我使用我在互联网上找到的这个 oneliner:

float rand(vec2 co){
    return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);
}

您还可以使用您喜欢的任何 PRNG 生成噪声纹理,然后以正常方式上传它并在着色器中对值进行采样;如果您愿意,我可以稍后挖掘代码示例。

另外,请查看 此文件,了解 Perlin 和 Simplex 噪声的 GLSL 实现,作者:Stefan古斯塔夫森。

For very simple pseudorandom-looking stuff, I use this oneliner that I found on the internet somewhere:

float rand(vec2 co){
    return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);
}

You can also generate a noise texture using whatever PRNG you like, then upload this in the normal fashion and sample the values in your shader; I can dig up a code sample later if you'd like.

Also, check out this file for GLSL implementations of Perlin and Simplex noise, by Stefan Gustavson.

柒七 2024-10-09 15:57:25

我想到您可以使用一个简单的整数哈希函数并将结果插入到浮点数的尾数中。 IIRC GLSL 规范保证 32 位无符号整数和 IEEE 二进制 32 浮点表示,因此它应该是完全可移植的。

我刚才尝试了一下。结果非常好:我尝试的每个输入看起来都像静态的一样,根本没有可见的模式。相比之下,在相同输入的情况下,流行的 sin/fract 片段在我的 GPU 上有相当明显的对角线。

一个缺点是它需要 GLSL v3.30。尽管它看起来足够快,但我还没有根据经验量化其性能。 AMD 的着色器分析器声称 HD5870 上的 vec2 版本每时钟像素为 13.33 像素。与 sin/fract 片段每时钟 16 像素进行对比。所以它肯定会慢一些。

这是我的实现。我将其保留在想法的各种排列中,以便更容易从中导出您自己的函数。

/*
    static.frag
    by Spatial
    05 July 2013
*/

#version 330 core

uniform float time;
out vec4 fragment;



// A single iteration of Bob Jenkins' One-At-A-Time hashing algorithm.
uint hash( uint x ) {
    x += ( x << 10u );
    x ^= ( x >>  6u );
    x += ( x <<  3u );
    x ^= ( x >> 11u );
    x += ( x << 15u );
    return x;
}



// Compound versions of the hashing algorithm I whipped together.
uint hash( uvec2 v ) { return hash( v.x ^ hash(v.y)                         ); }
uint hash( uvec3 v ) { return hash( v.x ^ hash(v.y) ^ hash(v.z)             ); }
uint hash( uvec4 v ) { return hash( v.x ^ hash(v.y) ^ hash(v.z) ^ hash(v.w) ); }



// Construct a float with half-open range [0:1] using low 23 bits.
// All zeroes yields 0.0, all ones yields the next smallest representable value below 1.0.
float floatConstruct( uint m ) {
    const uint ieeeMantissa = 0x007FFFFFu; // binary32 mantissa bitmask
    const uint ieeeOne      = 0x3F800000u; // 1.0 in IEEE binary32

    m &= ieeeMantissa;                     // Keep only mantissa bits (fractional part)
    m |= ieeeOne;                          // Add fractional part to 1.0

    float  f = uintBitsToFloat( m );       // Range [1:2]
    return f - 1.0;                        // Range [0:1]
}



// Pseudo-random value in half-open range [0:1].
float random( float x ) { return floatConstruct(hash(floatBitsToUint(x))); }
float random( vec2  v ) { return floatConstruct(hash(floatBitsToUint(v))); }
float random( vec3  v ) { return floatConstruct(hash(floatBitsToUint(v))); }
float random( vec4  v ) { return floatConstruct(hash(floatBitsToUint(v))); }





void main()
{
    vec3  inputs = vec3( gl_FragCoord.xy, time ); // Spatial and temporal inputs
    float rand   = random( inputs );              // Random per-pixel value
    vec3  luma   = vec3( rand );                  // Expand to RGB

    fragment = vec4( luma, 1.0 );
}

屏幕截图:

Output of random(vec3) in static.frag

我在图像编辑程序中检查了屏幕截图。有 256 种颜色,平均值为 127,这意味着分布均匀并且覆盖了预期范围。

It occurs to me that you could use a simple integer hash function and insert the result into a float's mantissa. IIRC the GLSL spec guarantees 32-bit unsigned integers and IEEE binary32 float representation so it should be perfectly portable.

I gave this a try just now. The results are very good: it looks exactly like static with every input I tried, no visible patterns at all. In contrast the popular sin/fract snippet has fairly pronounced diagonal lines on my GPU given the same inputs.

One disadvantage is that it requires GLSL v3.30. And although it seems fast enough, I haven't empirically quantified its performance. AMD's Shader Analyzer claims 13.33 pixels per clock for the vec2 version on a HD5870. Contrast with 16 pixels per clock for the sin/fract snippet. So it is certainly a little slower.

Here's my implementation. I left it in various permutations of the idea to make it easier to derive your own functions from.

/*
    static.frag
    by Spatial
    05 July 2013
*/

#version 330 core

uniform float time;
out vec4 fragment;



// A single iteration of Bob Jenkins' One-At-A-Time hashing algorithm.
uint hash( uint x ) {
    x += ( x << 10u );
    x ^= ( x >>  6u );
    x += ( x <<  3u );
    x ^= ( x >> 11u );
    x += ( x << 15u );
    return x;
}



// Compound versions of the hashing algorithm I whipped together.
uint hash( uvec2 v ) { return hash( v.x ^ hash(v.y)                         ); }
uint hash( uvec3 v ) { return hash( v.x ^ hash(v.y) ^ hash(v.z)             ); }
uint hash( uvec4 v ) { return hash( v.x ^ hash(v.y) ^ hash(v.z) ^ hash(v.w) ); }



// Construct a float with half-open range [0:1] using low 23 bits.
// All zeroes yields 0.0, all ones yields the next smallest representable value below 1.0.
float floatConstruct( uint m ) {
    const uint ieeeMantissa = 0x007FFFFFu; // binary32 mantissa bitmask
    const uint ieeeOne      = 0x3F800000u; // 1.0 in IEEE binary32

    m &= ieeeMantissa;                     // Keep only mantissa bits (fractional part)
    m |= ieeeOne;                          // Add fractional part to 1.0

    float  f = uintBitsToFloat( m );       // Range [1:2]
    return f - 1.0;                        // Range [0:1]
}



// Pseudo-random value in half-open range [0:1].
float random( float x ) { return floatConstruct(hash(floatBitsToUint(x))); }
float random( vec2  v ) { return floatConstruct(hash(floatBitsToUint(v))); }
float random( vec3  v ) { return floatConstruct(hash(floatBitsToUint(v))); }
float random( vec4  v ) { return floatConstruct(hash(floatBitsToUint(v))); }





void main()
{
    vec3  inputs = vec3( gl_FragCoord.xy, time ); // Spatial and temporal inputs
    float rand   = random( inputs );              // Random per-pixel value
    vec3  luma   = vec3( rand );                  // Expand to RGB

    fragment = vec4( luma, 1.0 );
}

Screenshot:

Output of random(vec3) in static.frag

I inspected the screenshot in an image editing program. There are 256 colours and the average value is 127, meaning the distribution is uniform and covers the expected range.

北凤男飞 2024-10-09 15:57:25

Gustavson 的实现使用一维纹理

不,从 2005 年开始就没有了。只是人们坚持下载旧版本。您提供的链接上的版本仅使用 8 位 2D 纹理。

Ashima 和我自己的 Ian McEwan 开发的新版本不使用纹理,但在具有大量纹理带宽的典型桌面平台上以大约一半的速度运行。在移动平台上,无纹理版本可能会更快,因为纹理通常是一个重要的瓶颈。

我们积极维护的源存储库是:

https://github.com/ashima/webgl-noise

无纹理的集合使用纹理的噪声版本在这里(仅使用 2D 纹理):

http ://www.itn.liu.se/~stegu/simplexnoise/GLSL-noise-vs-noise.zip

如果您有任何具体问题,请随时直接给我发电子邮件(我的电子邮件地址可以是可在 classicnoise*.glsl 源中找到。)

Gustavson's implementation uses a 1D texture

No it doesn't, not since 2005. It's just that people insist on downloading the old version. The version that is on the link you supplied uses only 8-bit 2D textures.

The new version by Ian McEwan of Ashima and myself does not use a texture, but runs at around half the speed on typical desktop platforms with lots of texture bandwidth. On mobile platforms, the textureless version might be faster because texturing is often a significant bottleneck.

Our actively maintained source repository is:

https://github.com/ashima/webgl-noise

A collection of both the textureless and texture-using versions of noise is here (using only 2D textures):

http://www.itn.liu.se/~stegu/simplexnoise/GLSL-noise-vs-noise.zip

If you have any specific questions, feel free to e-mail me directly (my email address can be found in the classicnoise*.glsl sources.)

情话已封尘 2024-10-09 15:57:25

金色噪点

// Gold Noise ©2015 [email protected]
// - based on the Golden Ratio
// - uniform normalized distribution
// - fastest static noise generator function (also runs at low precision)
// - use with indicated fractional seeding method. 

float PHI = 1.61803398874989484820459;  // Φ = Golden Ratio   

float gold_noise(in vec2 xy, in float seed){
       return fract(tan(distance(xy*PHI, xy)*seed)*xy.x);
}

立即在浏览器中查看金色噪点!

<​​/a> 在此处输入图像描述

该函数相对于 2017 年 9 月 9 日 @appas 答案中的当前函数改进了随机分布:

在此处输入图像描述

@appas 函数也不完整,因为没有提供种子(uv 不是种子 - 每个帧都相同),并且不适用于低精度芯片组。默认情况下,黄金噪声以低精度运行(速度要快得多)。

Gold Noise

// Gold Noise ©2015 [email protected]
// - based on the Golden Ratio
// - uniform normalized distribution
// - fastest static noise generator function (also runs at low precision)
// - use with indicated fractional seeding method. 

float PHI = 1.61803398874989484820459;  // Φ = Golden Ratio   

float gold_noise(in vec2 xy, in float seed){
       return fract(tan(distance(xy*PHI, xy)*seed)*xy.x);
}

See Gold Noise in your browser right now!

enter image description here

This function has improved random distribution over the current function in @appas' answer as of Sept 9, 2017:

enter image description here

The @appas function is also incomplete, given there is no seed supplied (uv is not a seed - same for every frame), and does not work with low precision chipsets. Gold Noise runs at low precision by default (much faster).

满意归宿 2024-10-09 15:57:25

在 2010 年首次发布这个问题之后,良好的随机函数和硬件支持领域发生了很多变化。

从今天的角度来看,该算法所得出的随机数的均匀性非常差。并且,均匀性会根据输入值的大小而受到很大影响,并且在针对光线/路径追踪应用等进行采样时,可见的伪影/图案将变得明显。

为此任务设计了许多不同的函数(其中大多数是整数哈希),针对不同的输入和输出维度,其中大多数正在 2020 年 JCGT 论文中进行评估 用于 GPU 渲染的哈希函数。根据您的需要,您可以从该论文中建议的函数列表中选择一个函数,并且只需从随附的 Shadertoy 中选择一个函数
本文未涵盖的内容论文,但这对我很有帮助,没有任何输入幅度值上的任何明显模式,这也是我想强调的一点。

其他类别的算法使用低差异序列来提取伪随机数,例如带有欧文置乱的 Sobol 序列。 Eric Heitz 在该领域做了一些令人惊叹的研究,以及他的分布蒙特的低差异采样器Carlo Errors as a Blue Noise in Screen Space 论文。
另一个例子是(迄今为止最新的)JCGT 论文 Practical Hash-based Owen Scrambling ,它通过 Laine-Karras 哈希函数实现 Owen 置乱,该函数本身源自论文 “随机透明度的分层采样” 但很快就被发现也是实现欧文置乱的工具。这种应用于 Sobol 序列的 Owen 置乱还有一个很好的 Shadertoy 实现
Nathan Vegdahl 还拥有一系列精彩的博客文章,介绍他的路径追踪器采样的发现和实现,这也更详细地介绍了这一点。
当然,值得一提的是由 Matt Pharr、Wenzel Jakob 和 Greg Humphreys 撰写的免费书籍《基于物理的渲染:从理论到实现》,其中介绍了 Sobol 序列和 Owen 扰乱的内容 第 8.7 章“Sobol' 采样器”

另一种可能性是所谓的“同余生成器”,“线性同余生成器”(LCG) 或 "Permuted同余生成器”(PCG),它与基于散列的算法不同,因为 LCG 和 PCG 具有“状态”和一个使该状态发生变化的关联函数,从中提取伪随机数。因此,它们不依赖于任何“输入”来计算哈希值,而只是转移/排列/变异一个状态,然后在函数的多次调用中进行。
PCG 的一种应用可以在 Nvidia 的“迷你路径跟踪器教程”中找到

还有一些类别使用​​算法来产生具有所需频谱的噪声模式,例如蓝色噪声,这对眼睛来说特别“令人愉悦”。

(我意识到 StackOverflow 的好答案应该以源代码的形式提供算法,而不是链接,因为它们可能会损坏,但是现在有太多不同的算法,我打算将此答案作为以下内容的总结今天已知的好算法)

After the initial posting of this question in 2010, a lot has changed in the realm of good random functions and hardware support for them.

Looking at the accepted answer from today's perspective, this algorithm is very bad in uniformity of the random numbers drawn from it. And the uniformity suffers a lot depending on the magnitude of the input values and visible artifacts/patterns will become apparent when sampling from it for e.g. ray/path tracing applications.

There have been many different functions (most of them integer hashing) devised for this task, for different input and output dimensionality, most of which are being evaluated in the 2020 JCGT paper Hash Functions for GPU Rendering. Depending on your needs you could select a function from the list of proposed functions in that paper and simply from the accompanying Shadertoy.
One that isn't covered in this paper but that has served me very well without any noticeably patterns on any input magnitude values is also one that I want to highlight.

Other classes of algorithms use low-discrepancy sequences to draw pseudo-random numbers from, such as the Sobol squence with Owen scrambling. Eric Heitz has done some amazing research in this area, as well with his A Low-Discrepancy Sampler that Distributes Monte Carlo Errors as a Blue Noise in Screen Space paper.
Another example of this is the (so far latest) JCGT paper Practical Hash-based Owen Scrambling, which implements Owen scrambling by means of a Laine-Karras hash function, which itself has its origins in the paper "Stratified Sampling for Stochastic Transparency" but was soon found to be also a tool for implementing Owen scrambling. This kind of Owen scrambling applied to the Sobol sequence has also a nice Shadertoy implementation.
Nathan Vegdahl also has a fantastic list of blog posts about his discoveries and implementations of sampling for his path tracer, which also covers this in more detail.
And, of course, worth mentioning is the free book "Physically Based Rendering: From Theory To Implementation" by Matt Pharr, Wenzel Jakob and Greg Humphreys, which covers the Sobol' sequence and Owen scrambling in its chapter 8.7 "Sobol' Samplers".

Another possibility are so called "Congruential Generators", either "Linear Congruential Generators" (LCG) or "Permuted Congruential Generators" (PCG), which differ from the hash-based algorithms in that LCGs and PCGs have a "state" and an associated function mutating this state from which the pseudo-random numbers are drawn. So, they do not rely on any "input" for computing a hash, but merely shift/permute/mutate a state which then carries across multiple invocations of the function.
One application of PCGs can be found in Nvidia's "Mini Path-Tracer Tutorial".

Yet other classes use algorithms that produce noise patterns with desirable frequency spectrums, such as blue noise, that is particularly "pleasing" to the eyes.

(I realize that good StackOverflow answers should provide the algorithms as source code and not as links because those can break, but there are way too many different algorithms nowadays and I intend for this answer to be a summary of known-good algorithms today)

眼波传意 2024-10-09 15:57:25

McEwan 和 @StefanGustavson 在此处描述了一个很好的实现看起来像 Perlin 噪声,但“不需要任何设置,即不需要纹理或统一数组。只需将其添加到着色器源代码中并在任何需要的地方调用它”。

这非常方便,特别是考虑到 @dep 链接到的 Gustavson 的早期实现使用 1D 纹理,即 GLSL ES(WebGL 的着色器语言)不支持。

There is also a nice implementation described here by McEwan and @StefanGustavson that looks like Perlin noise, but "does not require any setup, i.e. not textures nor uniform arrays. Just add it to your shader source code and call it wherever you want".

That's very handy, especially given that Gustavson's earlier implementation, which @dep linked to, uses a 1D texture, which is not supported in GLSL ES (the shader language of WebGL).

只是偏爱你 2024-10-09 15:57:25

请使用此:

highp float rand(vec2 co)
{
    highp float a = 12.9898;
    highp float b = 78.233;
    highp float c = 43758.5453;
    highp float dt= dot(co.xy ,vec2(a,b));
    highp float sn= mod(dt,3.14);
    return fract(sin(sn) * c);
}

不要使用此:

float rand(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

您可以在 针对 OpenGL ES 2.0 的规范单行 GLSL rand() 的改进

Do use this:

highp float rand(vec2 co)
{
    highp float a = 12.9898;
    highp float b = 78.233;
    highp float c = 43758.5453;
    highp float dt= dot(co.xy ,vec2(a,b));
    highp float sn= mod(dt,3.14);
    return fract(sin(sn) * c);
}

Don't use this:

float rand(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

You can find the explanation in Improvements to the canonical one-liner GLSL rand() for OpenGL ES 2.0

北城孤痞 2024-10-09 15:57:25

哈希:
现在有了 webGL2.0,因此 (w)GLSL 中可以使用整数。
->对于高质量的便携式哈希(与丑陋的浮点哈希的成本相似),我们现在可以使用“严肃的”哈希技术。
IQ 在 https://www.shadertoy.com/view/XlXcW4 中实现了一些(以及更多)

例如:

  const uint k = 1103515245U;  // GLIB C
//const uint k = 134775813U;   // Delphi and Turbo Pascal
//const uint k = 20170906U;    // Today's date (use three days ago's dateif you want a prime)
//const uint k = 1664525U;     // Numerical Recipes

vec3 hash( uvec3 x )
{
    x = ((x>>8U)^x.yzx)*k;
    x = ((x>>8U)^x.yzx)*k;
    x = ((x>>8U)^x.yzx)*k;

    return vec3(x)*(1.0/float(0xffffffffU));
}

hash:
Nowadays webGL2.0 is there so integers are available in (w)GLSL.
-> for quality portable hash (at similar cost than ugly float hashes) we can now use "serious" hashing techniques.
IQ implemented some in https://www.shadertoy.com/view/XlXcW4 (and more)

E.g.:

  const uint k = 1103515245U;  // GLIB C
//const uint k = 134775813U;   // Delphi and Turbo Pascal
//const uint k = 20170906U;    // Today's date (use three days ago's dateif you want a prime)
//const uint k = 1664525U;     // Numerical Recipes

vec3 hash( uvec3 x )
{
    x = ((x>>8U)^x.yzx)*k;
    x = ((x>>8U)^x.yzx)*k;
    x = ((x>>8U)^x.yzx)*k;

    return vec3(x)*(1.0/float(0xffffffffU));
}
笑饮青盏花 2024-10-09 15:57:25

刚刚发现这个版本的 GPU 3d 噪声,据称它是可用的最快的版本:

#ifndef __noise_hlsl_
#define __noise_hlsl_

// hash based 3d value noise
// function taken from https://www.shadertoy.com/view/XslGRr
// Created by inigo quilez - iq/2013
// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.

// ported from GLSL to HLSL

float hash( float n )
{
    return frac(sin(n)*43758.5453);
}

float noise( float3 x )
{
    // The noise function returns a value in the range -1.0f -> 1.0f

    float3 p = floor(x);
    float3 f = frac(x);

    f       = f*f*(3.0-2.0*f);
    float n = p.x + p.y*57.0 + 113.0*p.z;

    return lerp(lerp(lerp( hash(n+0.0), hash(n+1.0),f.x),
                   lerp( hash(n+57.0), hash(n+58.0),f.x),f.y),
               lerp(lerp( hash(n+113.0), hash(n+114.0),f.x),
                   lerp( hash(n+170.0), hash(n+171.0),f.x),f.y),f.z);
}

#endif

Just found this version of 3d noise for GPU, alledgedly it is the fastest one available:

#ifndef __noise_hlsl_
#define __noise_hlsl_

// hash based 3d value noise
// function taken from https://www.shadertoy.com/view/XslGRr
// Created by inigo quilez - iq/2013
// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.

// ported from GLSL to HLSL

float hash( float n )
{
    return frac(sin(n)*43758.5453);
}

float noise( float3 x )
{
    // The noise function returns a value in the range -1.0f -> 1.0f

    float3 p = floor(x);
    float3 f = frac(x);

    f       = f*f*(3.0-2.0*f);
    float n = p.x + p.y*57.0 + 113.0*p.z;

    return lerp(lerp(lerp( hash(n+0.0), hash(n+1.0),f.x),
                   lerp( hash(n+57.0), hash(n+58.0),f.x),f.y),
               lerp(lerp( hash(n+113.0), hash(n+114.0),f.x),
                   lerp( hash(n+170.0), hash(n+171.0),f.x),f.y),f.z);
}

#endif
双马尾 2024-10-09 15:57:25

1d Perlin 的直锯齿版本,本质上是随机低频振荡器之字形。

half  rn(float xx){         
    half x0=floor(xx);
    half x1=x0+1;
    half v0 = frac(sin (x0*.014686)*31718.927+x0);
    half v1 = frac(sin (x1*.014686)*31718.927+x1);          

    return (v0*(1-frac(xx))+v1*(frac(xx)))*2-1*sin(xx);
}

我还在 Shadertoy 所有者 inigo quilez perlin 教程网站和 voronoi 等网站上找到了 1-2-3-4d perlin 噪声,他有完整的快速实现和代码。

A straight, jagged version of 1d Perlin, essentially a random lfo zigzag.

half  rn(float xx){         
    half x0=floor(xx);
    half x1=x0+1;
    half v0 = frac(sin (x0*.014686)*31718.927+x0);
    half v1 = frac(sin (x1*.014686)*31718.927+x1);          

    return (v0*(1-frac(xx))+v1*(frac(xx)))*2-1*sin(xx);
}

I also have found 1-2-3-4d perlin noise on shadertoy owner inigo quilez perlin tutorial website, and voronoi and so forth, he has full fast implementations and codes for them.

一绘本一梦想 2024-10-09 15:57:25

我已将 Ken Perlin 的 Java 实现之一翻译为 GLSL,并在 ShaderToy 上的几个项目中使用了它。

下面是我所做的 GLSL 解释:

int b(int N, int B) { return N>>B & 1; }
int T[] = int[](0x15,0x38,0x32,0x2c,0x0d,0x13,0x07,0x2a);
int A[] = int[](0,0,0);

int b(int i, int j, int k, int B) { return T[b(i,B)<<2 | b(j,B)<<1 | b(k,B)]; }

int shuffle(int i, int j, int k) {
    return b(i,j,k,0) + b(j,k,i,1) + b(k,i,j,2) + b(i,j,k,3) +
        b(j,k,i,4) + b(k,i,j,5) + b(i,j,k,6) + b(j,k,i,7) ;
}

float K(int a, vec3 uvw, vec3 ijk)
{
    float s = float(A[0]+A[1]+A[2])/6.0;
    float x = uvw.x - float(A[0]) + s,
        y = uvw.y - float(A[1]) + s,
        z = uvw.z - float(A[2]) + s,
        t = 0.6 - x * x - y * y - z * z;
    int h = shuffle(int(ijk.x) + A[0], int(ijk.y) + A[1], int(ijk.z) + A[2]);
    A[a]++;
    if (t < 0.0)
        return 0.0;
    int b5 = h>>5 & 1, b4 = h>>4 & 1, b3 = h>>3 & 1, b2= h>>2 & 1, b = h & 3;
    float p = b==1?x:b==2?y:z, q = b==1?y:b==2?z:x, r = b==1?z:b==2?x:y;
    p = (b5==b3 ? -p : p); q = (b5==b4 ? -q : q); r = (b5!=(b4^b3) ? -r : r);
    t *= t;
    return 8.0 * t * t * (p + (b==0 ? q+r : b2==0 ? q : r));
}

float noise(float x, float y, float z)
{
    float s = (x + y + z) / 3.0;  
    vec3 ijk = vec3(int(floor(x+s)), int(floor(y+s)), int(floor(z+s)));
    s = float(ijk.x + ijk.y + ijk.z) / 6.0;
    vec3 uvw = vec3(x - float(ijk.x) + s, y - float(ijk.y) + s, z - float(ijk.z) + s);
    A[0] = A[1] = A[2] = 0;
    int hi = uvw.x >= uvw.z ? uvw.x >= uvw.y ? 0 : 1 : uvw.y >= uvw.z ? 1 : 2;
    int lo = uvw.x <  uvw.z ? uvw.x <  uvw.y ? 0 : 1 : uvw.y <  uvw.z ? 1 : 2;
    return K(hi, uvw, ijk) + K(3 - hi - lo, uvw, ijk) + K(lo, uvw, ijk) + K(0, uvw, ijk);
}

我从 Ken Perlin 的噪声硬件第 2 章的附录 B 中翻译了它,来源:

https://www.csee.umbc.edu/~olano/s2002c36/ch02.pdf

这是我在 Shader Toy 上使用发布的噪声函数制作的公共阴影:

https://www.shadertoy.com/view/3slXzM

我发现的其他一些很好的来源我研究期间的噪音主题包括:

https://thebookofshaders.com/11/

https://mzucker.github.io/html/perlin-noise-math-faq。 html

https://rmarcus.info/blog/2018 /03/04/perlin-noise.html

http:// flafla2.github.io/2014/08/09/perlinnoise.html

https://mrl.nyu.edu/~perlin/noise/" rel="nofollow noreferrer">https:// /mrl.nyu.edu/~perlin/noise/

https:// rmarcus.info/blog/assets/perlin/perlin_paper.pdf

https:/ /developer.nvidia.com/gpugems/GPUGems/gpugems_ch05.html

我强烈推荐这本着色器书,因为它不仅提供了关于噪声的精彩交互式解释,还提供了其他着色器概念。

编辑:

也许能够通过使用 GLSL 中提供的一些硬件加速函数来优化翻译后的代码。如果我最终这样做,我会更新这篇文章。

I have translated one of Ken Perlin's Java implementations into GLSL and used it in a couple projects on ShaderToy.

Below is the GLSL interpretation I did:

int b(int N, int B) { return N>>B & 1; }
int T[] = int[](0x15,0x38,0x32,0x2c,0x0d,0x13,0x07,0x2a);
int A[] = int[](0,0,0);

int b(int i, int j, int k, int B) { return T[b(i,B)<<2 | b(j,B)<<1 | b(k,B)]; }

int shuffle(int i, int j, int k) {
    return b(i,j,k,0) + b(j,k,i,1) + b(k,i,j,2) + b(i,j,k,3) +
        b(j,k,i,4) + b(k,i,j,5) + b(i,j,k,6) + b(j,k,i,7) ;
}

float K(int a, vec3 uvw, vec3 ijk)
{
    float s = float(A[0]+A[1]+A[2])/6.0;
    float x = uvw.x - float(A[0]) + s,
        y = uvw.y - float(A[1]) + s,
        z = uvw.z - float(A[2]) + s,
        t = 0.6 - x * x - y * y - z * z;
    int h = shuffle(int(ijk.x) + A[0], int(ijk.y) + A[1], int(ijk.z) + A[2]);
    A[a]++;
    if (t < 0.0)
        return 0.0;
    int b5 = h>>5 & 1, b4 = h>>4 & 1, b3 = h>>3 & 1, b2= h>>2 & 1, b = h & 3;
    float p = b==1?x:b==2?y:z, q = b==1?y:b==2?z:x, r = b==1?z:b==2?x:y;
    p = (b5==b3 ? -p : p); q = (b5==b4 ? -q : q); r = (b5!=(b4^b3) ? -r : r);
    t *= t;
    return 8.0 * t * t * (p + (b==0 ? q+r : b2==0 ? q : r));
}

float noise(float x, float y, float z)
{
    float s = (x + y + z) / 3.0;  
    vec3 ijk = vec3(int(floor(x+s)), int(floor(y+s)), int(floor(z+s)));
    s = float(ijk.x + ijk.y + ijk.z) / 6.0;
    vec3 uvw = vec3(x - float(ijk.x) + s, y - float(ijk.y) + s, z - float(ijk.z) + s);
    A[0] = A[1] = A[2] = 0;
    int hi = uvw.x >= uvw.z ? uvw.x >= uvw.y ? 0 : 1 : uvw.y >= uvw.z ? 1 : 2;
    int lo = uvw.x <  uvw.z ? uvw.x <  uvw.y ? 0 : 1 : uvw.y <  uvw.z ? 1 : 2;
    return K(hi, uvw, ijk) + K(3 - hi - lo, uvw, ijk) + K(lo, uvw, ijk) + K(0, uvw, ijk);
}

I translated it from Appendix B from Chapter 2 of Ken Perlin's Noise Hardware at this source:

https://www.csee.umbc.edu/~olano/s2002c36/ch02.pdf

Here is a public shade I did on Shader Toy that uses the posted noise function:

https://www.shadertoy.com/view/3slXzM

Some other good sources I found on the subject of noise during my research include:

https://thebookofshaders.com/11/

https://mzucker.github.io/html/perlin-noise-math-faq.html

https://rmarcus.info/blog/2018/03/04/perlin-noise.html

http://flafla2.github.io/2014/08/09/perlinnoise.html

https://mrl.nyu.edu/~perlin/noise/

https://rmarcus.info/blog/assets/perlin/perlin_paper.pdf

https://developer.nvidia.com/gpugems/GPUGems/gpugems_ch05.html

I highly recommend the book of shaders as it not only provides a great interactive explanation of noise, but other shader concepts as well.

EDIT:

Might be able to optimize the translated code by using some of the hardware-accelerated functions available in GLSL. Will update this post if I end up doing this.

留一抹残留的笑 2024-10-09 15:57:25

lygia,多语言着色器库

如果您不想将函数复制/粘贴到着色器中,您还可以使用 lygia,多语言着色器库。它包含一些生成函数,例如 GLSL 和 HLSL 中的 cnoise、fbm、noised、pnoise、random、snoise。还有许多其他很棒的功能。为此,请执行以下操作:

依赖于 #include “文件”,它由 Khronos GLSL 标准定义,并受到大多数引擎和环境的支持(例如 glslViewer、glsl-canvas VS Code 插件、Unity 等)。

示例:cnoise

使用 cnoise.glsl# include

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform float u_time;

#include "lygia/generative/cnoise.glsl"

void main (void) {
    vec2 st = gl_FragCoord.xy / u_resolution.xy;
    vec3 color = vec3(cnoise(vec3(st * 5.0, u_time)));

    gl_FragColor = vec4(color, 1.0);
}

为了运行这个示例,我使用了 glslViewer

lygia, a multi-language shader library

If you don't want to copy / paste the functions into your shader, you can also use lygia, a multi-language shader library. It contains a few generative functions like cnoise, fbm, noised, pnoise, random, snoise in both GLSL and HLSL. And many other awesome functions as well. For this to work it:

Relays on #include "file" which is defined by Khronos GLSL standard and suported by most engines and enviroments (like glslViewer, glsl-canvas VS Code pluging, Unity, etc. ).

Example: cnoise

Using cnoise.glsl with #include:

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform float u_time;

#include "lygia/generative/cnoise.glsl"

void main (void) {
    vec2 st = gl_FragCoord.xy / u_resolution.xy;
    vec3 color = vec3(cnoise(vec3(st * 5.0, u_time)));

    gl_FragColor = vec4(color, 1.0);
}

To run this example I used glslViewer.

神魇的王 2024-10-09 15:57:25

请参阅下面的示例,了解如何向渲染的纹理添加白噪声。
解决方案是使用两种纹理:原始纹理和纯白噪声,例如:wiki 白噪声

private static final String VERTEX_SHADER =
    "uniform mat4 uMVPMatrix;\n" +
    "uniform mat4 uMVMatrix;\n" +
    "uniform mat4 uSTMatrix;\n" +
    "attribute vec4 aPosition;\n" +
    "attribute vec4 aTextureCoord;\n" +
    "varying vec2 vTextureCoord;\n" +
    "varying vec4 vInCamPosition;\n" +
    "void main() {\n" +
    "    vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
    "    gl_Position = uMVPMatrix * aPosition;\n" +
    "}\n";

private static final String FRAGMENT_SHADER =
        "precision mediump float;\n" +
        "uniform sampler2D sTextureUnit;\n" +
        "uniform sampler2D sNoiseTextureUnit;\n" +
        "uniform float uNoseFactor;\n" +
        "varying vec2 vTextureCoord;\n" +
        "varying vec4 vInCamPosition;\n" +
        "void main() {\n" +
                "    gl_FragColor = texture2D(sTextureUnit, vTextureCoord);\n" +
                "    vec4 vRandChosenColor = texture2D(sNoiseTextureUnit, fract(vTextureCoord + uNoseFactor));\n" +
                "    gl_FragColor.r += (0.05 * vRandChosenColor.r);\n" +
                "    gl_FragColor.g += (0.05 * vRandChosenColor.g);\n" +
                "    gl_FragColor.b += (0.05 * vRandChosenColor.b);\n" +
        "}\n";

共享的片段包含参数 uNoiseFactor,该参数在主应用程序的每次渲染上更新:

float noiseValue = (float)(mRand.nextInt() % 1000)/1000;
int noiseFactorUniformHandle = GLES20.glGetUniformLocation( mProgram, "sNoiseTextureUnit");
GLES20.glUniform1f(noiseFactorUniformHandle, noiseFactor);

Please see below an example how to add white noise to the rendered texture.
The solution is to use two textures: original and pure white noise, like this one: wiki white noise

private static final String VERTEX_SHADER =
    "uniform mat4 uMVPMatrix;\n" +
    "uniform mat4 uMVMatrix;\n" +
    "uniform mat4 uSTMatrix;\n" +
    "attribute vec4 aPosition;\n" +
    "attribute vec4 aTextureCoord;\n" +
    "varying vec2 vTextureCoord;\n" +
    "varying vec4 vInCamPosition;\n" +
    "void main() {\n" +
    "    vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
    "    gl_Position = uMVPMatrix * aPosition;\n" +
    "}\n";

private static final String FRAGMENT_SHADER =
        "precision mediump float;\n" +
        "uniform sampler2D sTextureUnit;\n" +
        "uniform sampler2D sNoiseTextureUnit;\n" +
        "uniform float uNoseFactor;\n" +
        "varying vec2 vTextureCoord;\n" +
        "varying vec4 vInCamPosition;\n" +
        "void main() {\n" +
                "    gl_FragColor = texture2D(sTextureUnit, vTextureCoord);\n" +
                "    vec4 vRandChosenColor = texture2D(sNoiseTextureUnit, fract(vTextureCoord + uNoseFactor));\n" +
                "    gl_FragColor.r += (0.05 * vRandChosenColor.r);\n" +
                "    gl_FragColor.g += (0.05 * vRandChosenColor.g);\n" +
                "    gl_FragColor.b += (0.05 * vRandChosenColor.b);\n" +
        "}\n";

The fragment shared contains parameter uNoiseFactor which is updated on every rendering by main application:

float noiseValue = (float)(mRand.nextInt() % 1000)/1000;
int noiseFactorUniformHandle = GLES20.glGetUniformLocation( mProgram, "sNoiseTextureUnit");
GLES20.glUniform1f(noiseFactorUniformHandle, noiseFactor);
国粹 2024-10-09 15:57:25

FWIW 我有同样的问题,我需要它在 WebGL 1.0 中实现,所以我无法使用之前答案中给出的一些示例。我尝试了之前提到的Gold Noise,但是使用了PHI不太适合我。 (distance(xy * PHI, xy) * Seed 就等于 length(xy) * (1.0 - PHI) * Seed 所以我不明白 有什么魔力code>PHI 应该在直接乘以 seed 时发挥作用?

无论如何,我做了类似的事情,只是没有 PHI 而是在另一个中添加了一些变化地方,基本上我取 xy 和右上角框架之外的某个随机点之间距离的 tan,然后乘以 距离xy 和位于左下角的另一个这样的随机点之间(因此据我所知,这些点之间没有意外匹配)。单击以生成新帧。

(function main() {
  const dim = [512, 512];
  twgl.setDefaults({ attribPrefix: "a_" });
  const gl = twgl.getContext(document.querySelector("canvas"));
  gl.canvas.width = dim[0];
  gl.canvas.height = dim[1];
  const bfi = twgl.primitives.createXYQuadBufferInfo(gl);
  const pgi = twgl.createProgramInfo(gl, ["vs", "fs"]);
  gl.canvas.onclick = (() => {
    twgl.bindFramebufferInfo(gl, null);
    gl.useProgram(pgi.program);
    twgl.setUniforms(pgi, {
      u_resolution: dim,
      u_seed: Array(4).fill().map(Math.random)
    });
    twgl.setBuffersAndAttributes(gl, pgi, bfi);
    twgl.drawBufferInfo(gl, bfi);
  });
})();
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<script id="vs" type="x-shader/x-vertex">
  attribute vec4 a_position;
  attribute vec2 a_texcoord;

  void main() {
    gl_Position = a_position;
  }
</script>
<script id="fs" type="x-shader/x-fragment">
  precision highp float;

  uniform vec2 u_resolution;
  uniform vec2 u_seed[2];
  
  void main() {
    float uni = fract(
      tan(distance(
        gl_FragCoord.xy,
        u_resolution * (u_seed[0] + 1.0)
      )) * distance(
        gl_FragCoord.xy,
        u_resolution * (u_seed[1] - 2.0)
      )
    );
    gl_FragColor = vec4(uni, uni, uni, 1.0);
  }
</script>
<canvas></canvas>

FWIW I had the same questions and I needed it to be implemented in WebGL 1.0, so I couldn't use a few of the examples given in previous answers. I tried the Gold Noise mentioned before, but the use of PHI doesn't really click for me. (distance(xy * PHI, xy) * seed just equals length(xy) * (1.0 - PHI) * seed so I don't see how the magic of PHI should be put to work when it gets directly multiplied by seed?

Anyway, I did something similar just without PHI and instead added some variation at another place, basically I take the tan of the distance between xy and some random point lying outside of the frame to the top right and then multiply with the distance between xy and another such random point lying in the bottom left (so there is no accidental match between these points). Looks pretty decent as far as I can see. Click to generate new frames.

(function main() {
  const dim = [512, 512];
  twgl.setDefaults({ attribPrefix: "a_" });
  const gl = twgl.getContext(document.querySelector("canvas"));
  gl.canvas.width = dim[0];
  gl.canvas.height = dim[1];
  const bfi = twgl.primitives.createXYQuadBufferInfo(gl);
  const pgi = twgl.createProgramInfo(gl, ["vs", "fs"]);
  gl.canvas.onclick = (() => {
    twgl.bindFramebufferInfo(gl, null);
    gl.useProgram(pgi.program);
    twgl.setUniforms(pgi, {
      u_resolution: dim,
      u_seed: Array(4).fill().map(Math.random)
    });
    twgl.setBuffersAndAttributes(gl, pgi, bfi);
    twgl.drawBufferInfo(gl, bfi);
  });
})();
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<script id="vs" type="x-shader/x-vertex">
  attribute vec4 a_position;
  attribute vec2 a_texcoord;

  void main() {
    gl_Position = a_position;
  }
</script>
<script id="fs" type="x-shader/x-fragment">
  precision highp float;

  uniform vec2 u_resolution;
  uniform vec2 u_seed[2];
  
  void main() {
    float uni = fract(
      tan(distance(
        gl_FragCoord.xy,
        u_resolution * (u_seed[0] + 1.0)
      )) * distance(
        gl_FragCoord.xy,
        u_resolution * (u_seed[1] - 2.0)
      )
    );
    gl_FragColor = vec4(uni, uni, uni, 1.0);
  }
</script>
<canvas></canvas>

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文