GLSLのランダム/ノイズ関数
GPUドライバーベンダーは通常GLSLにnoiseX
を実装することを気にしないので、 "グラフィックスランダム化スイスアーミーナイフ"ユーティリティ関数セットを探しています。 、GPUシェーダー内での使用に最適化されていることが望ましい。私はGLSLを好みますが、どの言語でも私に代わってコーディングします。自分でGLSLに翻訳しても大丈夫です。
具体的には、私は期待しています:
a)擬似ランダム関数-M次元シードから計算された[-1,1]または[0,1]上のN次元の均一分布(理想的には任意の値ですが、I ' 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()を作成する方法を説明させてください...」。
非常に単純な擬似ランダムに見えるもののために、私はインターネットのどこかで見つけたこのワンライナーを使用します:
float Rand(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
好きなPRNGを使用してノイズテクスチャを生成し、これを通常の方法でアップロードし、シェーダーで値をサンプリングすることもできます。必要に応じて、後でコードサンプルを調べることができます。
また、Stefan GustavsonによるPerlinおよびSimplexノイズのGLSL実装の このファイル も確認してください。
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
のソースにあります)。
単純な整数ハッシュ関数を使用して、結果をfloatの仮数に挿入できることがわかりました。 IIRCは、GLSL仕様で32ビット符号なし整数とIEEE binary32 float表現を保証しているため、完全に移植可能です。
今これを試してみました。結果は非常に良好です。入力したすべての入力で静的に見え、目に見えるパターンはまったくありません。対照的に、人気のあるsin/fractスニペットは、同じ入力が与えられた場合、GPU上でかなりはっきりした対角線を持っています。
1つの欠点は、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です。これは、分布が均一で、予想される範囲をカバーしていることを意味します。
ゴールドノイズ
// 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);
}
この関数は、2017年9月9日の@appasの回答の現在の関数よりもランダム分布を改善しました。
@appas関数もシードが提供されていないため不完全で(uvはシードではない-すべてのフレームで同じ)、低精度のチップセットでは機能しません。ゴールドノイズはデフォルトで低精度で実行されます(はるかに高速です)。
また、McEwanと@StefanGustavsonによる here で記述されたニースの実装もあります。これは、Perlinノイズのように見えますが、「セットアップも必要ありません。つまり、テクスチャや均一な配列ではありません。好きな場所で呼び出してください」。
特に、@ depがリンクされたGustavsonの以前の実装では、 GLSL ESではサポートされていません (WebGLのシェーダー言語)である1Dテクスチャを使用しているため、これは非常に便利です。
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
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サイトや、voronoiなどで1-2-3-4dのperlinノイズを見つけました。彼は完全に高速な実装とコードを持っています。
レンダリングされたテクスチャにホワイトノイズを追加する方法の例を以下で参照してください。解決策は、次のようなオリジナルと純粋なホワイトノイズの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);
hash:最近では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));
}
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's 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で利用可能なハードウェアアクセラレーション機能の一部を使用して、翻訳されたコードを最適化できる場合があります。これを行うことになった場合、この投稿を更新します。
これを使用してください:
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()の改善 にあります。