介绍
Android 平台上使用的 ETC1 并不包含 Alpha 通道,因此如果想要平衡体积与质量,可以尝试将纹理拆分为两份:不包含 Alpha 通道的与包含 Alpha 通道的,然后在运行时通过 Shader 将两个纹理合并起来。
需求
- 支持处理场景、Prefab 中的材质
- 收集使用到的 Shader 以便后续改写处理
- 增加 Alpha 通道材质
- 自动替换材质的 Shader 并设置纹理字段
准备工作
需要将使用到的 Shader 编辑通道分离版本,即同时读取两张纹理显示。要注意的是需要尽量保持纹理属性名字一致,这样可以减少特殊情况处理。
处理思路
获取 Shader
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
private static void GetShadersInGameObject(GameObject go, List<string> list)
{
var renderers = go.GetComponentsInChildren<Renderer>(true);
foreach (var renderer in renderers)
{
if (renderer == null)
{
continue;
}
foreach (var material in renderer.sharedMaterials)
{
if (material == null || material.shader == null)
{
continue;
}
if (!list.Contains(material.shader.name))
{
list.Add(material.shader.name);
}
}
}
}
|
分离 Alpha 纹理
这里要注意,分离出来 Alpha 纹理需要继承原纹理的导入选项。
1
2
3
4
5
6
7
8
9
10
11
12
|
var originTextureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
var settings = originTextureImporter.GetPlatformTextureSettings("Android");
settings.textureCompression = TextureImporterCompression.Compressed;
originTextureImporter.SetPlatformTextureSettings(settings);
originTextureImporter.SaveAndReimport();
AssetDatabase.ImportAsset(alphaPath);
var textureImporter = AssetImporter.GetAtPath(alphaPath) as TextureImporter;
textureImporter.mipmapEnabled = false;
textureImporter.textureType = TextureImporterType.Default;
textureImporter.SetPlatformTextureSettings(settings);
textureImporter.SaveAndReimport();
|
遍历方法
由于需要修改 Renderer 的材质属性,因此按照 Renderer 逐个处理其材质即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
private static void ProcessScene()
{
var gos = SceneManager.GetActiveScene().GetRootGameObjects();
foreach (var go in gos)
{
Traverse(go.transform, ProcessTransform);
}
AssetDatabase.SaveAssets();
}
private static void Traverse(Transform t, System.Action<Transform> action)
{
action.Invoke(t);
for (int i = 0; i < t.childCount; i++)
{
Traverse(t.GetChild(i), action);
}
}
private static void ProcessTransform(Transform t)
{
var noRuntimeRenderer = t.GetComponent<NoRuntimeRenderer>();
if (noRuntimeRenderer != null)
{
return;
}
var renderers = t.GetComponents<Renderer>();
foreach (var renderer in renderers)
{
if (renderer == null)
{
Debug.LogError("Renderer error: " + t.name, t);
continue;
}
ProcessRenderer(renderer);
}
}
private static void ProcessRenderer(Renderer renderer)
{
if (renderer.sharedMaterials == null || renderer.sharedMaterials.Length == 0)
{
Debug.LogError("Renderer has no material: " + renderer.name, renderer);
return;
}
foreach (var material in renderer.sharedMaterials)
{
if (material == null || material.shader == null)
{
Debug.LogError("Material error: " + renderer.name, renderer);
continue;
}
switch (material.shader.name)
{
case "CustomShader":
ProcessMaterial(material, "_MainTex", renderer);
break;
default:
break;
}
}
}
|
替换 Shader
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
private static void ProcessMaterial(Material material, string mainTexName, Renderer renderer)
{
var texture = material.GetTexture(mainTexName) as Texture2D;
var alphaTexture = ProcessTexture(texture);
string dir = Path.GetDirectoryName(material.shader.name);
string name = Path.GetFileName(material.shader.name);
string alphaShaderName = dir + "_Alpha" + "/" + name;
Texture mainTexture = material.GetTexture(mainTexName);
Vector2 scale = material.GetTextureScale(mainTexName);
Vector2 offset = material.GetTextureOffset(mainTexName);
material.shader = Shader.Find(alphaShaderName);
SetTexture(material, "_MainTex", mainTexture, scale, offset);
SetTexture(material, "_AlphaTex", alphaTexture, scale, offset);
EditorUtility.SetDirty(material);
}
private static void SetTexture(Material material, string propertyName,
Texture texture, Vector2 scale, Vector2 offset)
{
material.SetTexture(propertyName, texture);
material.SetTextureScale(propertyName, scale);
material.SetTextureOffset(propertyName, offset);
}
|