圖形着色器公式

開篇

我在Shadertoy或者WegGL中編寫着色器程序時,經常需要用到許多繪製2D或者3D圖形學公式。和數學公式一樣,這些公式大多數時候是需要記憶的。爲了後續方便記憶和查閱,本博文總結了一些我在平時開發繪製圖形(尤其是3D圖形)時常用的計算公式。這其中大多數都是前人的思想結晶,我會在每一份說明文字中註明算法的來源,他們有些來自書籍,有些來自視頻,更多的是來自互聯網上博客文字(雖然也都不是他們原創,但可以知道這些技巧被使用時的上下文)。本博文只作爲記錄和總結功能,並不提供創新的想法,並且後續會持續地更新。

Shapes(圖形)

Remap(重分)

不知道用“重分”一詞是否準確,這個公式用在2D繪製的場景中,在需要對固定的區域內進行再次歸一化處理,以便處理圖形。此算法是在youtube頻道上看到的。

  vec2 Remap(vec2 p, float t, float r, float b, float l) {
    return vec2((p.x - r)/ (l -r), (p.y - b) / (t - b));
  }

Plot(線條)

雖然在HTML中畫一條線很容易,但在shader中畫一條線卻沒那麼簡單。需要用到smoothstep函數來處理。在The Book of Shaders的基礎知識中看到。

  float plot_x(vec2 p, float start, float end, float blur) {
    return smoothstep(start, start + blur, p.x)
        - smoothstep(end, end + blur, p.x)
  }

  float plot_y(vec2 p, float start, float end, float blur) {
    return smoothstep(start, start + blur, p.y)
        - smoothstep(end, end + blur, p.y)
  }

Grid(表格)

這是基礎分塊技術,旨在把畫布分成若干等分,是很多酷炫效果的基礎。在The Book of Shaders的基礎知識中學習到,

  vec2 uv = fract(uv * 10.0);

Signed field distance (基礎2D和3D的圖像算法)

符號距離場,這個名字翻譯實在有些彆扭,但是確實非常基礎和實用的創造基礎圖形的方法。這些方法由著名Inigo Quilez原創,他是Shadertoy的創始人,專注圖形領域二十多年,是一位真正的大神級別人物。該系列公式在他的博客中有總結,總共分2D圖形和3D圖像兩篇。此處只做一些常用的基礎的圖形:

Sphere(球體)

  float sdSphere( vec3 p, float s )
  {
    return length(p)-s;
  }

Cube (立方體)

  float sdBox( vec3 p, vec3 b )
  {
    vec3 q = abs(p) - b;
    return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0);
  }

Cylinder (圓柱體)

  float sdRoundedCylinder( vec3 p, float ra, float rb, float h )
  {
    vec2 d = vec2( length(p.xz)-2.0*ra+rb, abs(p.y) - h );
    return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rb;
  }

顏色

RGB和HSV顏色互轉

  
vec3 rgb2hsv(vec3 c){
  vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
  vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
  vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
  float d = q.x - min(q.w, q.y);
  float e = 1.0e-10;
  return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}

vec3 hsv2rgb(vec3 c){
  vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0), 6.0)-3.0)-1.0, 0.0, 1.0);
  rgb = rgb * rgb * (3.0 - 2.0 * rgb);
  return c.z * mix(vec3(1.0), rgb, c.y);
}

View(視角)

RayMarch(光線步進)

在Webgl或者OpenGL,可以直接用頂點着色器構建三維空間中的物體,而在只能使用片元着色器(fragment shader)的環境中,RayMarch算法是進入3D世界的基礎算法公式。它的核心原理是利用一束光線逐步去探索一個物體的邊緣的每個點,使用這個方法才能在2D繪製3D物體。它的計算非常“昂貴”,在GPU能力較差的機器上,幀數會降低。該方法在 Nathan Vaughn's Shaders Language Tutorial 有詳細說明。

