GLSLのランダム/ノイズ関数


179

GPUドライバーベンダーは通常noiseXGLSLでの実装を気にしないため、GPUシェーダー内での使用に最適化された「グラフィックスランダム化スイスアーミーナイフ」ユーティリティ関数セットを探しています。私はGLSLを好みますが、どの言語でもコードを作成できます。自分でそれをGLSLに翻訳してもかまいません。

具体的には、私は期待します:

a)擬似ランダム関数 -M次元のシードから計算された、[-1,1]または[0,1]を超えるN次元の均一な分布(理想的には任意の値ですが、シードを抑制しても問題ありません)たとえば、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()を作るために...」

回答:


263

非常に単純な疑似ランダムに見えるものについては、インターネットのどこかで見つけた次のワンライナーを使用します。

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

また、好きなPRNGを使用してノイズテクスチャを生成し、これを通常の方法でアップロードして、シェーダーで値をサンプリングすることもできます。必要に応じて、後でコードサンプルを掘り下げることができます。

また、Stefan GustavsonによるPerlinとシンプレックスノイズのGLSL実装については、このファイルを確認してください。


14
どのように使用しvec2 coますか?それは範囲ですか?シード?
ロス

12
このアルゴリズムを使用した低精度の浮動小数点フラグメントシェーダー(S3のARM Maliなど)に注意してください:stackoverflow.com/questions/11293628/…github.com/ashima/webgl-noiseプロジェクトは、問題をlowp持っていないようです。
PT

4
FWIW、ここで説明する機能については、ここで詳しく説明します
Loomchild 2013

3
参考までに:その関数の分布は恐ろしいです。
Tara

3
私はGLSLの初心者ですco.xyが、なぜが使用されるのか、誰かが説明することはできcoますか?
kelin

83

単純な整数ハッシュ関数を使用して、結果を浮動小数点数の仮数に挿入できることに気づきました。IIRC GLSL仕様では、32ビットの符号なし整数とIEEEのbinary32 float表現が保証されているため、完全に移植可能です。

今試してみました。結果は非常に良好です。私が試したすべての入力で静的なように見え、目に見えるパターンはまったくありません。対照的に、人気のあるsin / fractスニペットは、同じ入力を与えられたGPUでかなりはっきりとした対角線を持っています。

1つの欠点は、GLSL v3.30が必要なことです。そして、それは十分に速いように見えますが、そのパフォーマンスを経験的に定量化していません。AMDのシェーダーアナライザーは、HD5870のvec2バージョンでクロックあたり13.33ピクセルを主張しています。sin / fractスニペットの1クロックあたり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 );
}

スクリーンショット:

static.fragでのrandom(vec3)の出力

スクリーンショットを画像編集プログラムで調べました。256色あり、平均値は127です。つまり、分布は均一であり、期待される範囲をカバーしています。


17
+1は良いアイデアと実装です。256色あり、平均値が127であるため、分布は(厳密な意味で)均一でなければならないという主張に疑問を投げかけます。一律かもしれませんが、まだわかりません。たとえば、ベルカーブの分布の平均と色の数は同じでも、均一ではありません。
LarsH 2014

@LarsHから与えられた理由により、これに反対票を投じました。
Autumnsault 2014

まあ、それ均一性を必要としないほとんどのアプリケーションに十分です。:-)
itmuckel 2016

5
ヒストグラムの認識からすると、非常に均一に見えます。均一性を必要とするほとんどのアプリケーションにも十分対応できます。(他よりも生成が少ないように見える唯一の値は0と255です)
leviathanbadger

ありがとう。私の確率はさびています。GCN命令セットを確認すると、新しいハードウェアでは命令セットでビットフィールド演算を直接サポートしているため、これは非常に高速です。古いハードウェアで実行したテスト。
空間的な

73

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ソースに記載されています)。


