由于GPU驱动程序供应商通常不打算在GLSL中实现noiseX
,我正在寻找“图形随机化瑞士军刀”效用函数集,最好是优化到在GPU着色器中使用。我更喜欢GLSL,但任何语言的代码都适合我,我可以将它自己翻译成GLSL。
具体来说,我希望:
a)伪随机函数 - 在[-1,1]或[0,1]上的N维均匀分布,从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 Noise - 再次,N维,+ - 均匀分布,具有受约束的值集,并且看起来很好(一些选项可配置外观,如Perlin级别也可能有用)。我希望签名如下:
float noise (T coord, TT seed);
vec2 noise2 (T coord, TT seed);
// ...
我对随机数生成理论的了解不多,所以我最急切地想要一个预先制定的解决方案,但我也很欣赏像这样的答案“这里是一个非常好,高效的1D rand(),让我解释一下如何在它上面制作一个好的N维兰德()......“。
答案 0 :(得分:241)
对于非常简单的伪随机内容,我使用的是我在互联网上找到的oneliner:
float rand(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
您还可以使用您喜欢的任何PRNG生成噪点纹理,然后以正常方式上传并在着色器中对值进行采样;如果您愿意,我可以稍后挖掘代码示例。
另外,请参阅Stefan Gustavson的this file以了解Perlin和Simplex噪声的GLSL实现。
答案 1 :(得分:69)
Gustavson的实现使用了1D纹理
不,不是,自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
来源找到。)
答案 2 :(得分:65)
我发现你可以使用一个简单的整数哈希函数并将结果插入到float的尾数中。 IIRC GLSL规范保证32位无符号整数和IEEE binary32浮点表示,因此它应该是完全可移植的。
我刚试了这个。结果非常好:它看起来与我尝试的每个输入都是静态的,根本没有可见的模式。相比之下,流行的sin / fract片段在我的GPU上具有相当明显的对角线,给出相同的输入。
一个缺点是它需要GLSL v3.30。尽管看起来足够快,但我没有凭经验量化其性能。 AMD的Shader Analyzer声称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 );
}
截图:
我在图像编辑程序中检查了屏幕截图。有256种颜色,平均值为127,这意味着分布是均匀的并且覆盖了预期的范围。
答案 3 :(得分:19)
黄金噪音
// Gold Noise ©2015 dcerisano@standard3d.com
// - based on the Golden Ratio, PI and Square Root of Two
// - superior distribution
// - fastest noise generator function
// - works with all chipsets (including low precision)
float PHI = 1.61803398874989484820459 * 00000.1; // Golden Ratio
float PI = 3.14159265358979323846264 * 00000.1; // PI
float SQ2 = 1.41421356237309504880169 * 10000.0; // Square Root of Two
float gold_noise(in vec2 coordinate, in float seed){
return fract(tan(distance(coordinate*(seed+PHI), vec2(PHI, PI)))*SQ2);
}
See Gold Noise in your browser right now!
此功能改进了@ appas&#39;中当前函数的随机分布。截至2017年9月9日的回答:
@appas函数也是不完整的,因为没有提供种子(uv不是种子 - 每帧都相同),并且不适用于低精度芯片组。默认情况下,Gold Noise以低精度运行(更快)。
答案 4 :(得分:11)
McEwan和@StefanGustavson描述的here也有一个很好的实现,它看起来像Perlin噪音,但“不需要任何设置,即不需要纹理也不需要统一数组。只需将它添加到着色器源代码中即可把它叫到任何你想要的地方“。
这非常方便,特别是考虑到Gustavson早期的实现,@ dep链接到,使用一维纹理,not supported in GLSL ES(WebGL的着色器语言)。
答案 5 :(得分:3)
请使用此:
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);
}
您可以在Improvements to the canonical one-liner GLSL rand() for OpenGL ES 2.0
中找到说明。答案 6 :(得分:2)
刚刚为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
答案 7 :(得分:2)
在 2010 年首次发布此问题后,在良好的随机函数和对它们的硬件支持方面发生了很大变化。
从今天的角度来看已接受的答案,该算法在从中抽取的随机数的一致性方面非常糟糕。并且根据输入值的大小,均匀性会受到很大影响,并且当从中采样时,可见的伪影/模式将变得明显,例如光线/路径追踪应用。
为此任务设计了许多不同的函数(其中大多数是整数散列),用于不同的输入和输出维度,其中大部分正在 2020 JCGT 论文 Hash Functions for GPU Rendering 中进行评估。根据您的需要,您可以从该论文中建议的函数列表中选择一个函数,只需 from the accompanying Shadertoy。 One that isn't covered 在这篇论文中,但这对我非常有用,没有任何在任何输入幅度值上的明显模式也是我想强调的。
其他类别的算法使用低差异序列来提取伪随机数,例如带有 Owen-Nayar 加扰的 Sobol 序列。 Eric Heitz 在这方面做了一些了不起的研究,以及他的 A Low-Discrepancy Sampler that Distributes Monte Carlo Errors as a Blue Noise in Screen Space 论文。 另一个例子是(迄今为止最新的)JCGT 论文 Practical Hash-based Owen Scrambling,它将 Owen 置乱应用于不同的哈希函数(即 Laine-Karras)。
还有其他类使用算法来产生具有理想频谱的噪声模式,例如蓝噪声,这对眼睛来说特别“令人愉悦”。
(我意识到好的 StackOverflow 答案应该将算法作为源代码而不是作为链接提供,因为它们可能会损坏,但是现在有太多不同的算法,我打算让这个答案成为当今已知优秀算法的总结)
答案 8 :(得分:1)
1d Perlin的直的锯齿状版本,基本上是随机的lfo之字形。
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教程网站上发现了1-2-3-4d perlin噪音,而voronoi等等,他有完整的快速实现和代码。
答案 9 :(得分:0)
请参阅下面的示例,了解如何在渲染纹理中添加白噪声。 解决方案是使用两种纹理:原始和纯白噪声,如下所示: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";
片段shared包含参数uNoiseFactor,它在主应用程序的每次渲染时都会更新:
float noiseValue = (float)(mRand.nextInt() % 1000)/1000;
int noiseFactorUniformHandle = GLES20.glGetUniformLocation( mProgram, "sNoiseTextureUnit");
GLES20.glUniform1f(noiseFactorUniformHandle, noiseFactor);
答案 10 :(得分:0)
哈希: 如今,存在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));
}
答案 11 :(得分:0)
我已经将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/
https://rmarcus.info/blog/assets/perlin/perlin_paper.pdf
https://developer.nvidia.com/gpugems/GPUGems/gpugems_ch05.html
我强烈推荐着色器书,因为它不仅提供了很好的交互式噪点解释,而且还提供了其他着色器概念。
编辑:
通过使用GLSL中提供的一些硬件加速功能,可能能够优化翻译后的代码。如果最终执行此操作,将会更新此帖子。
答案 12 :(得分:0)
如果您不想将函数复制/粘贴到您的着色器中,您还可以使用多语言着色器库 lygia。它包含一些生成函数,如 GLSL 和 HLSL 中的 cnoise、fbm、noised、pnoise、random、snoise。还有许多其他很棒的功能。为此,它可以工作:
<块引用>基于 Khronos GLSL 标准定义并由大多数引擎和环境(如 glslViewer、glsl-canvas VS Code 插件、Unity 等)支持的 #include “文件”的中继。
将 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。