#define MAX_STEP  255
#define PRECISION 0.01
float RayMarch(vec2 ro, vec2 rd, float start, float end) {
  float depth = start;
  for(int i=0; i<MAX_STEP; i++) {
    vec2 p = ro + rd * depth;
    float d = (length(p) - 1.0);
    depth += d;

    if(depth < PRECISION  || depth > end) break;
  }
  return depth;
}

Camera Matrix(相機矩陣)

相機矩陣也是在無法使用頂點着色器(vertex shader)的環境中使用,它規定了一個相機的方向,視線方向從而固定了一個觀察者的位置。該方法也是在Nathan Vaughn's Shaders Language Tutorial 博文中有說明。

  mat3 camera(vec3 ro, vec3 lp) {
    vec3 camera_direction = normalize(lp - ro);
    vec3 camera_right = normalize(cross(vec3(0.0, 1.0, 0.0), camera_direction));
    vec3 camera_up = normalize(cross(camera_xx, camera_direction));
    return mat3(
        -camera_direction,
        camera_right,
        -camera_up 
    );
  }

光線碰撞

抗陰影鋸齒(anti aslising)

WebGL在繪製陰影的過程中,陰影會在邊緣顯示不平滑的效果。抗陰影鋸齒使用平均取值過渡算法,讓陰影變得絲滑。最初在Learn OpenGl 教程中學習到:

  float shadows = 0.0;
  float opacity= .4;// 陰影alpha值, 值越小暗度越深
  float texelSize= 1.0 / 2028.0;// 陰影像素尺寸,值越小陰影越逼真
  vec4 rgbaDepth;
  //  消除陰影邊緣的鋸齒 去平局差值四周圍的
  for(float y=-2.0; y <= 2.0; y++){
      for(float x=-2.0; x <=2.0; x++){
          rgbaDepth = texture(u_texture, depth.xy + vec2(x, y) * texelSize);
          shadows += (depth.z - bias > rgbaDepth.r) ? 1.0 : 0.0;
      }
  }
  shadows /= 9.0;// 4*4的樣本
  float visibility = min(opacity + (1.0 - shadows), 1.0); // 抗鋸齒陰影

Noise(噪聲)

噪聲屬於圖形技術中較爲高級的圖像算法。是一種有效的模擬現實世界隨機現象的方法:

Random(隨機算法)

Fractal(分形)

Matrix(矩陣)

rotate(旋轉)

3D的旋轉矩陣雖然經常使用卻對於人腦來說有些記憶成本,不過爲了使的記憶簡化,記住基礎的2D也是一種非常方便的手段。例如處理一個3D物體在一個方向上進行旋轉,就可以只用2D矩陣來處理,該方法最初是在The Book Oif Shaders中學習到,但真正的用法是在youtube中。

  mat2 rotate2D(float angle) {
    return mat2(cos(angle), -sin(angle),
                sin(angle), cos(angle));
  }

縮放與旋轉是異曲同工,不再贅述:

scale(縮放)

  mat2 scale2D(vec2 r) {
    return mat2(r.x, 0.0,
                0.0, r.y
    );
  }

Materials & Light(材質和光照)

3D場景的基礎光照模型---馮式光照模型也稱爲ADS光照模型,是讓虛擬世界中的無圖看起來跟家真實的有效光照模型。最初是在《自頂而下的圖像學設計》中看到。

Ambient(環境光)

  gl_FragColor = ambientColor * a_color;

diffuse(漫反射)

  float fDot = dot(a_normal, u_lightDirection);
  gl_FragColor = diffuseColor * fDot * a_color;

specular(鏡面反射)

  vec2 ref = reflect(a_normal, u_lightDirection);
  float fDot = clamp(dot(ref, -u_viewDirection), 0.0, 1.0);
  float n = pow(fDot, shiness);
  gl_FragColor = specularColor * n * a_color;

Position(定位)

Canvas(座標轉換)

  vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution.xy) / u_resolution.y;

極座標

  vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution.xy) / u_resolution.y
  vec2 polarCoordinate = (atan(uv.x, uv.y), length(uv));
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章