[Godot][GLSL] The Book of Shaders 学习 1

348 阅读5分钟

1.长方形

1.1 边框

主要使用 step 达成 0 和 1 的分界线

期望 step 返回 0 的地方最终得到黑色,返回 1 的地方最终得到白色

对单位坐标的判断可以得到横向上一个 0 1 分界和纵向上一个 0 1 分界,将原点和坐标轴方向改变之后又得到两个分界

四个分界组合,希望黑色会覆盖白色,也就是 0 和 0 得 0,0 和 1 得 0,1 和 1 得 1,因此使用乘法

// Author @patriciogv ( patriciogonzalezvivo.com ) - 2015

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

void main(){
    vec2 st = gl_FragCoord.xy/u_resolution.xy;
    vec3 color = vec3(0.0);

    // bottom-left
    float b = smoothstep(0.0,0.1,st.x);
    float l = smoothstep(0.0,0.1,st.y);
    float pct = b*l;

    // top-right
    float t = smoothstep(0.0,0.1,1.0-st.x);
    float r = smoothstep(0.0,0.1,1.0-st.y);
    pct *= t*r;
    
    color = vec3(pct);

    gl_FragColor = vec4(color,1.0);
}

1.2 色块填充

主要使用 step 达成 0 和 1 的分界线

期望 step 返回 0 的地方最终得到黑色,返回 1 的地方最终得到白色

对单位坐标的判断可以得到横向上一个 0 1 分界和纵向上一个 0 1 分界,将不等号改变之后又得到两个分界

四个分界组合,希望 0 和 0 得 0,0 和 1 得 1,1 和 1 得 1,因此使用带钳制的加法

// Author @patriciogv ( patriciogonzalezvivo.com ) - 2015

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

void main(){
    vec2 st = gl_FragCoord.xy/u_resolution.xy;
    vec3 color = vec3(0.0);

    vec2 bl1 = step(st,vec2(0.2));       // bottom-left
    vec2 bl2 = step(vec2(0.6),st);       // bottom-left
    vec2 bl = (bl1+bl2);
    color = vec3(clamp(bl.x+bl.y,0.0,1.0));

    gl_FragColor = vec4(color,1.0);
}

2.圆

2.1 双级

// Author @patriciogv ( patriciogonzalezvivo.com ) - 2015

#ifdef GL_ES
precision mediump float;
#endif

#define PI 3.14159265359

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

void main(){
    vec2 st = gl_FragCoord.xy/u_resolution;
    float a = distance(st,vec2(0.5+0.1*sin(u_time),0.5+0.1*cos(u_time)));
    a -= 0.05;
    float b = distance(st,vec2(0.5+0.1*sin(u_time+PI),0.5+0.1*cos(u_time+PI)));
    b -= 0.05;
    float pct = pow(a,b);

	vec3 color = vec3(pct);

	gl_FragColor = vec4( color, 1.0 );
}

2.2 重复

使用 fract() 获得若干个重复出现的区间

// Author @patriciogv ( patriciogonzalezvivo.com ) - 2015

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

void main(){
  vec2 st = gl_FragCoord.xy/u_resolution.xy;
  st.x *= u_resolution.x/u_resolution.y;
  vec3 color = vec3(0.0);
  float d = 0.0;

  // Remap the space to -1. to 1.
  st = st *2.-1.;

  // Make the distance field
  d = length( abs(st) );

  // Visualize the distance field
  gl_FragColor = vec4(vec3(fract(d*10.0-u_time)),1.0);
}

使用 plot 函数会得到更加干净的线条

使用造型函数可以修改 fract() 各区间的密度

// Author @patriciogv ( patriciogonzalezvivo.com ) - 2015

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

float plot(vec2 st, float pct){
  return  smoothstep( pct-0.02, pct, st.y) -
          smoothstep( pct, pct+0.02, st.y);
}

