您当前的位置: 首页 >  unity

Jave.Lin

暂无认证

  • 3浏览

    0关注

    704博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Unity Shader - GrabPass 实现武器热扭曲拖尾效果

Jave.Lin 发布时间:2020-03-06 13:19:44 ,浏览量:3

文章目录
  • 先来看看效果
  • 实现思路
  • Unity带的TrailRender组件
  • 编写脚本实现
    • CSharp
    • Shader
  • 参数
  • 注意性能
  • 还可以优化
  • 总结
  • Project
以前龙之谷喜欢选战士,帅气。 战士的武器在甩动过程中会有扭曲拖尾。 自己测试项目中想给武器也添加这效果,所以顺便学习以下。

先来看看效果

在这里插入图片描述

在这里插入图片描述

还可以给拖尾添加着色、亮度、和一些扭曲强度的参数设置。

实现思路
  • 按三个挂点的坐标来生成网格。
  • 但生成网格是需要判断上次分段的位置,与现在移动位置差距是否到达某个值而生成一段网格。
  • 生成分段是,设置好分段对应的颜色:RGB控制头尾颜色,A控制扭曲强度与颜色alpha
  • 在Shader中,先通过GrabPass获取ColorBuffer的内容,然后在frag shader中使用noiseTex来偏移uv,使用偏移后的UV来采样ColorBuffer的内容即可到达扭曲效果。
Unity带的TrailRender组件

在此前,我用过unity的TrailRenderer 在这里插入图片描述

但是对我来说不好用,因为我需要将拖尾的头部可以与我的武器的位置吻合,如果用TrailRenderer挂到武器上,就算怎么调好起始对好的位置,运行后,你就会发现各种对不准,因为TrailRenderer是只一个坐标为对准拖尾头部的。

看看unity自带的制作效果 在这里插入图片描述

这效果不理想啊,所以啊,没办法下,就自己动手写个实时生成拖尾网格的脚本。

编写脚本实现

只要对齐三个坐标位置即可,原来只要两个坐标的,但是不好做拖尾边缘平滑过渡处理,所以调整为三个坐标。

生成网格,就是根据三个点,移动超过一定距离,就记录三个点的位置到一个段列表中。

段列表:每一段数据都记录点,与过去时间的信息。

    // 拖尾段数据
    public class TrailSegment
    {
        public Vector3 pos1;        // 三个坐标记录
        public Vector3 pos2;
        public Vector3 pos3;
        public float distortion1;   // 三个坐标扭曲程度
        public float distortion2;
        public float distortion3;
        public float elapsedTime;   // 保持不变时长,记录:已用时(秒)
        public float fadeTime;      // 当elapsedTime >= duration 时,将开始记录fade time,就是淡出的时间已用时(秒)
    }

然后update中遍历位置段列表的每一项。

根据每一段的前后数据,生成网格。

再根据时间更新网格位置,颜色,等参数即可。

线框模式查看过程 在这里插入图片描述

放慢一些时间,方便观察 在这里插入图片描述

CSharp
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
#endif

/// 
/// authro      :   jave.lin
/// date        :   2020.03.06
/// 拖尾脚本
/// 建议放在一个空的GO上去使用,并且这个GO不会再调整Transform,不然会影响到MeshFilter的transform,
/// 如果你要放到一个容器下面,就不许保证这个容器不会有transform变化
/// (递归的父级都不能有改变,所以最好放在一个TrailRoot的容器,就好了,不要修改它的Transform,然后所有的TrailScript附加到的GO,都放它下面)
/// 
public class TrailScript : MonoBehaviour
{
    // 拖尾段数据
    public class TrailSegment
    {
        public Vector3 pos1;        // 三个坐标记录
        public Vector3 pos2;
        public Vector3 pos3;
        public float distortion1;   // 三个坐标扭曲程度
        public float distortion2;
        public float distortion3;
        public float elapsedTime;   // 保持不变时长,记录:已用时(秒)
        public float fadeTime;      // 当elapsedTime >= duration 时,将开始记录fade time,就是淡出的时间已用时(秒)
    }

#if UNITY_EDITOR
    public bool debug;
    [Space(10)]
#endif

