【Shader案例】2D掃描效果

目錄

蠻牛原文:http://www.manew.com/thread-143613-1-1.html 

製作過程視頻:https://www.bilibili.com/video/BV1Cf4y127ba

源碼資源:https://github.com/AMikeW/BStandShaderResources

一、效果圖 

二、實戰

1、製作Shader

2、創建Plane,賦予Shader所在的材質

3、創建C#腳本,掛在Plane物體身上

4、創建3個Cube拉長放到掃描範圍內 


蠻牛原文:http://www.manew.com/thread-143613-1-1.html 

製作過程視頻:https://www.bilibili.com/video/BV1Cf4y127ba

源碼資源:https://github.com/AMikeW/BStandShaderResources

一、效果圖 

二、實戰

1、製作Shader

Shader "Unlit/SaomiaoShader2"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_Color ("Color", Color) = (1,1,1,1)
		_StrengthFloat("_StrengthFloat", Float) = -0.5
		_AlphaDownFloat("_AlphaDownFloat", Range(0,3)) = 0.5
		_LerpFloat("_LerpFloat", Range(0,1)) = 0.8
		_Angle("_Angle", Float) = 45
		_SlerpFloat("_SlerpFloat", Range(1, 80)) = 20
	}
	SubShader
	{
		Blend SrcAlpha OneMinusSrcAlpha
		Tags { "RenderType"="Transparent" "Queue"="Transparent" }
		LOD 100

		Pass
		{
			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag			
			
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
				float3 normal : NORMAL;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;				
				float4 vertex : SV_POSITION;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed4 _Color;
			float _StrengthFloat;
			float _AlphaDownFloat;
			float _LerpFloat;
			float _Angle;
			float _SlerpFloat;		
			uniform float _FloatArray[256];
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);				
				float3 nor = UnityObjectToWorldNormal(v.normal);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{						
				fixed4 col;
				//1.UV座標偏移(UV中心點爲(0,0),左下角爲(-0.5,-0.5),右上角爲(0.5, 0.5))
				float2 uv = i.uv;
				uv.x = uv.x - 0.5;
				uv.y = uv.y - 0.5;

				//2.根據UV來計算出一個變化區域:一個以UV中心點爲中心,半徑爲0.5的圓形,圓形外到內從0漸變到1
				fixed refDis = 1.0 - sqrt(uv.x * uv.x + uv.y * uv.y) * 2;
				
				//3.中心點向周圍發射的向量(歸一化)
				fixed2 fragmentDir = normalize(uv.xy);

				//4.半圓弧(正X軸方向): 圓弧中心向兩旁的值從1逐漸變爲0,cos正好滿足
				fixed rightHalfCircle = clamp(dot(float2(1, 0), fragmentDir.xy), 0, 1);
					
				//5.漸變半圓弧顏色(內到外)
				col = lerp(_Color, fixed4(1, 1, 1, 1), refDis * _LerpFloat);

				////6_1.增強邊緣效果
				//fixed strengthF = pow(refDis, _StrengthFloat);
				//col.a = col.a * refDis * rightHalfCircle * strengthF;
							
				//7.衰減半圓弧兩旁,固定衰減25°角後的
				fixed tempAngleCos = cos(radians(_Angle)); //25度角的餘弦值 0.906 31 

				//6_2.增強邊緣效果 + 潤滑邊緣
				fixed strengthF = pow(refDis, _StrengthFloat);
				col.a = col.a * refDis * pow(rightHalfCircle / max(0.001, tempAngleCos), _SlerpFloat) * strengthF;
				
				//大於_Angle角度區域的像素衰減 (在視野之外的), _Angle其實是一個視野角度的一半.
				if (rightHalfCircle < tempAngleCos)
				{
					col.a *= _AlphaDownFloat;
				}
				else {
					//核心:視野角度內(_Angle, -Angle)範圍內 進行一個遮擋處理 (這裏就是所說的Shader弧度變化在(angle,-angle))
					
					//計算出index
					//fragmentDir.y是歸一化後的向量Y值
					//因 sqrt(fragmentDir.y^2 + fragmentDir.x^2) = 1
					// sin(fragmentDIr與UV正X軸(0.5,0.5)的角度弧度) 爲 (fragmentDir.y / 1) 即fragmentDir.y ,反過來說fragmentDir.y就是sin(角度)
					float curRad = asin(fragmentDir.y); //asin 反正弦函數(正弦值)獲取角度對應的弧度.
					curRad += radians(_Angle); //偏移到正數(上面的弧度是指(_Angle, Angle)角度的當前片元所在的角度弧度..有點繞
					float f = curRad / radians(_Angle * 2); //當前弧度/總弧度 得到一個係數
					float index = f * 256; //係數乘上索引最大值 獲取索引

					//因爲C#計算出的當index爲0時,應該是照射區域上方,而此時Shader,想象一下是不是0時爲上方。。。
					//答案不是,上方index爲0時,curRad是0,在沒有經過偏移時,它位於照射區域最下。所以應該取反索引 (這裏很繞但是要理解,它就是這樣子..)
					index = 256 - index;//日 我SB了

					float curFloat = _FloatArray[index];
					float curFragmentDistance = sqrt(uv.x * uv.x + uv.y * uv.y) * 2; //[0, 0.5]變爲[0,1] 因爲curFloat是[0,1]範圍的
					if (curFloat > 0 && curFragmentDistance > curFloat) {
						col *= 0;
					}
				}

				return col;
			}
			ENDCG
		}
	}
}

