【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拉长放到扫描范围内 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值