【OpenGL】Shaderインスタンス分析(一)-Wave


転送先:http://blog.csdn.net/stalendp/article/details/21993227
この文章は主に1つのShaderを分析して、それによってshaderの魅力を感じて、そして関連するshaderの関数の使い方を学びます.
まず、Shaderの実行の効果を見てみましょう.

コードは次のとおりです.
Shader "shadertoy/Waves" {  //see https://www.shadertoy.com/view/4dsGzH

	CGINCLUDE  

		#include "UnityCG.cginc"              
		#pragma target 3.0  
		struct vertOut {  
			float4 pos:SV_POSITION;  
			float4 srcPos; 
		};

		vertOut vert(appdata_base v) {
			vertOut o;
			o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
			o.srcPos = ComputeScreenPos(o.pos);
			return o;
		}

		fixed4 frag(vertOut i) : COLOR0 {

			fixed3 COLOR1 = fixed3(0.0,0.0,0.3);
			fixed3 COLOR2 = fixed3(0.5,0.0,0.0);
			float BLOCK_WIDTH = 0.03;

			float2 uv = (i.srcPos.xy/i.srcPos.w);

			// To create the BG pattern
			fixed3 final_color = fixed3(1.0);
			fixed3 bg_color = fixed3(0.0);
			fixed3 wave_color = fixed3(0.0);

			float c1 = fmod(uv.x, 2.0* BLOCK_WIDTH);
			c1 = step(BLOCK_WIDTH, c1);
			float c2 = fmod(uv.y, 2.0* BLOCK_WIDTH);
			c2 = step(BLOCK_WIDTH, c2);
			bg_color = lerp(uv.x * COLOR1, uv.y * COLOR2, c1*c2);

			// TO create the waves 
			float wave_width = 0.01;
			uv = -1.0 + 2.0*uv;
			uv.y += 0.1;
			for(float i=0.0; i<10.0; i++) {
				uv.y += (0.07 * sin(uv.x + i/7.0 +  _Time.y));
				wave_width = abs(1.0 / (150.0 * uv.y));
				wave_color += fixed3(wave_width * 1.9, wave_width, wave_width * 1.5);
			}
			final_color = bg_color + wave_color;

			return fixed4(final_color, 1.0);
		}

	ENDCG  

	SubShader {  
		Pass {  
			CGPROGRAM  

			#pragma vertex vert  
			#pragma fragment frag  
			#pragma fragmentoption ARB_precision_hint_fastest   

			ENDCG  
		}  

	}   
	FallBack Off  
}

次の分析を行います.
1.ComputeScreenPosの解析:
3 Dの座標を画面上の点に変換します.2つの方法がありますので、公式の例を参照してください
ComputeScreenPosはUnityCGにあります.cgincファイルでは、次のように定義されています.
// Projected screen position helpers
#define V2F_SCREEN_TYPE float4
inline float4 ComputeScreenPos (float4 pos) {
	float4 o = pos * 0.5f;
	#if defined(UNITY_HALF_TEXEL_OFFSET)
	o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w * _ScreenParams.zw;
	#else
	o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w;
	#endif
	
	#if defined(SHADER_API_FLASH)
	o.xy *= unity_NPOTScale.xy;
	#endif
	
	o.zw = pos.zw;
	return o;
}
原理解析(続き)
2.背景の描画
2.1)fmodは、例えば、fmod(1.5,1.0)が0.5を返すような残数を求めるために使用される.
2.2)stepはサイズの比較に用いられ、step(a,x):0 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〕であれば、c 1の値は0である.範囲は[1,2)、c 1の値は1、2は1周期である.
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*COLOR 1,uv.y*COLOR 2,c 1*c 2);ここで,c 1とc 2の値は1ではなく0であるため,メッシュとなる場合がある.背景は次のように描画されます.

3.波紋の描画
3.1)座標の変換
uv = -1.0 + 2.0*uv;//元のuvを拡張してシフトし、新しいuvを得る.我々の操作は新しいuvで行い、最終的に表示されると元のuvにマッピングされますので、下図を参照してください.
【OpenGL】Shader实例分析(一)-Wave_第1张图片
3.2)直線を描く:
上はy軸をスクリーンの中心に移動するので、スクリーンの上半分は正で、下半分は負で、コードは以下の通りです.
wave_width = abs(1.0 / (50.0 * uv.y));
wave_color = fixed3(wave_width * 1.9, wave_width, wave_width * 1.5);
のうち50.0は線の幅を制御するために使用され(数値が大きいほど線が細い)、効果は以下の通りです.

3.3)直線を曲線に変えて、それを動かす:
uv.y += (0.07 * sin(uv.x*10 + _Time.y));
wave_width = abs(1.0 / (50.0 * uv.y));
wave_color = fixed3(wave_width * 1.9, wave_width, wave_width * 1.5);
の効果は次のとおりです.

3.4)複数の曲線を描き、波を形成する:
for(float i=0.0; i<10.0; i++) {
	uv.y += (0.07 * sin(uv.x + i/7.0 +  _Time.y));
	wave_width = abs(1.0 / (150.0 * uv.y));
	wave_color += fixed3(wave_width * 1.9, wave_width, wave_width * 1.5);
}
最終効果は文章の冒頭を参照してください.
実はshaderと書いて、効果を重ねてデバッグすることで効果を達成することが多いです.