    public Transform trans1;                    // 拖尾头的三个挂点
    public Transform trans2;
    public Transform trans3;

    public float duration = 1;                  // 拖尾段保持不变的时长(秒)
    public float fadeOut = 1;                   // 拖尾段保持不变时长到时后,开始淡出的时长(秒)
    public Color startColor;                    // 拖尾头部的颜色
    public Color endColor;                      // 拖尾尾部颜色

    public Material mat;                        // 拖尾的材质

    public bool emit;                           // 是否发射拖尾
    public float emitDistance = 0.1f;           // 出发生成拖尾分段的最小距离

    private List segmentList;     // 扭曲分段的列表

    private Vector3[] vertics;                  // 顶点的 world space position
    private int[] indices;                      // 顶点的 索引
    //private Vector2[] uvs;
    private Color[] colors;                     // 顶点的颜色

    private MeshRenderer meshRender;
    private MeshFilter meshFilter;

    private Mesh mesh;

    private Vector3 lastPos1;
    private Vector3 lastPos2;
    private Vector3 lastPos3;
    private float lastTime;

    private void Start()
    {
        segmentList = new List();
        mesh = new Mesh();
        meshRender = gameObject.AddComponent();
        meshFilter = gameObject.AddComponent();
        meshFilter.mesh = mesh;
        meshRender.material = mat;
        mesh.MarkDynamic(); // 文档说是底层会加速处理那些频繁更新网格信息时使用 https://docs.unity3d.com/ScriptReference/Mesh.MarkDynamic.html

        lastPos1 = trans1.position;
        lastPos2 = trans2.position;
        lastPos3 = trans3.position;
    }

    private void Update()
    {
        if (emitDistance  emitDistance) ||
                (!IsZero(deltaPos2) && deltaPos2.magnitude > emitDistance) ||
                (!IsZero(deltaPos3) && deltaPos3.magnitude > emitDistance))
            {
                float invEmitDistance = emitDistance == 0 ? 0 : 1f / emitDistance;
                segmentList.Add(new TrailSegment { 
                    pos1 = trans1.position, pos2 = trans2.position, pos3 = trans3.position,
                    distortion1 = deltaPos1.magnitude * invEmitDistance,
                    distortion2 = deltaPos2.magnitude * invEmitDistance,
                    distortion3 = deltaPos3.magnitude * invEmitDistance,
                });
                lastPos1 = trans1.position;
                lastPos2 = trans2.position;
                lastPos3 = trans3.position;
            }
        }

        var count = segmentList.Count;
        var offset = 0;
        TrailSegment curSeg = null;
        TrailSegment nextSeg = null;

        if (segmentList.Count > 1)
        {
            // 更新追后一个段的位置为:当前最新的拖尾头的位置
            var lastOne = segmentList[segmentList.Count - 1];
            lastOne.pos1 = trans1.position;
            lastOne.pos2 = trans2.position;
            lastOne.pos3 = trans3.position;
        }

        if (segmentList.Count > 0)
        {
            vertics = new Vector3[count * 3];               // 顶点
            indices = new int[(count) * (4 * 3)];           // 索引
            //uvs = new Vector2[count * 3];                 // uv,暂时不用
            colors = new Color[count * 3];                  // 颜色

            var w2lMatrix = transform.worldToLocalMatrix;   // 世界坐标转本地坐标的矩阵

            do
            {
                curSeg = segmentList[offset];

                var etT = duration == 0 ? 0 : Mathf.Clamp01(curSeg.elapsedTime / duration);
                var fadeT = fadeOut == 0 ? 0 : Mathf.Clamp01(curSeg.fadeTime / fadeOut);
                var reverseFadeT = 1 - fadeT;

                // 要靠近的左边
                var closeToPos = (offset             
关注
打赏
1664331872
查看更多评论
0.0654s