Shader异形(不规则)视频播放

先解释一下标题,异形视频播放:不规则形状播放。先贴个图:

如图,背景图是正常的视频播放,三角形则是另一视频播放,不过是不规则的三角形。原理就是通过shader检测三个坐标点计算进行纹理的渲染,贴一下代码:

Shader代码:

Shader "Hidden/Triangle"
{
    Properties
    {
        _MainTex ("MainTex", 2D) = "white" {}
        _ViceTex ("ViceTex", 2D) = "white" {}
    }

    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue"="Transparent" }
        Cull Off ZWrite Off ZTest Always
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            sampler2D _MainTex;
            sampler2D _ViceTex;

            vector points[3];
            int pointNums = 0;

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

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

            float IsInSide(float3 uv,float3 point1,float3 point2,float3 point3)
            {
                //叉乘法:利用叉乘法判断一个点是否在三角形内
                float3 a = point1 - uv;
                float3 b = point2 - uv;
                float3 c = point3 - uv;

                float3 ab = cross(a,b);
                float3 bc = cross(b,c);
                float3 ca = cross(c,a);

                float d1 = dot(ab,bc);
                float d2 = dot(ab,ca);
                float d3 = dot(bc,ca);
                return (d1 > 0 && d2 > 0 && d3 > 0) || (d1 < 0 && d2 < 0 && d3 < 0);
            }

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

            fixed4 frag (v2f i) : SV_Target
            {
                float nums = step(3,pointNums);
                float4 main = tex2D(_MainTex, i.uv);
                float4 vice = tex2D(_ViceTex, i.uv);
                float isInSide = IsInSide(float3(i.uv,0),points[0],points[1],points[2]);
                //顶点小于3,return main;大于等于3 在判断是否在三角形内(isInSide),在 return main ,否 return vice
                fixed4 col = lerp(main,lerp(main,vice,isInSide),nums);
                return col;
            }
            ENDCG
        }
    }
}

C#代码:

using UnityEngine;

public class TriangleVideoPlayer : MonoBehaviour
{
    /// <summary>
    /// 三角形顶点
    /// </summary>
    private Vector4[] points;

    /// <summary>
    /// 材质球
    /// </summary>
    public Material material;

    /// <summary>
    /// 顶点数量
    /// </summary>
    private int nums;

    /// <summary>
    /// 圆心
    /// </summary>
    private Vector2 center;

    void Start()
    {
        this.points = new Vector4[3];
        this.center = Vector2.zero;
    }

    void Update()
    {
        this.UpdateParameter();
    }

    private void UpdateParameter()
    {
        if (Input.GetMouseButtonDown(0))
        {
            if (this.points[0] == null)
                this.points[0] = new Vector4(Input.mousePosition.x / Screen.width, Input.mousePosition.y / Screen.height, 0f, 0f);
            else if (this.points[1] == null)
                this.points[1] = new Vector4(Input.mousePosition.x / Screen.width, Input.mousePosition.y / Screen.height, 0f, 0f);
            else if (this.points[2] == null)
                this.points[2] = new Vector4(Input.mousePosition.x / Screen.width, Input.mousePosition.y / Screen.height, 0f, 0f);
            else
            {
                this.points[0] = this.points[1];
                this.points[1] = this.points[2];
                this.points[2] = new Vector4(Input.mousePosition.x / Screen.width, Input.mousePosition.y / Screen.height, 0f, 0f);
            }
            this.nums++;
            this.nums = Mathf.Clamp(this.nums, 0, 3);

            this.material.SetVectorArray("points", this.points);
            this.material.SetInt("pointNums", this.nums);
        }
    }
}

这里解释一下shader代码中的IsInSide这个函数,其实是利用了叉乘法判断一个点是否在三角形内,关于叉乘法请自行搜索,通过IsInSide这个函数能区分出那一部分需要渲染主视频另一部分渲染副视频,视频部分是这样设置的:

由于两个视频有音频输出,为了好玩,设置了一下左右声道,如图:

这里用了VideoPlayer组件进行的视频播放,当然我能也可以换个思路利用常见的圆形进行视频不规则播放:

原理很简单,直接看源码:

Shader代码:

Shader "Hidden/Circle"
{
    Properties
    {
        _MainTex ("MainTex", 2D) = "white" {}
        _ViceTex ("ViceTex", 2D) = "white" {}
        _CircleCenter("CircleCenter",Vector) = (0,0,0,0)
        _Radius("Radius",Range(0,1)) = 0
    }

    SubShader
    {
        Tags{ "RenderType" = "Transparent"  "Queue" = "Transparent" "IgnoreProjector" = "True" }

        Cull Off ZWrite On ZTest Less
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
			
            sampler2D _MainTex;
            sampler2D _ViceTex;
            float4 _CircleCenter;
            float _Radius;

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

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

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

            fixed4 frag (v2f i) : SV_Target
            {
                //偏移至中心
                float2 uv = i.uv - float2(_CircleCenter.x,_CircleCenter.y);
                //适配屏幕宽高
                uv *= float2(_ScreenParams.x / _ScreenParams.y,1);
                //计算距离
                float len = length(uv);
                float4 main = tex2D(_MainTex, i.uv);
                float4 vice = tex2D(_ViceTex,i.uv);
                return lerp(main,vice,step(len,_Radius));
            }
            ENDCG
        }
    }
}

C#代码:

using UnityEngine;

public class CircleVideoPlayer : MonoBehaviour
{
    /// <summary>
    /// 圆心
    /// </summary>
    private Vector4 circleCenter;

    /// <summary>
    /// 材质球
    /// </summary>
    private Material material;

    /// <summary>
    /// 半径
    /// </summary>
    [Range(0,0.5f)]
    public float radius;

    void Start()
    {
        this.circleCenter = Vector4.zero;
        this.material = this.GetComponent<MeshRenderer>().material;
        this.SetMaterialParameter();
    }

    void Update()
    {
        this.Check();
        this.SetMaterialParameter();
    }

    private void Check()
    {
        this.circleCenter = new Vector4(Input.mousePosition.x / Screen.width, Input.mousePosition.y / Screen.height, Input.mousePosition.z, 0);
        if (Input.GetAxis("Mouse ScrollWheel") < 0)
            this.radius -= Time.deltaTime;
        else if (Input.GetAxis("Mouse ScrollWheel") > 0)
            this.radius += Time.deltaTime;
        this.radius = Mathf.Clamp(this.radius, 0, 0.5f);
    }

    /// <summary>
    /// 设置材质球参数
    /// </summary>
    private void SetMaterialParameter()
    {
        this.material.SetVector("_CircleCenter", this.circleCenter);
        this.material.SetFloat("_Radius", this.radius);
    }
}

如果有需要的朋友,下载链接:

https://lanzous.com/b00nihunc
密码:9o4m

参考链接(这个是多边形):https://blog.csdn.net/zzlyw/article/details/53992048

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值