208

GPUドライバーベンダーは通常GLSLに実装する必要がないため、GPUシェーダー内で使用するように最適化された「グラフィックランダム化スイスアーミーナイフ」ユーティリティ関数セットnoiseXを探しています。私は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()を作成する方法..."

4

14 に答える 14

298

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

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

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

また、Stefan GustavsonによるPerlinおよびSimplexノイズのGLSL実装については、このファイルを確認してください。

于 2010-11-25T09:12:38.677 に答える
104

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

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

1つの欠点は、GLSLv3.30が必要なことです。そして、それは十分に速いように見えますが、私はそのパフォーマンスを経験的に定量化していません。AMDのShaderAnalyzerは、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 );
}

スクリーンショット:

static.fragのrandom(vec3)の出力

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

于 2013-07-04T23:40:13.150 に答える
77

Gustavsonの実装は1Dテクスチャを使用します

いいえ、そうではありません。2005年以降ではありません。人々が古いバージョンのダウンロードを主張しているだけです。指定したリンクにあるバージョンは、8ビットの2Dテクスチャのみを使用します。

AshimaのIanMcEwanと私による新しいバージョンは、テクスチャを使用していませんが、テクスチャ帯域幅が広い一般的なデスクトッププラットフォームでは約半分の速度で実行されます。モバイルプラットフォームでは、テクスチャリングが重大なボトルネックになることが多いため、テクスチャレスバージョンの方が高速な場合があります。

積極的に維持されているソースリポジトリは次のとおりです。

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

テクスチャレスバージョンとテクスチャ使用バージョンの両方のノイズのコレクションがここにあります(2Dテクスチャのみを使用):

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

ご不明な点がございましたら、お気軽に直接メールでお問い合わせください(私のメールアドレスはclassicnoise*.glsl出典に記載されています)。

于 2012-03-22T13:55:57.790 に答える
42

ゴールドノイズ

// Gold Noise ©2015 dcerisano@standard3d.com
// - based on the Golden Ratio
// - uniform normalized distribution
// - fastest static noise generator function (also runs at low precision)
// - use with indicated 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);
}

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

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

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

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

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

于 2015-01-22T17:36:42.237 に答える
13

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

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

于 2011-06-07T22:33:49.683 に答える
6

これを使用してください:

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 ES2.0の正規のワンライナーGLSLrand()の改善にあります。

于 2019-08-07T08:56:32.913 に答える
5

ハッシュ:現在、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));
}
于 2018-09-06T15:28:03.960 に答える
5

2010年にこの質問が最初に投稿された後、優れたランダム関数とそれらのハードウェアサポートの領域で多くの変更が行われました。

今日の観点から受け入れられた答えを見ると、このアルゴリズムは、そこから引き出される乱数の均一性が非常に悪いです。また、入力値の大きさに応じて均一性が大幅に低下し、光線/パストレーシングアプリケーションなどでサンプリングすると、目に見えるアーティファクト/パターンが明らかになります。

このタスクのために、さまざまな入力および出力の次元に対して多くの異なる関数(ほとんどが整数ハッシュ)が考案されており、そのほとんどは2020JCGTの論文「GPUレンダリング用のハッシュ関数」で評価されています。ニーズに応じて、そのペーパーで提案されている関数のリストから、および付属のShadertoyから関数を選択できます。 このホワイトペーパーでは取り上げていませんが、入力マグニチュード値に目立ったパターンがなく、非常に役立っているものも強調したいと思います。

他のクラスのアルゴリズムは、Owen-Nayarスクランブリングを使用したSobolシーケンスなど、低差異シーケンスを使用して疑似乱数を抽出します。Eric Heitzは、この分野でいくつかの驚くべき研究を行いました。また、画面スペースの紙にモンテカルロエラーをブルーノイズとして分散する彼のALow-DiscrepancySamplerを使用しました。この別の例は、(これまでのところ)JCGTペーパーの実用的なハッシュベースのオーウェンスクランブリングです。これは、オーウェンスクランブリングを別のハッシュ関数(つまり、レインカラス)に適用します。

さらに他のクラスは、ブルーノイズなど、目に特に「心地よい」望ましい周波数スペクトルを持つノイズパターンを生成するアルゴリズムを使用します。

( StackOverflowの優れた回答は、リンクとしてではなくソースコードとしてアルゴリズムを提供する必要があることを認識しています。アルゴリズムが壊れることがあるためですが、今日ではさまざまなアルゴリズムが多すぎるため、この回答を今日の既知の優れたアルゴリズムの要約にするつもりです)

于 2021-01-04T13:50:41.903 に答える
2

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パーリンノイズを見つけました。彼は、それらの完全に高速な実装とコードを持っています。

于 2013-09-25T11:40:58.603 に答える
2

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

このソースで、KenPerlinのノイズハードウェアの第2章の付録Bから翻訳しました。

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

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

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

于 2019-02-25T21:20:55.443 に答える
1

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
于 2014-02-06T13:49:31.070 に答える
1

lygia、多言語シェーダーライブラリ

関数をコピーしてシェーダーに貼り付けたくない場合は、多言語シェーダーライブラリであるlygiaを使用することもできます。これには、GLSLとHLSLの両方で、cnoise、fbm、noised、pnoise、random、snoiseなどのいくつかの生成関数が含まれています。そして、他の多くの素晴らしい機能も同様です。これが機能するためには:

Khronos GLSL標準で定義され、ほとんどのエンジンと環境(glslViewer、glsl-canvas VS Codeプラグ、Unityなど)でサポートされている#include"file"のリレー。

例: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を使用しました。

于 2021-06-04T09:52:14.407 に答える
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);
于 2018-01-03T15:13:11.427 に答える
0

FWIW同じ質問があり、WebGL 1.0で実装する必要があったため、以前の回答で示した例のいくつかを使用できませんでした。前述のゴールドノイズを試しましたが、使用PHIしてもあまり効果がありません。 (distance(xy * PHI, xy) * seedちょうど等しいので、直接乗算されたときにlength(xy) * (1.0 - PHI) * seedの魔法がどのように機能するのかわかりませんか?PHIseed

とにかく、私はまったく同じようなことをしましたがPHI、代わりに別の場所でいくつかのバリエーションを追加しました。基本的には、右上にあるフレームの外側にあるランダムな点とtanの間の距離を取り、その間にある別のそのようなランダムな点と乗算します左下にあります(したがって、これらのポイント間で偶発的な一致はありません)。私が見る限り、かなりまともなように見えます。クリックして新しいフレームを生成します。xydistancexy

(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>

于 2021-09-20T18:52:28.500 に答える