• 弹孔,血迹 等受击表现


    1 最简单的 在碰撞点新建一个片,用来绘制弹孔
    2 投影,侧面会被拉的变形很严重
    3 但是最大的问题是当受击物体移动,播动画时,弹孔还留在原地
       需要保持弹孔位置,最终考虑把弹孔信息保存在顶点数据里,比如
        uv   主贴图
        uv2 lightmap
        uv3 弹孔贴图

    最终结果

    根据碰撞点计算uv的算法:
        1 使用Unity的用Transform计算坐标系转换
        2 命中时,设置好计算弹孔的Transform
        3 循环每个顶点,把顶点坐标转到碰撞的坐标系,x,y即是弹孔的uv值
        4 shader中简单的混合原贴图与受击贴图

    杂项记录:
        1. mesh的read/write要打开
        2. 受击贴图的边缘要是完全透明的,wrap mode要选择clamp
        3. SkinnedMeshRenderer的mesh有两种一个是sharedMesh(mesh的原始数据),一个使用BakeMesh函数取出来的蒙皮以后的数据
        4. Transform组件的各种转换函数 
                TransformPoint 把本地坐标转为世界坐标
                InverseTransformPoint 把世界坐标转为本地坐标
                localToWorldMatrix worldToLocalMatrix 转化矩阵 用Matrix4x4.MultiplyPoint(pos) 矩阵转换

    扩展思考:
        1. 多个弹孔重叠问题,用的是顶点的uv信息存的,两个受击点重叠是,数据会相互覆盖
            一个uv存两个(float4),最多叠加4个,位置差距较大的存在同一组uv,(可以用顶点颜色,float4的uv信息等存储)
        2. 弹孔uv的生成是没有用到z的信息的,受击贴图是会被拉伸的
            最好能像模型贴uv一样考虑模型的形状 严格贴合,计算的时候要考虑三角形什么的么

    代码:
    C#
    1
    173
    1
    173
     
    1
    using System.Collections;
    2
    using System.Collections.Generic;
    3
    using UnityEngine;
    4
    5
    public class HitMaskTest : MonoBehaviour
    6
    {
    7
        public float maskThickness = 2;  //厚度
    8
        public float maskMaxCos = 0.8f;  //夹角
    9
    10
        //Mask索引
    11
        public int maskIdx = 0;
    12
    13
        //Mesh信息
    14
        Mesh mesh;
    15
        List<Vector3> meshVertices = new List<Vector3>();
    16
        List<Vector3> meshNormals = new List<Vector3>();
    17
        List<Vector4> meshUv2 = new List<Vector4>(); //0 1 2 的2
    18
    19
        //skinMesh 特殊处理
    20
        SkinnedMeshRenderer skinMesh;
    21
        Mesh bakeMesh;
    22
    23
        //用Transform计算坐标系转换
    24
        private static Transform _hitTrans;
    25
        private static Transform hitTrans
    26
        {
    27
            get
    28
            {
    29
                if (_hitTrans == null)
    30
                {
    31
                    //计算贴花的坐标
    32
                    GameObject hitTransObj = new GameObject("hitTransObj");
    33
                    hitTransObj.transform.localScale = Vector3.one * 0.3f;
    34
                    _hitTrans = hitTransObj.transform;
    35
                }
    36
    37
                return _hitTrans;
    38
            }
    39
        }
    40
    41
        private void Awake()
    42
        {
    43
            var meshFilter = GetComponent<MeshFilter>();
    44
            if (meshFilter != null)
    45
            {
    46
                mesh = meshFilter.mesh;
    47
            }
    48
            else
    49
            {
    50
                skinMesh = GetComponent<SkinnedMeshRenderer>();
    51
                bakeMesh = new Mesh();
    52
                mesh = skinMesh.sharedMesh;
    53
            }
    54
    55
            //默认都在弹孔的边缘
    56
            for (int i = 0; i < mesh.vertexCount; i++)
    57
            {
    58
                meshUv2.Add(Vector4.one);
    59
            }
    60
            mesh.SetUVs(2, meshUv2);
    61
        }
    62
    63
        public void SetBulletMask(Vector3 hitPos, Quaternion qu)
    64
        {
    65
            //最多两个 存在uv3上
    66
            maskIdx++;
    67
            maskIdx = maskIdx % 2;
    68
    69
            hitTrans.position = hitPos;
    70
            hitTrans.rotation = qu;
    71
    72
            //mesh数据
    73
            Mesh countMesh = mesh;
    74
            if (skinMesh != null)
    75
            {
    76
                skinMesh.BakeMesh(bakeMesh);
    77
                countMesh = bakeMesh;
    78
            }
    79
            countMesh.GetVertices(meshVertices);
    80
            countMesh.GetNormals(meshNormals);
    81
    82
            //测试输出
    83
            //ShowVertices("bakeMesh", true);
    84
    85
            //逐顶点计算
    86
            float minZ = float.MaxValue;
    87
            Matrix4x4 m2h = hitTrans.worldToLocalMatrix * transform.localToWorldMatrix;
    88
    89
            for (int i = 0; i < meshUv2.Count; i++)
    90
            {
    91
                //夹角判断
    92
                float cos = Vector3.Dot(hitTrans.forward, meshNormals[i]);
    93
                if (cos >= maskMaxCos)
    94
                {
    95
                    meshVertices[i] = Vector3.one;
    96
                    continue;
    97
                }
    98
    99
                //坐标转换
    100
                meshVertices[i] = m2h.MultiplyPoint(meshVertices[i]);
    101
                if (meshVertices[i].z < minZ)
    102
                {
    103
                    minZ = meshVertices[i].z;
    104
                }
    105
            }
    106
    107
            for (int i = 0; i < meshUv2.Count; i++)
    108
            {
    109
                if (meshVertices[i].z - minZ <= maskThickness)
    110
                {
    111
                    SaveMaskUv(i, meshVertices[i].x + 0.5f, meshVertices[i].y + 0.5f);
    112
                }
    113
                else
    114
                {
    115
                    SaveMaskUv(i, 1, 1);
    116
                }
    117
            }
    118
    119
            //写入原本mesh
    120
            mesh.SetUVs(2, meshUv2);
    121
        }
    122
    123
        void SaveMaskUv(int idx, float u, float v)
    124
        {
    125
            Vector4 uv = meshUv2[idx];
    126
            switch (maskIdx)
    127
            {
    128
                case 0:
    129
                    uv.x = u;
    130
                    uv.y = v;
    131
                    break;
    132
                case 1:
    133
                    uv.z = u;
    134
                    uv.w = v;
    135
                    break;
    136
                default:
    137
                    break;
    138
            }
    139
    140
            meshUv2[idx] = uv;
    141
        }
    142
    143
        //鼠标点击测试
    144
        private void Update()
    145
        {
    146
            if (Input.GetKeyDown(KeyCode.Mouse0))
    147
            {
    148
                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    149
                RaycastHit hit;
    150
                if (Physics.Raycast(ray, out hit))
    151
                {
    152
                    SetBulletMask(hit.point, Camera.main.transform.rotation);
    153
                }
    154
            }
    155
        }
    156
    157
        private void ShowVertices(string szName, bool needTrans)
    158
        {
    159
            GameObject showObj = new GameObject(szName);
    160
            for (int i = 0; i < meshVertices.Count; i++)
    161
            {
    162
                GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
    163
                if (needTrans)
    164
                    obj.transform.position = transform.TransformPoint(meshVertices[i]);
    165
                else
    166
                    obj.transform.position = meshVertices[i];
    167
    168
                obj.transform.localScale = Vector3.one * 0.03f;
    169
                obj.transform.parent = showObj.transform;
    170
            }
    171
        }
    172
    }
    173

    Shader
    主要一个简单的混合 
     
    1
    //hit Mask
    2
    fixed4 hit = tex2D(_HitTex, i.hit.xy);
    3
    col.rgb = col.rgb * (1-hit.a) + hit.rgb * hit.a;
    4
    5
    hit = tex2D(_HitTex, i.hit.zw);
    6
    col.rgb = col.rgb * (1 - hit.a) + hit.rgb * hit.a;
    其中hit是C#计算的uv3坐标






  • 相关阅读:
    求素数(定义法,埃式法,欧拉法)
    打表法
    python学习日记(匿名函数)
    python学习日记(编码再回顾)
    python学习日记(文件操作练习题)
    python学习日记(迭代器、生成器)-乱七八糟
    python学习日记(生成器函数进阶)
    python学习日记(装饰器的补充)
    python学习日记(函数--装饰器)
    python学习日记(函数进阶)
  • 原文地址:https://www.cnblogs.com/Hichy/p/8033591.html
Copyright © 2020-2023  润新知