图形学中的噪声入门

一、白噪声

白噪声常与伪随机数一起使用。这样,固定的输入就会产出固定的随机数输出,最终渲染出来的纹理也会是固定的,但又具备随机的视觉效果。

常用的白噪声随机产生函数如下:

float random = dot(vec in , vec const);

使用输入向量和一个任意向量点乘,即可得到一个随机的结果;

在OpenGL中使用:

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform float u_time;

float rand(vec2 vec){
    float random = dot(vec , vec2(-12.000,20.820));
    random = fract(random);
    return random;
}

void main() {
    vec2 uv = gl_FragCoord.xy/u_resolution.yy;
    vec3 color = vec3(rand(uv));
    gl_FragColor = vec4(color,1.0);
}

得到的噪声图:

 

 可以看到两个向量点乘投影形成的条带。一点也不均匀。

解决方法是对点积后的结果取正弦,然后乘上一个大数,将其小数点移后几位,得到更为随机分布的小数。

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform float u_time;

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

void main() {
    vec2 uv = gl_FragCoord.xy/u_resolution.yy;
    vec3 color = vec3(rand(uv));
    gl_FragColor = vec4(color,1.0);
}

得到的噪声如下:

 

 注意这里取的

vec2(12.9898,78.233)
和43758.5453;

都是前辈们总结下来的经验数,可以得到很好的白噪声图。

二、细胞

分割为细胞的方法就是纹理坐标乘上对应的倍数,简单高效。

void main() {
    vec2 uv = gl_FragCoord.xy/u_resolution.yy;
    uv  = uv * 10.0;
    
    vec3 color = vec3(fract(uv),1.0);
    gl_FragColor = vec4(color,1.0);
}

得到的效果图:

 

 为每个细胞赋上不同的颜色:

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;

float rand2dTo1d(vec2 value ,vec2 randvec2 )
{
    vec2 dotDir = randvec2;
    vec2 smallValue = sin(value);
    float random = dot(smallValue, dotDir);
    random = fract(sin(random) * 143758.5453);
    return random;
}

vec2 rand2dto2d(vec2 value){
     return vec2(
        rand2dTo1d(value, vec2(12.989, 78.233)),
        rand2dTo1d(value, vec2(39.346, 11.135))
    );
}

void main() {
    vec2 uv = gl_FragCoord.xy/u_resolution.yy;
    vec2 st  = uv * 10.0;
    

    vec3 color = vec3(rand2dto2d(floor(st)),1.0);
    gl_FragColor = vec4(color,1.0);
}

得到的图片:

 

 这里使用了前面提到的白噪声公式来为细胞产出不同的颜色。

三、连续噪声(Value Noise)

白噪声很好,但是它不具备连续性。

柏林噪声可以很好地解决这个问题。

使用一维噪声:

使用一维的白噪声生成灰度图

float rand1dTo1d(float value){
    float mutator = 0.546;
    float random = fract(sin(value + mutator) * 143758.5453);
    return random;
}

void main() {
    vec2 uv = gl_FragCoord.xy/u_resolution.yy;

    vec3 color = vec3(rand1dTo1d( floor(uv.x * 30.)));
    gl_FragColor = vec4(color,1.0);
}

得到:

 

将其压缩成线:

float rand1dTo1d(float value){
    float mutator = 0.546;
    float random = fract(sin(value + mutator) * 143758.5453);
    return random;
}

void main() {
    vec2 uv = gl_FragCoord.xy/u_resolution.yy;
    
    //移到中心
    uv -= vec2(0.5);
    vec2 st = uv * 6.;
    
    
    float noise = rand1dTo1d( floor(st.x));
    
  //取噪声与纹理y坐标距离
  //在 dist-> 0时画线,
float dist = abs(noise - st.y);

  //抗锯齿 vec3 color
= vec3(smoothstep(0, fwidth(st.y), dist)); gl_FragColor = vec4(color,1.0); }

得到:

 

 为了让值连续,取其小数部分进行插值,修改noise如下:

//插值
    float noise = mix(rand1dTo1d( floor(st.x)), rand1dTo1d(ceil(st.x)), fract(st.x));

得到的图如下:

 

 这时使用的插值因子是

st.x

得到的结果并不平滑,我们应该使用缓动公式插值,常见的公式是

更改,如下:

    //插值
    float x = fract(st.x);
    float t= 6.*pow(x,5.) - 15.*pow(x,4.)+10.*pow(x,3.);
    float noise = mix(rand1dTo1d( floor(st.x)), rand1dTo1d(ceil(st.x)), fract(t));

