三角函数的天下

最近学习Shader,在网上膜拜了几位大神后,搞了一个简单例程——三角函数的天下,效果如下,希望大家喜欢。

首先,当然是准备好我们的Shader与Material,不知道如何准备的可以在CSDN查看浅墨或者在新浪博客查看风宇冲的博客。Shader的代码如下

Shader "followDreamLgx/Trigonometric function" 
{
    Properties 
    {   
        _MainTex ("Base (RGB)", 2D) = "white" {}  
        _A ("振幅(最大和最小的幅度)", Range(0, 2)) = 1  
        _W ("角速度(圈数)", Range(0, 20)) = 15   
        _K ("偏距(整体大小)", Range(0, 3)) = 1  
    }   
       
    SubShader {      
        Tags {"Queue" = "Transparent"}        
        ZWrite Off  
  
        Pass 
        {       
            CGPROGRAM       
            #pragma vertex vert  
            #pragma fragment frag       
            #include "UnityCG.cginc"  
            #pragma target 3.0     
         
            sampler2D _MainTex;   
            float _A;  
            float _W;  
            float _K;  
           
            struct v2f
            {       
                float4 pos:SV_POSITION;       
                float4 srcPos : TEXCOORD0;    
            };     
     
            v2f vert(appdata_base v) 
            {     
                v2f o;     
                o.pos = mul (UNITY_MATRIX_MVP, v.vertex);     
                // 根据当前顶点计算在屏幕上的位置  
                o.srcPos = ComputeScreenPos(o.pos);    
                return o;     
            }     
     
            fixed4 frag(v2f i) : COLOR0 
            {   
                // 当前顶点在屏幕上的位置  
                float2 center = (i.srcPos.xy/i.srcPos.w);    //纹理在视觉坐标系中的坐标
                float3 col = (0,0,0);                                  //最终输出牙呢RGB值
                  int index = 150;                                        //将圆进行等分的份数(我们实际上是在画圆,只不过这是一个变形的圆)
                  
                for(int j = 0; j < index; j ++)  
                {  
                    // 求每个点的角度【将圆形变成30边形,计算每个顶点的角度】  
                    float pi = 3.14;
                    float an = 2 * pi * float(j) / index;  
                    // 求每个点坐标,再乘以波值 
                    float2 pointPos = float2(cos(an), sin(an))  * (_A +   _K * cos(_W * an)) ; 
                    // 在UV值域范围上半径大小为【50/_ScreenParams.xy】  
                    // 所以新的UV偏移量为【center + pointPos * 单位半径】  
                    col = max(col, tex2D(_MainTex, center + pointPos * 25 / _ScreenParams.xy).xyz);  
                }  
                return fixed4(col, 1);   
            }    
            ENDCG    
        }//end pass  
    }// end subshader  
    FallBack Off     
} 

直接看我们的重点,FragmentShader的代码

基础知识

  1. 圆的参数方程为:x = rcosA,y = rcosB(原点在圆心)。
  2. 点A到点B的距离为10,那么点B到点A的距离也为10。
    我们平时要绘制一个圆的时候,首先拿到的点是圆心,然后根据圆心去确定圆周。但是在Shader中,我们拿到的点是圆周上的点,所以没办法根据圆心去确定圆周。但是根据上面的结论,我们通过判断点是否在圆周上也是可行的。
  3. ComputeScreenPos:能够将顶点从投影坐标系变换到视觉坐标系(一般情况下这两个坐标系是重合的)
  4. _Time:为Unity内部定义的float4变量,y分量对应着时间,x分量对应着20分之一时间
  5. ScreenParams:x是摄像机渲染物体的像素宽度,y是摄像机渲染物体的像素高度,详情可以查看Unity Manual中Built-in shader variables

代码详解

  1. float an = 2 * pi * float(j) / index;计算每一个点的圆心角
  2. float pointPos = float2(cos(an),sin(an)) * (_A + _K * cos(_W * an))
    float(cos(an),sin(an))计算出了当前要绘制点的位置(圆的参数方程,半径为1)
    (_A + _K * cos(_W * an))为圆的半径,由于cos的存在,半径一直在改变。先抛开cos(_W * an),也就是将圆的半径缩放了(_A + _K)倍。而添加了cos(_W * an),我们知道三角函数是周期函数,抛开_W,那么an在循环结束的时候,就会对应着一个周期,取值遍布[-1,1]。假如这个时候_K = 1,_A = 1那么(_A + _K * cos(_W * an))的取值为[0,2],0对应着中心部分,2代表着最大的圆的半径。如图1.观察可以发现,有一部分凹进去了,那部分就是半径为0的部分。而假如_W取其它的值,比如3,那么整个图案就会有3个周期。如图2.假如我们将_K的取值改为3,那么就会得到图3的效果。
             
                                 图1                                                                        图2                                                                          图3  
  3. col = max(col, tex2D(_MainTex, center + pointPos * 25 / _ScreenParams.xy).xyz);
    在tex2D中,我们以当前点为圆心,取得周围点的颜色值,然后再跟默认纹理的颜色进行比较,假如tex2D寻址到的颜色值比较大,说明我们找到了圆心(除了圆心,图片上其它颜色都是黑色)。那么就获取圆心的颜色。center为视觉坐标系中纹理的UV值,pointPos*25/_ScreenParams.xy为我们最终的半径的大小。
  4. 最后将捕获到的颜色值返回,实际上就是圆心的颜色值。

总结

  1. 最重要的是能够转换思维,理解AB的长度等于BA的长度。代码上从圆心处开始思考。
  2. 下载链接http://yunpan.cn/cdvx9LPYiKUgU  访问密码 0a8f
原文地址:https://www.cnblogs.com/suimeng/p/4727981.html