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 : MonoBehaviour6
{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 的218
19
//skinMesh 特殊处理20
SkinnedMeshRenderer skinMesh;21
Mesh bakeMesh;22
23
//用Transform计算坐标系转换24
private static Transform _hitTrans;25
private static Transform hitTrans26
{27
get28
{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
else49
{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
else114
{115
SaveMaskUv(i, 1, 1);116
}117
}118
119
//写入原本mesh120
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
else166
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 Mask2
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坐标