得到的图如下:

 

 此时,一个连续的一维的噪声就生成了。

下面应用到二维:

一张纹理图片中,直观输入就是uv,一个vec2变量。

 在一维情况下,我们需要考虑左右插值,仅需插值一次。

二维时,取输入点记作(x0,y0),则需要使用(x0+1,y0),(x0,y0+1),(x0+1,y0+1)共四个点计算噪声值。所以,噪声函数应该变为:

float noise2d(vec2 value){
    float x10 = rand2dTo1d(vec2(floor(value.x), ceil(value.y)), vec2(12.989, 78.233));
    float x11 = rand2dTo1d(vec2(ceil(value.x), ceil(value.y)), vec2(12.989, 78.233));
    float x00 = rand2dTo1d(vec2(floor(value.x), floor(value.y)), vec2(12.989, 78.233));
    float x01 = rand2dTo1d(vec2(ceil(value.x), floor(value.y)), vec2(12.989, 78.233));

    float x = fract(value.x);
    float y = fract(value.y);
  
float tx= 6.*pow(x,5.) - 15.*pow(x,4.)+10.*pow(x,3.); float ty= 6.*pow(y,5.) - 15.*pow(y,4.)+10.*pow(y,3.);   //使用 tx插值 x -> x+1 float x1 = mix(x10, x11, tx); float x0 = mix(x00, x01, tx);
  //使用ty插值 y -> y+1
float noise = mix(x0, x1, ty); return noise; }

主函数调用变为:

void main() {
    vec2 uv = gl_FragCoord.xy/u_resolution.yy;
    
    //移到中心
    uv -= vec2(0.5);
    vec2 st = uv * 10.;
    
    //插值
    float noise = noise2d(st);
    vec3 color = vec3(noise);
    
    gl_FragColor = vec4(color,1.0);
}

这时得到的噪声图如下:

 

 一个连续的噪声图。

三维的情况同样,只不过插值点变成了八个。

四、柏林噪声

价值噪声使用内插值的方式实现了连续效果,但是情况不理想,更多时候采用梯度插值的方式,也就是柏林噪声,这样得到的噪声更加平滑连续。

一维柏林噪声实现

float gradientNoise(float value){


  float x =fract(value);
  float tx= 6.*pow(x,5.) - 15.*pow(x,4.)+10.*pow(x,3.);


  float noise0 = (rand1dTo1d(floor(value)) * 2. - 1.) * x;
  float noise1 = (rand1dTo1d(ceil(value)) * 2. - 1.)*(x -1.);

  return mix(noise0, noise1, tx);
}

柏林噪声与价值噪声区别就在噪声产生的方式,rand1dTo1d(floor(value)) * 2. - 1.操作会产生(-1,+1)的随机变量,在一维情况下可视为随机长度的上下梯度。

在(x,x+1)的整数之间,由于伪随机数的关系,产生随机值是固定的,因此,乘上小数部分后,即可得到连续的梯度。

一维柏林噪声结果:

 

 

 

 二维柏林噪声

取(x,y)(x+1,y)(x,y+1)(x+1,y+1)四个正方形顶点,产生四个向量,并与指向正方形内部的方向向量作点积,得到各个顶点在正方形上的投影值,插值即可得到。

a = dot(random( i + vec2(0.0,0.0) ), f - vec2(0.0,0.0) )
b = dot( random( i + vec2(1.0,0.0) ), f - vec2(1.0,0.0) )
c = dot( random( i + vec2(0.0,1.0) ), f - vec2(0.0,1.0) )
d = dot( random( i + vec2(1.0,1.0) ), f - vec2(1.0,1.0) )

//a b 、c d 在x轴连续
ab= mix(a,b,u.x)
cd = mix(b,c,u.x)

//ab cd 在y轴连续
noise = mix(ab,cd,u.y)

得到:

 

 加亮颜色值

col += 0.5;

 

 让明暗分界更加明显:

col = fract(col *10.);

col*10得到的值范围是(0,10),取小数后得到十个(0,1)的数值范围,而且根据噪声的插值方式,这十个数值范围应该是连续且嵌套的,得到类似等高线的效果:

 

 

 五、噪声叠加

在一个噪声基础上叠加多种频率的噪声,可以得到变化丰富的噪声图:

 

 0次叠加

 

叠加1/2频率 +1/2 的noise

 

 (1+1/2+1/4)*noise

在叠加的基础上加上UV偏移

 

 uv =rotateMatrix * uv

 

 

转载于:https://www.cnblogs.com/Afuness/p/11553174.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值