转发请保持地址:http://www.jb51.cc/article/p-bzdgsshf-bap.html
这篇文章主要分析一个Shader,从而感受shader的魅力,并学习相关shader的函数的用法。
先看Shader运行的效果(随着时间移动的波浪,具体效果请参考https://www.shadertoy.com/view/4dsGzH):
下面是代码:
- Shader"shadertoy/Waves"{//seehttps://www.shadertoy.com/view/4dsGzH
- CGINCLUDE
- #include"UnityCG.cginc"
- #pragmatarget3.0
- structvertOut{
- float4pos:SV_POSITION;
- float4srcPos;
- };
- vertOutvert(appdata_basev){
- vertOuto;
- o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
- o.srcPos=ComputeScreenPos(o.pos);
- returno;
- }
- fixed4frag(vertOuti):COLOR0{
- fixed3COLOR1=fixed3(0.0,0.0,0.3);
- fixed3COLOR2=fixed3(0.5,0.0);
- floatBLOCK_WIDTH=0.03;
- float2uv=(i.srcPos.xy/i.srcPos.w);
- //TocreatetheBGpattern
- fixed3final_color=fixed3(1.0);
- fixed3bg_color=fixed3(0.0);
- fixed3wave_color=fixed3(0.0);
- floatc1=fmod(uv.x,2.0*BLOCK_WIDTH);
- c1=step(BLOCK_WIDTH,c1);
- floatc2=fmod(uv.y,108); color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> c2=step(BLOCK_WIDTH,c2);
- bg_color=lerp(uv.x*COLOR1,uv.y*COLOR2,c1*c2);
- //TOcreatethewaves
- floatwave_width=0.01;
- uv=-1.0+2.0*uv;
- uv.y+=0.1;
- for(floati=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;
- returnfixed4(final_color,1.0);
- ENDCG
- SubShader{
- Pass{
- CGPROGRAM
- #pragmavertexvert
- #pragmafragmentfrag
- #pragmafragmentoptionARB_precision_hint_fastest
- FallBackOff
- }
下面进行分析:
1.ComputeScreenPos的解析:
用于把三维的坐标转化为屏幕上的点。有两种方式,请参考官方例子
ComputeScreenPos在UnityCG.cginc文件中定义如下:
- //Projectedscreenpositionhelpers
- #defineV2F_SCREEN_TYPEfloat4
- inlinefloat4ComputeScreenPos(float4pos){
- float4o=pos*0.5f;
- #ifdefined(UNITY_HALF_TEXEL_OFFSET)
- o.xy=float2(o.x,o.y*_ProjectionParams.x)+o.w*_ScreenParams.zw;
- #else
- #endif
- #ifdefined(SHADER_API_FLASH)
- o.xy*=unity_NPOTScale.xy;
- o.zw=pos.zw;
- 原理解析(待续)
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,c1*c2); 其中c1和c2的取值不是为1,就是为0,所以就可以变成网格的情况。 背景绘制如下:
3. 波纹的绘制
3.1 ) 坐标的转化
uv = -1.0 + 2.0*uv; // 把uv的x和y轴压缩到原来的0.5倍后,向右上角移动1个单位。
3.2 ) 画一条直线:
由于上面把y轴移动到屏幕的中心,所以屏幕的上半部分为正的,下半部分为负的,代码如下:
wave_width=abs(1.0/(50.0*uv.y));
- wave_color=fixed3(wave_width*1.9,wave_width*1.5);
其中50.0是用来控制线的宽度的(数值越大,线越细),效果如下:
3.3)把直线变为曲线,并使其动起来:
uv.y+=(0.07*sin(uv.x*10+_Time.y));
3.4)多画几条曲线,形成波浪:
for(floati=0.0;i<10.0;i++){