由于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)连续的噪声,比如柏林噪声——同样是n维,+-均匀分布,有约束的值集,看起来不错(一些配置外观的选项,比如柏林电平也很有用)。我希望签名是这样的:

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

我对随机数生成理论不太感兴趣,所以我最渴望的是一个现成的解决方案,但我也很喜欢这样的回答:“这里有一个非常好的,高效的1D rand(),让我解释一下如何在它的基础上制作一个好的n维rand()……”.


当前回答

我也有同样的问题,我需要在WebGL 1.0中实现它,所以我不能使用之前回答中给出的一些例子。我尝试了前面提到的Gold Noise,但是PHI的使用并不适合我。(距离(xy * PHI, xy) *种子就等于长度(xy) * (1.0 - PHI) *种子,所以我不明白当它直接乘以种子时,PHI的魔力应该如何发挥作用?

不管怎样,我做了类似的事情,只是没有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>

其他回答

Lygia,一个多语言着色器库

如果你不想复制/粘贴函数到你的着色器,你也可以使用lygia,一个多语言着色器库。它在GLSL和HLSL中都包含了一些生成函数,如cnoise, fbm, noised, pnoise, random, snoise。还有很多其他很棒的功能。要做到这一点:

#上的接力包括“文件”,由Khronos GLSL标准定义,并被大多数引擎和环境(如glslViewer, GLSL -canvas VS Code插件,Unity等)所支持。

例如:cnoise

使用cnoise。GLSL与#包括:

#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。

对于非常简单的伪随机的东西,我使用这个在线程序,我在互联网上找到的地方:

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

你也可以使用任何你喜欢的PRNG生成一个噪音纹理,然后以正常的方式上传这个,并在你的着色器中采样值;如果你愿意,我可以稍后再找一个代码样本。

另外,查看Stefan Gustavson的Perlin和Simplex噪声的GLSL实现文件。

我也有同样的问题,我需要在WebGL 1.0中实现它,所以我不能使用之前回答中给出的一些例子。我尝试了前面提到的Gold Noise,但是PHI的使用并不适合我。(距离(xy * PHI, xy) *种子就等于长度(xy) * (1.0 - PHI) *种子,所以我不明白当它直接乘以种子时,PHI的魔力应该如何发挥作用?

不管怎样,我做了类似的事情,只是没有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>

这里还有一个由McEwan和@StefanGustavson描述的很好的实现,看起来像柏林噪声,但“不需要任何设置,即不需要纹理或统一数组。只需将它添加到你的着色器源代码,并在任何你想要的地方调用它”。

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

刚找到这个版本的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