zoukankan      html  css  js  c++  java
  • 【OpenGL】Shader实例分析(一)-Wave

    转发请保持地址:http://blog.csdn.net/stalendp/article/details/21993227

    这篇文章主要分析一个Shader,从而感受shader的魅力,并学习相关shader的函数的用法。

    先看Shader运行的效果(随着时间移动的波浪,具体效果请参考 https://www.shadertoy.com/view/4dsGzH):

    下面是代码:

     

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

    下面进行分析:

    1. ComputeScreenPos的解析:

    用于把三维的坐标转化为屏幕上的点。有两种方式,请参考 官方例子

    ComputeScreenPos在UnityCG.cginc文件中定义如下:

     

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
    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.      
    11.     #if defined(SHADER_API_FLASH)  
    12.     o.xy *= unity_NPOTScale.xy;  
    13.     #endif  
    14.       
    15.     o.zw = pos.zw;  
    16.     return o;  
    17. }  

    原理解析(待续)

     

    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轴移动到屏幕的中心,所以屏幕的上半部分为正的,下半部分为负的,代码如下:

     

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
    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)把直线变为曲线,并使其动起来:

     

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
    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)多画几条曲线,形成波浪:

     

    [csharp] view plaincopy在CODE上查看代码片派生到我的代码片
    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,很多时候都是要通过不断地效果叠加并调试来达到效果。

  • 相关阅读:
    20182320《程序设计与数据结构》第八周学习总结
    20182320《程序设计与数据结构》第七周学习总结
    20182320 2019-2020-1 《数据结构与面向对象程序设计》实验6报告
    实验5
    20182320《程序设计与数据结构》第六周学习总结
    20182320 2019-2020-1 《数据结构与面向对象程序设计》第5周学习总结
    实验4
    20182320 2019-2020-1 《数据结构与面向对象程序设计》第4周学习总结
    实验3
    实验2报告
  • 原文地址:https://www.cnblogs.com/cooka/p/3673816.html
Copyright © 2011-2022 走看看