我的专栏目录:
小IVan:专题概述及目录程序化纹理有着很多好处,第一可以方便修改,当我们的生成逻辑写好后,想要调整的话就是调调参数的事情。第二就是思路清晰,这张图是怎么来的,逻辑非常清晰,而不是传统的图像绘制,画到哪儿想到哪儿,思维是游走的,离散的。第三我们可以实时生成,可以让纹理和玩家产生互动等等。
现在主流的是用Substance来生成,但是其实引擎也能做,引擎本来就是用来操作这些素材的地方。想看引擎和Substance的对接的话可能你要失望了,对于目前的Substance而言,我个人极力反对目前版本的Substance和引擎直接结合。本章将在引擎里实现各种程序化纹理,不管是离线烘焙也好,还是实时生成也罢。
程序化纹理篇的大概规划是,先把程序化生成需要的节点我们自己全部开发一次,然后把这些工具节点撸好以后在开始用这些工具做纯程序化的纹理(在引擎中)。我们的核心目的是把材质编辑器拓展成引擎内置的“Substance”,像Substance一样能有几何生成,图像转换等功能。当然拓展材质编辑器有很多种办法,不止改引擎一个。
在开始本篇之前最好能熟练使用Substance软件。欢迎各路大神斧正错误。
Substance有很多噪波生成器,下面将从最基础的噪波开始,把Substance有的或者没有的噪波生成节点全部在Unreal种实现。使用噪波的组合,我们能做出很多效果。
要生成一个噪声,我们一般需要三步,第一步是构造随机发生器,第二步是插值,第三步是叠加。
噪声从生成方式上可以分为几种:
(1)white noise
(2)perlin noise
(3)value noise
(4)worley noise
效果:
公式代码:
float RandFast( uint2 PixelPos, float Magic = 3571.0 )
{
float2 Random2 = ( 1.0 / 4320.0 ) * PixelPos + float2( 0.25, 0.0 );
float Random = frac( dot( Random2 * Random2, Magic ) );
Random = frac( Random * Random * (2 * Magic) );
return Random;
}
材质:
其实为啥这个算法是这样呢,并没有为啥,就是拟合得好所以就这样了。我把这些常用的函数封装到材质函数节点里,以后做程序化纹理的时候就能直接用了。
后面的很多介绍里的很多这种公用逻辑都会这样做,做完这些只会我们就能建立一个自己的工具库,就像Substance那样。这时有人就要说了,我们不是有noise节点么,为啥还要自己写呢?其实我们应该更多靠自己,我们需要知道原理之后才能根据需求来改进它和精进它,而不是一直拿来主义。
这一步就是只做了构造随机发生器这一步。
这种是对上面的改进,加入缩放因素,可以让我们做出棋盘格之类的纹理
效果:
代码:
float2 Random(float2 uv)
{
float Magic = 3571;
float2 PixelPos = uv * 512;
float2 Random2 = ( 1.0 / 4320.0 ) * PixelPos + float2( 0.25, 0.0 );
float Random = frac( dot( Random2 * Random2, Magic ) );
Random = frac( Random * Random * (2 * Magic) );
return Random;
}
float main(float2 uv)
{
return Random(floor(uv * 50));
}
材质:
想要生成柏林噪声,需要做以下几步:
(1)定义晶格,每个晶格的顶点有一个“伪随机”的梯度向量(其实就是个向量啦)。对于二维的Perlin噪声来说,晶格结构就是一个平面网格,三维的就是一个立方体网格。
(2)输入一个点(二维的话就是二维坐标,三维就是三维坐标,n维的就是n个坐标),我们找到和它相邻的那些晶格顶点(二维下有4个,三维下有8个,n维下有 个),计算该点到各个晶格顶点的距离向量,再分别与顶点上的梯度向量做点乘,得到 个点乘结果。
(3)使用缓和曲线来计算它们的权重和。
以二维为例主要代码如下(解释看注释)
//随机数
float2 hash22(float2 p)
{
p = float2( dot(p,float2(127.1,311.7)),
dot(p,float2(269.5,183.3)));
return -1.0 + 2.0 * frac(sin(p) * 43758.5453123);
}
//构造perlin noise,输入一个点
float perlin_noise(float 2p)
{
//生成第二步:构建晶格
float2 pi = floor(p);
float2 pf = p - pi;
float2 w = pf * pf * (3.0 - 2.0 * pf);
//生成第三步:使用缓和曲线
return lerp(lerp(dot(hash22(pi + float2(0.0, 0.0)), pf - float2(0.0, 0.0)),
dot(hash22(pi + float2(1.0, 0.0)), pf - float2(1.0, 0.0)), w.x),
lerp(dot(hash22(pi + float2(0.0, 1.0)), pf - float2(0.0, 1.0)),
dot(hash22(pi + float2(1.0, 1.0)), pf - float2(1.0, 1.0)), w.x),
w.y);
}
把单一的perlin noise进行叠加即可出现很多有趣的效果。下面几个是多层叠加的柏林噪波
效果:
Noise itself
Noise_Sum
noise_sum_abs
noise_sum_abs_sin
代码:
//#define Use_Perlin
//#define Use_Value
#define Use_Simplex
// ========= Hash ===========
float3 hashOld33(float3 p)
{
p = float3( dot(p,float3(127.1,311.7, 74.7)),
dot(p,float3(269.5,183.3,246.1)),
dot(p,float3(113.5,271.9,124.6)));
return -1.0 + 2.0 * frac(sin(p)*43758.5453123);
}
float hashOld31(float3 p)
{
float h = dot(p,float3(127.1,311.7, 74.7));
return -1.0 + 2.0 * frac(sin(h)*43758.5453123);
}
// Grab from https://www.shadertoy.com/view/4djSRW
#define MOD3 float3(0.1031, 0.11369, 0.13787)
//#define MOD3 float3(443.8975,397.2973, 491.1871)
float hash31(float3 p3)
{
p3 = frac(p3 * MOD3);
p3 += dot(p3, p3.yzx + 19.19);
return -1.0 + 2.0 * frac((p3.x + p3.y) * p3.z);
}
float3 hash33(float3 p3)
{
p3 = frac(p3 * MOD3);
p3 += dot(p3, p3.yxz+19.19);
return -1.0 + 2.0 * frac(float3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x));
}
// ========= Noise ===========
float value_noise(float3 p)
{
float3 pi = floor(p);
float3 pf = p - pi;
float3 w = pf * pf * (3.0 - 2.0 * pf);
return lerp(
lerp(
lerp(hash31(pi + float3(0, 0, 0)), hash31(pi + float3(1, 0, 0)), w.x),
lerp(hash31(pi + float3(0, 0, 1)), hash31(pi + float3(1, 0, 1)), w.x),
w.z),
lerp(
lerp(hash31(pi + float3(0, 1, 0)), hash31(pi + float3(1, 1, 0)), w.x),
lerp(hash31(pi + float3(0, 1, 1)), hash31(pi + float3(1, 1, 1)), w.x),
w.z),
w.y);
}
float perlin_noise(float3 p)
{
float3 pi = floor(p);
float3 pf = p - pi;
float3 w = pf * pf * (3.0 - 2.0 * pf);
return lerp(
lerp(
lerp(dot(pf - float3(0, 0, 0), hash33(pi + float3(0, 0, 0))),
dot(pf - float3(1, 0, 0), hash33(pi + float3(1, 0, 0))),
w.x),
lerp(dot(pf - float3(0, 0, 1), hash33(pi + float3(0, 0, 1))),
dot(pf - float3(1, 0, 1), hash33(pi + float3(1, 0, 1))),
w.x),
w.z),
lerp(
lerp(dot(pf - float3(0, 1, 0), hash33(pi + float3(0, 1, 0))),
dot(pf - float3(1, 1, 0), hash33(pi + float3(1, 1, 0))),
w.x),
lerp(dot(pf - float3(0, 1, 1), hash33(pi + float3(0, 1, 1))),
dot(pf - float3(1, 1, 1), hash33(pi + float3(1, 1, 1))),
w.x),
w.z),
w.y);
}
float simplex_noise(float3 p)
{
const float K1 = 0.333333333;
const float K2 = 0.166666667;
float3 i = floor(p + (p.x + p.y + p.z) * K1);
float3 d0 = p - (i - (i.x + i.y + i.z) * K2);
// thx nikita: https://www.shadertoy.com/view/XsX3zB
float3 e = step(float3(0, 0, 0), d0 - d0.yzx);
float3 i1 = e * (1.0 - e.zxy);
float3 i2 = 1.0 - e.zxy * (1.0 - e);
float3 d1 = d0 - (i1 - 1.0 * K2);
float3 d2 = d0 - (i2 - 2.0 * K2);
float3 d3 = d0 - (1.0 - 3.0 * K2);
float4 h = max(0.6 - float4(dot(d0, d0), dot(d1, d1), dot(d2, d2), dot(d3, d3)), 0.0);
float4 n = h * h * h * h * float4(dot(d0, hash33(i)), dot(d1, hash33(i + i1)), dot(d2, hash33(i + i2)), dot(d3, hash33(i + 1.0)));
return dot(float4(31.316, 31.316, 31.316, 31.316), n);
}
float noise(float3 p)
{
#ifdef Use_Perlin
return perlin_noise(p * 2.0);
#elif defined Use_Value
return value_noise(p * 2.0);
#elif defined Use_Simplex
return simplex_noise(p);
#endif
return 0.0;
}
// ========== Different function ==========
float noise_itself(float3 p)
{
return noise(p * 8.0);
}
float noise_sum(float3 p)
{
float f = 0.0;
p = p * 4.0;
f += 1.0000 * noise(p); p = 2.0 * p;
f += 0.5000 * noise(p); p = 2.0 * p;
f += 0.2500 * noise(p); p = 2.0 * p;
f += 0.1250 * noise(p); p = 2.0 * p;
f += 0.0625 * noise(p); p = 2.0 * p;
return f;
}
float noise_sum_abs(float3 p)
{
float f = 0.0;
p = p * 3.0;
f += 1.0000 * abs(noise(p)); p = 2.0 * p;
f += 0.5000 * abs(noise(p)); p = 2.0 * p;
f += 0.2500 * abs(noise(p)); p = 2.0 * p;
f += 0.1250 * abs(noise(p)); p = 2.0 * p;
f += 0.0625 * abs(noise(p)); p = 2.0 * p;
return f;
}
float noise_sum_abs_sin(float3 p)
{
float f = noise_sum_abs(p);
f = sin(f * 2.5 + p.x * 5.0 - 1.5);
return f ;
}
材质:
这个节点相对上一个节点,噪声构造步骤就算走全了,有噪声发生器的构造,插值,叠加这三步。叠加是在fbm(分形布朗运动)函数中完成。
通过对柏林噪声修改我们可以得到很多效果,把这些效果进行组合又是很多种变化。对每种噪声的参数进行修改又是很多变化。
Perlin noise_sum_abs和这个很像
效果:
代码:
// Some useful functions
float3 mod289(float3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
float2 mod289(float2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
float3 permute(float3 x) { return mod289(((x*34.0)+1.0)*x); }
float snoise(float2 v) {
// Precompute values for skewed triangular grid
const float4 C = float4(0.211324865405187,
// (3.0-sqrt(3.0))/6.0
0.366025403784439,
// 0.5*(sqrt(3.0)-1.0)
-0.577350269189626,
// -1.0 + 2.0 * C.x
0.024390243902439);
// 1.0 / 41.0
// First corner (x0)
float2 i = floor(v + dot(v, C.yy));
float2 x0 = v - i + dot(i, C.xx);
// Other two corners (x1, x2)
float2 i1 = float2(0.0, 0.0);
i1 = (x0.x > x0.y)? float2(1.0, 0.0):float2(0.0, 1.0);
float2 x1 = x0.xy + C.xx - i1;
float2 x2 = x0.xy + C.zz;
// Do some permutations to avoid
// truncation effects in permutation
i = mod289(i);
float3 p = permute(
permute( i.y + float3(0.0, i1.y, 1.0))
+ i.x + float3(0.0, i1.x, 1.0 ));
float3 m = max(float3(0.5,0.5,0.5) - float3(dot(x0,x0),dot(x1,x1),dot(x2,x2)), 0.0);
m = m*m ;
m = m*m ;
// Gradients:
// 41 pts uniformly over a line, mapped onto a diamond
// The ring size 17*17 = 289 is close to a multiple
// of 41 (41*7 = 287)
float3 x = 2.0 * frac(p * C.www) - 1.0;
float3 h = abs(x) - 0.5;
float3 ox = floor(x + 0.5);
float3 a0 = x - ox;
// Normalise gradients implicitly by scaling m
// Approximation of: m *= inversesqrt(a0*a0 + h*h);
m *= 1.79284291400159 - 0.85373472095314 * (a0* a0 + h * h);
// Compute final noise value at P
float3 g = float3(0.0, 0.0, 0.0);
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * float2(x1.x, x2.x) + h.yz * float2(x1.y, x2.y);
return 130.0 * dot(m, g);
}
#define OCTAVES 3
float fbm (in float2 st)
{
// Initial values
float value = 0.0;
float amplitude = 0.5;
float frequency = 0.0;
//
// Loop of octaves
for (int i = 0; i < OCTAVES; i++)
{
value += amplitude * abs(snoise(st));
st *= 2.0;
amplitude *= 0.5;
}
return value;
}
材质:
其实后面那个参数可以暴露出来可调。
做道路,线,布料,青苔根须,能量等效果都非常合适
效果:
代码:
// Some useful functions
float3 mod289(float3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
float2 mod289(float2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
float3 permute(float3 x) { return mod289(((x*34.0)+1.0)*x); }
//
// Description : GLSL 2D simplex noise function
// Author : Ian McEwan, Ashima Arts
// Maintainer : ijm
// Lastmod : 20110822 (ijm)
// License :
// Copyright (C) 2011 Ashima Arts. All rights reserved.
// Distributed under the MIT License. See LICENSE file.
// https://github.com/ashima/webgl-noise
//
float snoise(float2 v) {
// Precompute values for skewed triangular grid
const float4 C = float4(0.211324865405187,
// (3.0-sqrt(3.0))/6.0
0.366025403784439,
// 0.5*(sqrt(3.0)-1.0)
-0.577350269189626,
// -1.0 + 2.0 * C.x
0.024390243902439);
// 1.0 / 41.0
// First corner (x0)
float2 i = floor(v + dot(v, C.yy));
float2 x0 = v - i + dot(i, C.xx);
// Other two corners (x1, x2)
float2 i1 = float2(0.0, 0.0);
i1 = (x0.x > x0.y)? float2(1.0, 0.0):float2(0.0, 1.0);
float2 x1 = x0.xy + C.xx - i1;
float2 x2 = x0.xy + C.zz;
// Do some permutations to avoid
// truncation effects in permutation
i = mod289(i);
float3 p = permute(
permute( i.y + float3(0.0, i1.y, 1.0))
+ i.x + float3(0.0, i1.x, 1.0 ));
float3 m = max(0.5 - float3(
dot(x0,x0),
dot(x1,x1),
dot(x2,x2)
), 0.0);
m = m*m ;
m = m*m ;
// Gradients:
// 41 pts uniformly over a line, mapped onto a diamond
// The ring size 17*17 = 289 is close to a multiple
// of 41 (41*7 = 287)
float3 x = 2.0 * frac(p * C.www) - float3(1.0, 1.0, 1.0);
float3 h = abs(x) - 0.5;
float3 ox = floor(x + 0.5);
float3 a0 = x - ox;
// Normalise gradients implicitly by scaling m
// Approximation of: m *= inversesqrt(a0*a0 + h*h);
m *= 1.79284291400159 - 0.85373472095314 * (a0*a0+h*h);
// Compute final noise value at P
float3 g = float3(0.0, 0.0, 0.0);
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * float2(x1.x, x2.x) + h.yz * float2(x1.y, x2.y);
return 130.0 * dot(m, g);
}
#define OCTAVES 4
// Ridged multifractal
// See "Texturing & Modeling, A Procedural Approach", Chapter 12
float ridge(float h, float offset)
{
h = abs(h); // create creases
h = offset - h; // invert so creases are at top
h = h * h; // sharpen creases
return h;
}
float fbm(float2 p)
{
float lacunarity = 2.0;
float gain = 0.5;
float offset = 0.9;
float sum = 0.0;
float freq = 1.0, amp = 0.5;
float prev = 1.0;
for(int i=0; i < OCTAVES; i++)
{
float n = ridge(snoise(p*freq), offset);
sum += n*amp;
sum += n*amp*prev; // scale by previous octave
prev = n;
freq *= lacunarity;
amp *= gain;
}
return sum;
}
材质:
它用来做水,云雾,地形等效果都挺不错。
效果:
代码:
float random (in float2 _st) {
return frac(sin(dot(_st.xy,
float2(12.9898,78.233)))*
43758.5453123);
}
// Based on Morgan McGuire @morgan3d
// https://www.shadertoy.com/view/4dS3Wd
float noise (in float2 _st) {
float2 i = floor(_st);
float2 f = frac(_st);
// Four corners in 2D of a tile
float a = random(i);
float b = random(i + float2(1.0, 0.0));
float c = random(i + float2(0.0, 1.0));
float d = random(i + float2(1.0, 1.0));
float2 u = f * f * (float2(3.0, 3.0) - 2.0 * f);
return lerp(a, b, u.x) +
(c - a)* u.y * (1.0 - u.x) +
(d - b) * u.x * u.y;
}
#define NUM_OCTAVES 5
float fbm ( in float2 _st) {
float v = 0.0;
float a = 0.5;
float2 shift = float2(100.0, 100.0);
// Rotate to reduce axial bias
float2x2 rot = float2x2(cos(0.5), sin(0.5),
-sin(0.5), cos(0.50));
for (int i = 0; i < NUM_OCTAVES; ++i)
{
v += a * noise(_st);
_st = mul(rot , _st) * 2.0 + shift;
a *= 0.5;
}
return v;
}
材质:
Voronoi Nose的思路就是把UV空间分成若干个格子,然后在格子里撒点,算点到像素的最短距离。维诺噪声因为比较像水的caustic,所以也常常拿来做水下Caustic效果。
效果:
代码:
#define HASHSCALE3 float3(0.1031, 0.1030, 0.0973)
float2 hash22(float2 p)
{
float3 p3 = frac(p.xyx * HASHSCALE3);
float tem = dot(p3, p3.yzx + float3(19.19, 19.19, 19.19));
p3 = p3 + float3(tem, tem, tem);
return frac((p3.xx + p3.yz) * p3.zy);
}
float wnoise(float2 p, float time)
{
float2 n = floor(p);
float2 f = frac(p);
float md = 5.0;
float2 m = float2(0.0 ,0.0);
for (int i = -1; i <= 1; i++)
{
for (int j = -1; j <= 1; j++)
{
float2 g = float2(i, j);
float2 o = hash22(n + g);
o = float2(0.5, 0.5) + 0.5* sin(float2(time, time) + 6.28 * o);
float2 r = g + o - f;
float d = dot(r, r);
if (d < md)
{
md = d;
m = n + g + o;
}
}
}
return md;
}
材质
这种Noise十分适合拿来做流体。做岩石,河床等被流体冲刷过的效果也挺不错的。
效果:
代码:
#define noiseSwirlSteps 2
#define noiseSwirlValue 1.0
#define noiseSwirlStepValue noiseSwirlValue / noiseSwirlSteps
#define noiseScale 2.0
#define noiseTimeScale 0.1
float3 mod289(float3 x)
{
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
float4 mod289(float4 x)
{
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
float4 permute(float4 x)
{
return mod289(((x * 34.0) + float4(1, 1, 1, 1)) * x);
}
float4 taylorInvSqrt(float4 r)
{
return 1.79284291400159 - 0.85373472095314 * r;
}
float simplex(float3 v)
{
const float2 C = float2(1.0/6.0, 1.0/3.0);
const float4 D = float4(0.0, 0.5, 1.0, 2.0);
// First corner
float3 i = floor(v + dot(v, C.yyy));
float3 x0 = v - i + dot(i, C.xxx);
// Other corners
float3 g = step(x0.yzx, x0.xyz);
float3 l = float3(1.0, 1.0, 1.0) - g;
float3 i1 = min( g.xyz, l.zxy );
float3 i2 = max( g.xyz, l.zxy );
// x0 = x0 - 0.0 + 0.0 * C.xxx;
// x1 = x0 - i1 + 1.0 * C.xxx;
// x2 = x0 - i2 + 2.0 * C.xxx;
// x3 = x0 - 1.0 + 3.0 * C.xxx;
float3 x1 = x0 - i1 + C.xxx;
float3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
float3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
// Permutations
i = mod289(i);
float4 p = permute(permute(permute(i.z + float4(0.0, i1.z, i2.z, 1.0)) + i.y + float4(0.0, i1.y, i2.y, 1.0)) + i.x + float4(0.0, i1.x, i2.x, 1.0));
// Gradients: 7x7 points over a square, mapped onto an octahedron.
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
float n_ = 0.142857142857; // 1.0/7.0
float3 ns = n_ * D.wyz - D.xzx;
float4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
float4 x_ = floor(j * ns.z);
float4 y_ = floor(j - 7.0 * x_); // mod(j,N)
float4 x = x_ *ns.x + ns.yyyy;
float4 y = y_ *ns.x + ns.yyyy;
float4 h = 1.0 - abs(x) - abs(y);
float4 b0 = float4( x.xy, y.xy );
float4 b1 = float4( x.zw, y.zw );
//float4 s0 = float4(lessThan(b0,0.0))*2.0 - 1.0;
//float4 s1 = float4(lessThan(b1,0.0))*2.0 - 1.0;
float4 s0 = floor(b0) * 2.0 + 1.0;
float4 s1 = floor(b1) * 2.0 + 1.0;
float4 sh = -step(h, float4(0, 0, 0, 0));
float4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;
float4 a1 = b1.xzyw + s1.xzyw * sh.zzww;
float3 p0 = float3(a0.xy, h.x);
float3 p1 = float3(a0.zw, h.y);
float3 p2 = float3(a1.xy, h.z);
float3 p3 = float3(a1.zw, h.w);
//Normalise gradients
float4 norm = taylorInvSqrt(float4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
p0 *= norm.x;
p1 *= norm.y;
p2 *= norm.z;
p3 *= norm.w;
// Mix final noise value
float4 m = max(0.6 - float4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
m = m * m;
return 42.0 * dot( m * m, float4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));
}
float fbm3(float3 v)
{
float result = simplex(v);
result += simplex(v * 2.0) / 2.0;
result += simplex(v * 4.0) / 4.0;
result /= (1.0 + 1.0 / 2.0 + 1.0 / 4.0);
return result;
}
float fbm5(float3 v)
{
float result = simplex(v);
result += simplex(v * 2.0) / 2.0;
result += simplex(v * 4.0) / 4.0;
result += simplex(v * 8.0) / 8.0;
result += simplex(v * 16.0) / 16.0;
result = result / (1.0 + 1.0 / 2.0 + 1.0 / 4.0 + 1.0 / 8.0 + 1.0 / 16.0);
return result;
}
float getNoise(float3 v)
{
// make it curl
for (int i = 0; i < noiseSwirlSteps; i++)
{
v.xy += float2(fbm3(v), fbm3(float3(v.xy, v.z + 1000.0))) * noiseSwirlStepValue;
}
// normalize
return fbm5(v) / 2.0 + 0.5;
//return v.z;
}
材质:
主函数调getNoise即可。
效果:
代码
const int MAX_ITER = 5;
float2 p = fmod(uv * 3.14, 3.14) - 250.0;
float2 i = float2(p);
float c = 1.0;
float inten = 0.005;
for (int n = 0; n < MAX_ITER; n++)
{
float t = Time * (1.0 - (3.5 / float(n + 1)));
i = p + float2(cos(t - i.x) + sin(t + i.y), sin(t - i.y) + cos(t + i.x));
c += 1.0 / length(float2(p.x / (sin(i.x + t) / inten), p.y / (cos(i.y + t) / inten)));
}
c /= float(MAX_ITER);
c = 1.17 - pow(c, 1.4);
float val = pow(abs(c), 8.0);
这个我是用Computeshader算出来放到RT上的
至此我们的UnrealSubstance噪波生成工具节点算是初步开发完成了,后面如果有新的需求再加。我们暂时把这些逻辑放在材质函数里,以后再把它们开发成C++节点,后期如果虚幻材质编辑器扛不住巨大的计算压力,甚至可以加入CS为节点加速。
Enjoy It!
参考资料
【1】http://webstaff.itn.liu.se/~stegu/jgt2012/article.pdf
【2】https://thebookofshaders.com/11/
【3】Shadertoy
【4】http://www.iquilezles.org/www/articles/morenoise/morenoise.htm
【5】[数学][转载][柏林噪声] - Memo - 博客园
【6】Shadertoy
【7】中级Shader教程23 voronoi算法
【8】https://www.cs.ubc.ca/~rbridson/docs/bridson-siggraph2007-curlnoise.pdf
【10】Shadertoy
【10】Shadertoy
【11】【图形学】谈谈噪声 - candycat - CSDN博客(强烈推荐这篇博客)