4
はい、私が参照している実装、@ depがリンクしているdavidcornette.comのコードは1Dテクスチャを使用しています:glBindTexture(GL_TEXTURE_1D, *texID);など。私の回答から引用したため、「提供したリンク」が何を意味するのかは明確ではありません。しかし、その答えはあなたの実装にリンクしていませんでした。回答を更新して、私が何を参照しているかを明確にし、あなたが提供した新しい情報を反映させます。人々を古いバージョンのダウンロードを「主張する」と特徴付けることは、あなたが信用しない歪みです。
LarsH

1
PSあなたはDavid Cornette(彼はdavidcornette.comに連絡先情報を持っています)に書いて、ソースリポジトリにリンクするようにdavidcornette.com/glsl/links.htmlで彼のリンクを変更するように依頼することができます。私も彼にメールします。
LarsH 2012年

1
PPS明確にできますか?どのバージョンが8ビット2Dテクスチャのみを使用していますか?特定のプラットフォームにとっては良いオプションのように
思え

31

ゴールドノイズ

// Gold Noise ©2015 dcerisano@standard3d.com
// - based on the Golden Ratio
// - uniform normalized distribution
// - fastest static noise generator function (also runs at low precision)

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);
}

今すぐブラウザでゴールドノイズをご覧ください!

ここに画像の説明を入力してください

この関数は、2017年9月9日の時点で、@ appasの回答の現在の関数に対してランダム分布を改善しています。

ここに画像の説明を入力してください

@appas関数も不完全で、シードが提供されない(uvはシードではない-すべてのフレームで同じ)ため、低精度のチップセットでは機能しません。ゴールドノイズはデフォルトで低精度で実行されます(はるかに高速)。


これを投稿してくれてありがとう。ブラウザーで実行できるように、shadertoy.comなどで実行可能なバージョンを投稿することを検討しますか?
LarsH 2015年

@snb Shadertoy.comは今月メンテナンス中です。しばらくお待ちください。また、そこにあるコードに不合理なシード値の要件を明確に文書化しました。ゴールドノイズはスカラーを返すため、それを使用してベクトルを構築することは簡単であり、コードにも記載されています。
Dominic Cerisano 2018年

7
これは他のノイズ関数と何ら変わらないと思います。これに特別な特性があることの証明は何ですか。あなたが不合理な数の束を使用するからといってそれを特別にすることはありません。
M.kazem Akhgary

2
@Dominic:「同様の機能よりも優れた分布」:これは証明する必要があります。tan()は本当に悪条件です。すべてのfract(non-linear * big)は重要度の低いビットに基づいているため、pi / 2に近いtan()とゼロに近いsqrt()の両方が異なるハードウェアで異なる結果を生成する可能性が非常に高くなります。入力値が小さいか大きい場合も影響があります。また、ビットダイナミクスは、場所によっても大きく異なります。
Fabrice NEYRET、2018

2
注意:現在のGLSLには整数があるため、同様のパフォーマンスで品質分布(およびダイナミクス)が必要な場合に、「深刻な」intベースのハッシュジェネレーターを使用しない理由はありません。(非常にローエンドのデバイスを除く)。
Fabrice NEYRET、2018

12

McEwanと@StefanGustavsonによってここで説明されている、Perlinノイズのように見える素晴らしい実装もありますが、「設定も必要ありません。つまり、テクスチャや均一な配列は必要ありません。シェーダーのソースコードに追加して、好きな場所で呼び出すだけです」。

これは、@ depがリンクされたGustavsonの以前の実装がGLSL ES(WebGLのシェーダー言語)でサポートされていない 1Dテクスチャを使用する場合に特に便利です。


1
これがOPのb)ノイズタイプの要求に対する最良の回答です。ここに直接リンクgithub.com/ashima/webgl-noiseがあります。GLSL 120コードとして用意されている2D、3D、4Dバージョンがあります。
user362515

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);
}

その説明は、OpenGL ES 2.0の正規の1行GLSL rand()の改善にあります。


記事を読み飛ばしましたが、まだわかりません。3.14 modはpiの近似値ですか?
Kaan E.

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

