1. 程式人生 > >cocos2d-x 強大的shader系列:【OpenGL】Shader例項分析(一)-Wave

cocos2d-x 強大的shader系列:【OpenGL】Shader例項分析(一)-Wave

這篇文章主要分析一個Shader,從而感受shader的魅力,並學習相關shader的函式的用法。

下面是程式碼:

  1. Shader "shadertoy/Waves" {  //see https://www.shadertoy.com/view/4dsGzH
  2.     CGINCLUDE    
  3.         #include "UnityCG.cginc"              
  4.         #pragma target 3.0  
  5.         struct vertOut {    
  6.             float4 pos:SV_POSITION;    
  7.             float4 srcPos;   
  8.         };  
  9.         vertOut vert(appdata_base v) {  
  10.             vertOut o;  
  11.             o.pos = mul (UNITY_MATRIX_MVP, v.vertex);  
  12.             o.srcPos = ComputeScreenPos(o.pos);  
  13.             return o;  
  14.         }  
  15.         fixed4 frag(vertOut i) : COLOR0 {  
  16.             fixed3 COLOR1 = fixed3(0.0,0.0,0.3);  
  17.             fixed3 COLOR2 = fixed3(0.5,0.0,0.0);  
  18.             float BLOCK_WIDTH = 0.03;  
  19.             float2 uv = (i.srcPos.xy/i.srcPos.w);  
  20.             // To create the BG pattern
  21.             fixed3 final_color = fixed3(1.0);  
  22.             fixed3 bg_color = fixed3(0.0);  
  23.             fixed3 wave_color = fixed3(0.0);  
  24.             float c1 = fmod(uv.x, 2.0* BLOCK_WIDTH);  
  25.             c1 = step(BLOCK_WIDTH, c1);  
  26.             float c2 = fmod(uv.y, 2.0* BLOCK_WIDTH);  
  27.             c2 = step(BLOCK_WIDTH, c2);  
  28.             bg_color = lerp(uv.x * COLOR1, uv.y * COLOR2, c1*c2);  
  29.             // TO create the waves 
  30.             float wave_width = 0.01;  
  31.             uv = -1.0 + 2.0*uv;  
  32.             uv.y += 0.1;  
  33.             for(float i=0.0; i<10.0; i++) {  
  34.                 uv.y += (0.07 * sin(uv.x + i/7.0 +  _Time.y));  
  35.                 wave_width = abs(1.0 / (150.0 * uv.y));  
  36.                 wave_color += fixed3(wave_width * 1.9, wave_width, wave_width * 1.5);  
  37.             }  
  38.             final_color = bg_color + wave_color;  
  39.             return fixed4(final_color, 1.0);  
  40.         }  
  41.     ENDCG    
  42.     SubShader {    
  43.         Pass {    
  44.             CGPROGRAM    
  45.             #pragma vertex vert  
  46.             #pragma fragment frag  
  47.             #pragma fragmentoption ARB_precision_hint_fastest   
  48.             ENDCG    
  49.         }    
  50.     }     
  51.     FallBack Off    
  52. }  

下面進行分析:

1. ComputeScreenPos的解析:

用於把三維的座標轉化為螢幕上的點。有兩種方式,請參考 官方例子

ComputeScreenPos在UnityCG.cginc檔案中定義如下:

  1. // Projected screen position helpers
  2. #define V2F_SCREEN_TYPE float4
  3. inline float4 ComputeScreenPos (float4 pos) {  
  4.     float4 o = pos * 0.5f;  
  5.     #if defined(UNITY_HALF_TEXEL_OFFSET)
  6.     o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w * _ScreenParams.zw;  
  7.     #else
  8.     o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w;  
  9.     #endif
  10.     #if defined(SHADER_API_FLASH)
  11.     o.xy *= unity_NPOTScale.xy;  
  12.     #endif
  13.     o.zw = pos.zw;  
  14.     return o;  
  15. }  

原理解析(待續)

2. 背景的繪製

2.1) fmod用於求餘數,比如fmod(1.5, 1.0) 返回0.5;

2.2) step用於大小的比較,step(a,x) :  0 if x<a; 1 if x>=a; 比如: step(1, 1.2), 返回1; step(1, 0.8) 返回0;

2.3) 結合fmod和step可以得到一個虛線的效果。 比如要得到虛線段長度為1的程式碼如下:

c1 = fmod(x, 2*width); c1=step(width,c1); //其中width為1

那麼如果x的範圍是[0,1),c1的值為0;範圍為[1,2),c1的值為1;2為一個週期;

那麼fmod起到了製作週期的作用,step計算週期內的0和1;

2.4)把2.3中的知識運用到2維,就可以計算出方塊。

lerp函式的用法:lerp( a , b ,f ), f為百分數(取值範圍[0,1]);如果f為0,則lerp返回a,f為1,則返回b。f為0到1之間,就返回a到b之間的值。

程式碼中的 lerp(uv.x * COLOR1, uv.y * COLOR2, c1*c2); 其中c1和c2的取值不是為1,就是為0,所以就可以變成網格的情況。 背景繪製如下:

3. 波紋的繪製

3.1 ) 座標的轉化

uv = -1.0 + 2.0*uv;  // 把uv的x和y軸壓縮到原來的0.5倍後,向右上角移動1個單位。

3.2 )  畫一條直線:

由於上面把y軸移動到螢幕的中心,所以螢幕的上半部分為正的,下半部分為負的,程式碼如下:

  1. wave_width = abs(1.0 / (50.0 * uv.y));  
  2. wave_color = fixed3(wave_width * 1.9, wave_width, wave_width * 1.5);  

其中50.0是用來控制線的寬度的(數值越大,線越細),效果如下:

3.3)把直線變為曲線,並使其動起來:

  1. uv.y += (0.07 * sin(uv.x*10 + _Time.y));  
  2. wave_width = abs(1.0 / (50.0 * uv.y));  
  3. wave_color = fixed3(wave_width * 1.9, wave_width, wave_width * 1.5);  

效果如下:

3.4)多畫幾條曲線,形成波浪:

  1. for(float i=0.0; i<10.0; i++) {  
  2.     uv.y += (0.07 * sin(uv.x + i/7.0 +  _Time.y));  
  3.     wave_width = abs(1.0 / (150.0 * uv.y));  
  4.     wave_color += fixed3(wave_width * 1.9, wave_width, wave_width * 1.5);  
  5. }  

最終效果請見文章開頭。

其實寫shader,很多時候都是要通過不斷地效果疊加並除錯來達到效果。