Unity实现手电筒效果

用Unity开发手电筒的效果,原理就是将背景图片放在最底层,在图片上面盖一层黑色遮罩,再用Shader在遮罩上开出来几个圆形透明的“孔”,后期可以用鼠标或者Kinect控制光束的位置,我的项目是用Kinect控制的,人体感应控制手电筒,最终实现了一个探索文化的效果。

在这里插入图片描述

源代码:

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

[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class DarkEffect : MonoBehaviour
{
    [System.Serializable]
    public class Item
    {
        [SerializeField]
        public Transform target;

        [SerializeField]
        public int radius;

        public Vector3 GetScreenPosition(Camera cam)
        {
            return cam.WorldToScreenPoint(target.position);
        }
    }

    //渐变像素数量
    public int _smoothLength = 20;
    //遮罩混合颜色
    public Color _darkColor = Color.black;
    //目标物体
    public List<Item> _items = new List<Item>();

    protected Material _mainMaterial;
    protected Camera _mainCamera;

    Vector4[] _itemDatas;
    Item _tmpItem;
    Vector4 _tmpVt;
    Vector3 _tmpPos;
    int _tmpScreenHeight;

    private void OnEnable()
    {
        _mainMaterial = new Material(Shader.Find("Peter/DarkEffect"));
        _mainCamera = GetComponent<Camera>();
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {

        if (_itemDatas == null || _itemDatas.Length != _items.Count)
        {
            _itemDatas = new Vector4[_items.Count];
        }

        _tmpScreenHeight = Screen.height;

        for (int i = 0; i < _items.Count; i++)
        {
            _tmpItem = _items[i];
            _tmpPos = _tmpItem.GetScreenPosition(_mainCamera);

            _tmpVt.x = _tmpPos.x;
            _tmpVt.y = _tmpScreenHeight - _tmpPos.y;
            _tmpVt.z = _tmpItem.radius;
            _tmpVt.w = 0;

            _itemDatas[i] = _tmpVt;
        }

        _mainMaterial.SetInt("_SmoothLength", _smoothLength);
        _mainMaterial.SetColor("_DarkColor", _darkColor);
        _mainMaterial.SetInt("_ItemCnt", _itemDatas.Length);
        _mainMaterial.SetVectorArray("_Item", _itemDatas);

        Graphics.Blit(source, destination, _mainMaterial);
    }
}

Shader:
Shader “Peter/DarkEffect”
{
Properties
{
_MainTex(“Texture”, 2D) = “white” {}
}

    SubShader
{
    // No culling or depth
    Cull Off ZWrite Off ZTest Always

    Pass
    {
     CGPROGRAM
     #pragma vertex vert
     #pragma fragment frag

     #include "UnityCG.cginc"

     //追踪物体最多个数
     #define ItemSize 9

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

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

     sampler2D _MainTex;

     fixed4 _DarkColor;
     float _SmoothLength;
     fixed _ItemCnt;
     float4 _Item[ItemSize];

     v2f vert(appdata v)
     {
      v2f o;
      o.vertex = UnityObjectToClipPos(v.vertex);
      o.uv = v.uv;
      return o;
     }

     fixed CalcAlpha(float4 vt, float4 pt)
     {
      if (pt.z < 0)
      {
       return 1;
      }

      float distPow2 = pow(vt.x - pt.x, 2) + pow(vt.y - pt.y, 2);
      float dist = (distPow2 > 0) ? sqrt(distPow2) : 0;

      float smoothLength = _SmoothLength;
      if (smoothLength < 0)
      {
       smoothLength = 0;
      }

      float maxValue = pt.z;
      float minValue = pt.z - smoothLength;
      if (minValue < 0)
      {
       minValue = 0;
       smoothLength = pt.z;
      }

      if (dist <= minValue)
      {
       return 0;
      }
      else if (dist > maxValue)
      {
       return 1;
      }

      fixed retVal = (dist - minValue) / smoothLength;

      return retVal;
     }

     fixed4 frag(v2f i) : SV_Target
     {
      fixed alphaVal = 1;
      fixed tmpVal = 1;

      for (fixed index = 0; index < _ItemCnt; ++index)
      {
       tmpVal = CalcAlpha(i.vertex, _Item[index]);
       if (tmpVal < alphaVal)
       {
        alphaVal = tmpVal;
       }
      }

      alphaVal *= _DarkColor.a;

      return tex2D(_MainTex, i.uv) * (1 - alphaVal) + _DarkColor * alphaVal;
     }

     ENDCG
    }
}

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小姑娘不爱写代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值