float doubleCubicSeatWithLinearBlend (float x, float a, float b){

  float epsilon = 0.00001;
  float min_param_a = 0.0 + epsilon;
  float max_param_a = 1.0 - epsilon;
  float min_param_b = 0.0;
  float max_param_b = 1.0;
  a = min(max_param_a, max(min_param_a, a));  
  b = min(max_param_b, max(min_param_b, b)); 
  b = 1.0 - b; //reverse for intelligibility.
  
  float y = 0.0;
  if (x<=a){
    y = b*x + (1.0-b)*a*(1.0-pow(1.0-x/a, 3.0));
  } else {
    y = b*x + (1.0-b)*(a + (1.0-a)*pow((x-a)/(1.0-a), 3.0));
  }
  return y;
}

void main(){
    vec2 st = gl_FragCoord.xy/u_resolution.xy;
    vec3 color = vec3(0.0);

    vec2 pos = vec2(0.5)-st;

    float r = length(pos)*2.0;
    float a = atan(pos.y,pos.x);
    
    float pct = plot(abs(pos),fract(5.0*doubleCubicSeatWithLinearBlend(r, 0.4, 0.2)+u_time));
    color = pct*vec3(0.0, 1.0, 0.0);
    gl_FragColor = vec4(color, 1.0);
}

2.3 齿轮

// Author @patriciogv ( patriciogonzalezvivo.com ) - 2015

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

void main(){
    vec2 st = gl_FragCoord.xy/u_resolution.xy;
    vec3 color = vec3(0.0);

    vec2 pos = vec2(0.5)-st;

    float r = length(pos)*2.0;
    float a = atan(pos.y,pos.x);

    // 外环
    float f = smoothstep(-0.9, 0.7, cos((a+u_time)*10.0))*0.2+0.5;
    // 中空
    f *= step(0.3, r);
    // 内环
    f += step(r, 0.1);

    color = vec3( 1.-smoothstep(f,f+0.02,r) );

    gl_FragColor = vec4(color, 1.0);
}

2.4 辐射

这个图案有一点危险

// Author @patriciogv ( patriciogonzalezvivo.com ) - 2015

#ifdef GL_ES
precision mediump float;
#endif

#define PI 3.14159265359
#define TWO_PI 6.28318530718

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

// Reference to
// http://thndl.com/square-shaped-shaders.html

void main(){
  vec2 st = gl_FragCoord.xy/u_resolution.xy;
  st.x *= u_resolution.x/u_resolution.y;
  vec3 color = vec3(0.0);
  float d = 0.0;

  // Remap the space to -1. to 1.
  st = st *2.-1.;

  // Number of sides of your shape
  int N = 10;

  // Angle and radius from the current pixel
  float a = atan(st.x,st.y)+PI+u_time;
  float r = TWO_PI/float(N);

  // Shaping function that modulate the distance
  d = min((floor(.8+a/r)*r-a),length(st));

  color = vec3(1.0-smoothstep(.4,.41,d));
  // color = vec3(d);

  gl_FragColor = vec4(color,1.0);
}

3.矩阵

3.1 常用矩阵

缩放

mat2 scale(vec2 _scale){
    return mat2(_scale.x,0.0,
                0.0,_scale.y);
}

旋转

mat2 rotate2d(float _angle){
    return mat2(cos(_angle),-sin(_angle),
                sin(_angle),cos(_angle));
}

平铺

vec2 tile(vec2 _st, float _zoom){
    _st *= _zoom;
    return fract(_st);
}

4.随机

4.1 随机索引

图案 1

图案 2

两种图案的组合

将坐标缩放后,以坐标的整数部分为种子取随机值,作为索引值,以坐标的浮点数部分,作为 0~1 的坐标

所有可能的图案构成一个图案集,每一个 UV 小格的索引值对应到这个图案集的某个图案

vec2 st = gl_FragCoord.xy/u_resolution.xy;
st *= 10.0;

