弹孔,血迹 等受击表现
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坐标