虚幻4渲染编程(程序化纹理篇)【第一卷:UnrealSubstance工具节点搭建---噪波】...

我的专栏目录:

小IVan:专题概述及目录​zhuanlan.zhihu.com图标

本篇概述:

程序化纹理有着很多好处,第一可以方便修改,当我们的生成逻辑写好后,想要调整的话就是调调参数的事情。第二就是思路清晰,这张图是怎么来的,逻辑非常清晰,而不是传统的图像绘制,画到哪儿想到哪儿,思维是游走的,离散的。第三我们可以实时生成,可以让纹理和玩家产生互动等等。

现在主流的是用Substance来生成,但是其实引擎也能做,引擎本来就是用来操作这些素材的地方。想看引擎和Substance的对接的话可能你要失望了,对于目前的Substance而言,我个人极力反对目前版本的Substance和引擎直接结合。本章将在引擎里实现各种程序化纹理,不管是离线烘焙也好,还是实时生成也罢。

程序化纹理篇的大概规划是,先把程序化生成需要的节点我们自己全部开发一次,然后把这些工具节点撸好以后在开始用这些工具做纯程序化的纹理(在引擎中)。我们的核心目的是把材质编辑器拓展成引擎内置的“Substance”,像Substance一样能有几何生成,图像转换等功能。当然拓展材质编辑器有很多种办法,不止改引擎一个。

在开始本篇之前最好能熟练使用Substance软件。欢迎各路大神斧正错误。


Substance有很多噪波生成器,下面将从最基础的噪波开始,把Substance有的或者没有的噪波生成节点全部在Unreal种实现。使用噪波的组合,我们能做出很多效果。

要生成一个噪声,我们一般需要三步,第一步是构造随机发生器,第二步是插值,第三步是叠加。

噪声从生成方式上可以分为几种:

(1)white noise

(2)perlin noise

(3)value noise

(4)worley noise

【1】Fast Noise(White Noise)

效果:

v2-51e046a6b761b3b20b8b58ce28ca3ff0_b.jpg

公式代码:

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

材质:

v2-fbe7579ecc83e5fcec6af23bc6c3f052_b.jpg

其实为啥这个算法是这样呢,并没有为啥,就是拟合得好所以就这样了。我把这些常用的函数封装到材质函数节点里,以后做程序化纹理的时候就能直接用了。

v2-3cf7e9e4b4faa839dc2c09e9069a2067_b.jpg
v2-3ee20596f1daa66279fc48d53daf110a_b.jpg

后面的很多介绍里的很多这种公用逻辑都会这样做,做完这些只会我们就能建立一个自己的工具库,就像Substance那样。这时有人就要说了,我们不是有noise节点么,为啥还要自己写呢?其实我们应该更多靠自己,我们需要知道原理之后才能根据需求来改进它和精进它,而不是一直拿来主义。

这一步就是只做了构造随机发生器这一步。


【2】Random With Scale(White Noise)

这种是对上面的改进,加入缩放因素,可以让我们做出棋盘格之类的纹理

效果:

v2-51e20fa1b1647d4e5d509cca96d772ca_b.jpg

代码:

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

材质:

v2-3b376f44ae4f903a52b05887501685e7_b.jpg
v2-9100bb7e94fa10d4303c3508013ac600_b.jpg

【3】Perlin Noise

想要生成柏林噪声,需要做以下几步:

(1)定义晶格,每个晶格的顶点有一个“伪随机”的梯度向量(其实就是个向量啦)。对于二维的Perlin噪声来说,晶格结构就是一个平面网格,三维的就是一个立方体网格。

(2)输入一个点(二维的话就是二维坐标,三维就是三维坐标,n维的就是n个坐标),我们找到和它相邻的那些晶格顶点(二维下有4个,三维下有8个,n维下有 2^{n} 个),计算该点到各个晶格顶点的距离向量,再分别与顶点上的梯度向量做点乘,得到 2^{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

v2-4d6b6189df63d4bcf4c8589429d6a0ea_b.jpg

Noise_Sum

v2-1708299ca9ed9031fb33fb3d1001be03_b.jpg

noise_sum_abs

v2-127feda02c4fffe96a56b68b1d98617f_b.jpg

noise_sum_abs_sin

v2-dc92312648b486acceba72ccd11285ce_b.jpg

代码:

//#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 ;
}

材质:

v2-86b37977f19c947a12c98ab5fc0a08ea_b.jpg

这个节点相对上一个节点,噪声构造步骤就算走全了,有噪声发生器的构造,插值,叠加这三步。叠加是在fbm(分形布朗运动)函数中完成。

通过对柏林噪声修改我们可以得到很多效果,把这些效果进行组合又是很多种变化。对每种噪声的参数进行修改又是很多变化。


【4】turbulence noise

Perlin noise_sum_abs和这个很像

效果:

v2-9477dcd92936e95ede806d2539070885_b.jpg

代码:

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

材质:

v2-a62340db1f5a88186a592163ec09e62c_b.jpg

其实后面那个参数可以暴露出来可调。


【5】ridge noise

做道路,线,布料,青苔根须,能量等效果都非常合适

效果:

v2-0719d93fb825d1515e1f4b0cc7afaa04_b.jpg

代码:

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

材质:

v2-20c53e4d62c6be8c4cb49e9a2a2f3b9f_b.jpg

【6】Domain Warping

它用来做水,云雾,地形等效果都挺不错。

效果:

v2-e7211149ba0d495a32e8103bb4489e11_b.jpg

代码:

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

材质:

v2-d1b4ce51bb192ead6519d6d8703d01fd_b.jpg

【7】Voronoi Nose

Voronoi Nose的思路就是把UV空间分成若干个格子,然后在格子里撒点,算点到像素的最短距离。维诺噪声因为比较像水的caustic,所以也常常拿来做水下Caustic效果。

效果:

v2-5ff8f5911bb8715121a3621cc977c630_b.jpg
v2-401f5dd8aaa25ee7a15d64124eac1f2d_b.gif

代码:

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

材质

v2-8350c8db72abb836b24eb7e14fc86a44_b.jpg

【8】Curl Noise

这种Noise十分适合拿来做流体。做岩石,河床等被流体冲刷过的效果也挺不错的。

效果:

v2-1cb50773e23fafe1a97247c6c890ae49_b.jpg

代码:

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

材质:

v2-06593dd5746e2a9883266a7a459ae0fa_b.jpg
v2-dd50eb0cac2ce68347e1060ffc179dc2_b.jpg

主函数调getNoise即可。


【9】其它叠加

效果:

v2-897bac7ba4a0002892e3f5ed31402a98_b.jpg

代码

    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上的

v2-2e249dcccab4cbe932be20510b2f5442_b.jpg

至此我们的UnrealSubstance噪波生成工具节点算是初步开发完成了,后面如果有新的需求再加。我们暂时把这些逻辑放在材质函数里,以后再把它们开发成C++节点,后期如果虚幻材质编辑器扛不住巨大的计算压力,甚至可以加入CS为节点加速。

v2-6dc93ba5e32070189906a48c017f73bb_b.jpg

Enjoy It!


参考资料

【1】webstaff.itn.liu.se/~st

【2】thebookofshaders.com/11

【3】Shadertoy

【4】iquilezles.org/www/arti

【5】[数学][转载][柏林噪声] - Memo - 博客园

【6】Shadertoy

【7】中级Shader教程23 voronoi算法

【8】cs.ubc.ca/~rbridson/doc

【10】Shadertoy

【10】Shadertoy

【11】【图形学】谈谈噪声 - candycat - CSDN博客(强烈推荐这篇博客)

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cpongo11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值