vec2 ipos = floor(st);  // integer
vec2 fpos = fract(st);  // fraction

我后来越想越觉得这个取整取小数的操作很神

因为他既把一个正方形分成了若干个小格,又获得了这个小格的独立索引,又获得了这个小格内的单位坐标

小格的独立索引还是顺序的,这意味着你知道 (x0,y0) 就知道 (x0+a,y0+b)

4.2 噪声

Value Noise

// 2D Noise based on Morgan McGuire @morgan3d
// https://www.shadertoy.com/view/4dS3Wd
float noise (in vec2 st) {
    vec2 i = floor(st);
    vec2 f = fract(st);

    // Four corners in 2D of a tile
    float a = random(i);
    float b = random(i + vec2(1.0, 0.0));
    float c = random(i + vec2(0.0, 1.0));
    float d = random(i + vec2(1.0, 1.0));

    // Smooth Interpolation

    // Cubic Hermine Curve.  Same as SmoothStep()
    vec2 u = f*f*(3.0-2.0*f);
    // u = smoothstep(0.,1.,f);
    
    return mix(a, b, u.x) +
            (c - a)* u.y * (1.0 - u.x) +
            (d - b) * u.x * u.y;
    
    // 等效于
    // float mix1 = mix(a, b, u.x);
    // float mix2 = mix(c, d, u.x);
    // float mix3 = mix(mix1, mix2, u.y);
    // return mix3;
}

知道 (x0,y0) 就知道 (x0+a,y0+b) 的一个意义就在于,在我随机的时候,我这一个小格得到的随机值被整数索引确定了,那我对周围四个格子采样也是确定的

最后那个返回的插值公式跟普通的双插值效果一样的

    // float mix1 = mix(a, b, u.x);
    // float mix2 = mix(c, d, u.x);
    // float mix3 = mix(mix1, mix2, u.y);
    float mix1 = a*(1.0-u.x)+b*u.x;
    float mix2 = c*(1.0-u.x)+d*u.x;
    float mix3 = mix1*(1.0-u.y)+mix2*u.y;
    // mix3 = mix1*(1.0-u.y)+(c*(1.0-u.x)+d*u.x)*u.y
    // mix3 = mix1 - mix1*u.y + c*(1.0-u.x)*u.y+d*u.x*u.y
    // mix3 = mix1 - (a*(1.0-u.x)+b*u.x)*u.y + c*(1.0-u.x)*u.y + d*u.x*u.y
    // mix3 = mix1 - a*(1.0-u.x)*u.y + c*(1.0-u.x)*u.y - b*u.x*u.y + d*u.x*u.y
    // 之后合并同类项就得到了简化的式子
    // 感觉这样应该没有什么深意把

最后简化得到的结果乍一看跟原意相差甚远,mix(a, b, u.x)是横向,(c - a)* u.y * (1.0 - u.x)(d - b) * u.x * u.y 是两个对角线,不写一下感觉不能保证一横向和两个对角线和先两横再一竖的结果相同

两个 Noise 的比较:

Value Noise

    float mix1 = mix( random2( i + vec2(0.0,0.0) ), random2( i + vec2(1.0,0.0) ), u.x);
    float mix2 = mix( random2( i + vec2(0.0,1.0) ), random2( i + vec2(1.0,1.0) ), u.x);
    return mix(mix1, mix2, u.y);

Gradient Noise

    float mix1 = mix( dot( random2(i + vec2(0.0,0.0) ), f - vec2(0.0,0.0) ),
dot( random2(i + vec2(1.0,0.0) ), f - vec2(1.0,0.0) ), u.x);
    float mix2 = mix( dot( random2(i + vec2(0.0,1.0) ), f - vec2(0.0,1.0) ), dot( random2(i + vec2(1.0,1.0) ), f - vec2(1.0,1.0) ), u.x);
    return mix( mix1, mix2, u.y);