2、創建Plane,賦予Shader所在的材質

3、創建C#腳本,掛在Plane物體身上

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Saomiao2 : MonoBehaviour
{
    public Material mat;
    //旋轉速度
    public float speed;

    //角度(與Shader一樣,它只是視角角度的一半)
    public float angle;

    float[] arrayFloat;

    //射線長度
    public float rayLength;

    void Start()
    {
        arrayFloat = new float[256];
    }

    void Update()
    {
        transform.Rotate(transform.up, Time.deltaTime * speed);
        UpdateRay();
    }

    void UpdateRay()
    {
        //角度轉弧度
        float rad = Mathf.Deg2Rad * angle;
        //每一次射線遞增步長(一個弧度) //視角角度的弧度/256
        float step = rad * 2f / 256;
        int index = 0;
        for (int i = 1; i <= 256; i++)
        {
            //step * i 是視角範圍內的一個弧度變化 + 自身角度弧度進行旋轉 注意:要減去一半
            //step * i + Mathf.Deg2Rad * (transform.eulerAngles.y + 180) 這部分是 [0,angle*2] 度數的弧度變化
            //Shader裏是[-angle, angle]的變化
            float curRad = step * i + Mathf.Deg2Rad * (transform.eulerAngles.y + 180) - rad;
            //根據當前弧度計算出座標
            float x = rayLength * Mathf.Cos(-curRad);
            float z = rayLength * Mathf.Sin(-curRad);
            Vector3 pos = new Vector3(x, 0, z);            
            //發射射線
            Ray ray = new Ray(transform.position, pos);
            RaycastHit raycastHit;
            if(Physics.Raycast(ray, out raycastHit))
            {
                arrayFloat[index] = raycastHit.distance * 1.0f / rayLength; //射線碰到物體後拿到的一個射線到物體之間的距離
                Debug.DrawLine(transform.position, pos, Color.red);
            }
            else
            {
                arrayFloat[index] = -1;
                Debug.DrawLine(transform.position, pos);
            }
            index++;
            //此時index爲0時,射線是位於照射範圍的最上,index=255時,射線位於照射範圍的最下..(一定要理解這一點)
            //transform.eulerAngles.y是從0遞增,順時針旋轉,然而實際情況我們的射線會逆時針轉,所以在我取反了curRad進行獲取座標..
            //此時當index逐漸增大時,是從照射區域最上方漸變到最下方,到Shader我們就通過這個關係計算出index!            
        }
        mat.SetFloatArray("_FloatArray", arrayFloat);
    }
}

注意:角度要保持一樣

4、創建3個Cube拉長放到掃描範圍內 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章