1
ゴールドノイズ(上記)は明らかに高速です。これは、操作がはるかに少なく、ハッシュを1つだけ実行するためです。これは、ハッシュ関数を8回呼び出し、ネストされた線形補間(lerps)を実行します。また、これは特に低精度での分布が劣ります。
ドミニクセリザーノ2018

1
良い点は、これはInigo QuilezによるShadertohからのパーリンノイズタイプのグラフです。ニースのコードドミニクの病気チェックl8r
com.prehensible 2018

@Fabrice OPの質問、私の回答、コード、またはコメントを理解していないようです。ゴールドノイズはOPの定義によって連続しています。UVとシードを受け入れ、シェーダーを提供することでそれを証明します。コメントについてのすべてが間違っています。ハッシュ関数を疑似ランダムノイズ関数と混同し続けます。彼らは同じではありません。ノイズ関数は、ハッシュ関数(実際のハッシュの全体のポイント)のような一意の識別子を生成する必要はありません。
ドミニクセリザーノ2018

Dominicを読んでください。理解していないと思われる用語について申し立てを行う前に、そうではありません。これらの用語は完全に正確で、文献で明確に定義されているだけでなく、私はフィールドで働いています。また、OPは、彼が後に示した例によって用語を理解したことを証明しています。ヒント:「継続的」+「ノイズ」+「パーリンのような」。en.wikipedia.org/wiki/Perlin_noise
Fabrice NEYRET 2018

連続は、ループ句を追加する場合にのみ、多くのノイズ関数がループし、特にグラフィックスのビット丸めのために、特定の方法の後に劣化します。皆さんとのコミュニケーションが途絶えているので、重要な研究に時間をかけてください。
com.prehensible 2018

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のチュートリアルWebサイト、およびボロノイなどで1-2-3-4dパーリンノイズが見つかりました。彼はそれらの完全な高速実装とコードを持っています。


1

ハッシュ:現在、webGL2.0が存在するため、(w)GLSLで整数を使用できます。->質の高いポータブルハッシュ(醜いfloatハッシュと同様のコスト)のために、「深刻な」ハッシュ技術を使用できるようになりました。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));
}

0

レンダリングされたテクスチャにホワイトノイズを追加する方法の例を以下に示します。解決策は、2つのテクスチャを使用することです。次のようなオリジナルと純粋なホワイトノイズ: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);

0

Ken PerlinのJava実装の1つを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のNoise Hardwareの第2章の付録Bから翻訳しました。

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

これは、投稿されたノイズ関数を使用するシェーダートイで行ったパブリックシェードです。

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で利用可能なハードウェアアクセラレーション機能の一部を使用して、変換されたコードを最適化できる場合があります。私がこれをやった場合、この投稿を更新します。


また、Perlin / Simplex Noiseが疑似ランダムであることは確かです。私が覚えていることから、興味深いことは、ノイズをさまざまなレベルで重ねて「ズーム」して、非常にシームレスに見えるようにすることができるということです。それについて私を引用しないでください。
Andrew Meservy

@Zibri残念ながら、私はCや.shの直接のコマンドにはあまり詳しくありません。ただし、関数は単なる疑似乱数ジェネレータであり、ノイズ関数ではないようです。また、glslピクセルシェーダーはgpuで直接実行されることに注意してください。あなたはC.で利用できる場合がありますそれらの余分ライブラリやCPU能力のいずれかにアクセスすることはできません
アンドリューMeservy

Book Of Shadersは、グリッドを歪め、ポイントごとの必要な計算が少ないため、シンプレックスノイズがPerlinノイズのより効率的なバージョンであることを説明しています。間違いなく読む価値があります。
Andrew Meservy

フラクタルブラウン運動とボロノイズの章もご覧ください
Andrew Meservy

Andrew Meservy:ライブラリは必要ありません...私のノイズ関数は非常にシンプルです:2 64ビット整数は状態x(n)とx(n-1)です。シンプルで高速な式はx(n + 1)= ROTR( x(n)+ x(n-1)、8)。私のgitを複製して実行すると、動作していることがわかります。
Zibri、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.