最近学习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的代码
基础知识
- 圆的参数方程为:x = rcosA,y = rcosB(原点在圆心)。
- 点A到点B的距离为10,那么点B到点A的距离也为10。
我们平时要绘制一个圆的时候,首先拿到的点是圆心,然后根据圆心去确定圆周。但是在Shader中,我们拿到的点是圆周上的点,所以没办法根据圆心去确定圆周。但是根据上面的结论,我们通过判断点是否在圆周上也是可行的。 - ComputeScreenPos:能够将顶点从投影坐标系变换到视觉坐标系(一般情况下这两个坐标系是重合的)
- _Time:为Unity内部定义的float4变量,y分量对应着时间,x分量对应着20分之一时间
- ScreenParams:x是摄像机渲染物体的像素宽度,y是摄像机渲染物体的像素高度,详情可以查看Unity Manual中Built-in shader variables
代码详解
- float an = 2 * pi * float(j) / index;计算每一个点的圆心角
- 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 - col = max(col, tex2D(_MainTex,
center + pointPos * 25 / _ScreenParams.xy).xyz);
在tex2D中,我们以当前点为圆心,取得周围点的颜色值,然后再跟默认纹理的颜色进行比较,假如tex2D寻址到的颜色值比较大,说明我们找到了圆心(除了圆心,图片上其它颜色都是黑色)。那么就获取圆心的颜色。center为视觉坐标系中纹理的UV值,pointPos*25/_ScreenParams.xy为我们最终的半径的大小。 - 最后将捕获到的颜色值返回,实际上就是圆心的颜色值。
总结
- 最重要的是能够转换思维,理解AB的长度等于BA的长度。代码上从圆心处开始思考。
- 下载链接http://yunpan.cn/cdvx9LPYiKUgU 访问密码 0a8f