弹孔,血迹 等受击表现

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坐标






posted @ 2017-12-13 16:31  Hichy  阅读(1293)  评论(0编辑  收藏  举报