首页 > 学院 > 开发设计 > 正文

Unity材质清理器

2019-11-07 22:50:09
字体:
来源:转载
供稿:网友

  本文介绍一种快捷清理Unity材质球上关联的废弃贴图引用的方法,全程无痛方便,推荐大家在自己项目中做一次全局清理。

材质球的无用贴图残留是这样产生的:

第一步,创建一个带有多张贴图材质球资源 这里写图片描述

第二步,直接切换成一个使用更少贴图数量的shader,看上去这个材质球似乎清理了多余的贴图引用,但是实际上它这个机制隐藏了资源引用 这里写图片描述

第三步,选择材质球资源,点击Select Dependencies,显示所有依赖,果然原有的两张贴图资源还被引用着 这里写图片描述

  由上面的重现步骤来看,我们日常的美术制作流程是非常容易引入上述的冗余问题的,材质球shader的切换,并不会清理掉多余贴图槽位上的资源引用,导致资源被冗余包含到发布包中,白白浪费内存,浪费加载带宽。

  所以我们就非常有必要对项目中的所有材质文件做一次全局大扫除,达到优化资源的目的。我们仅仅需要一个简单的批处理脚本,该脚本会清理掉材质球上所有没有被shader引用到的贴图资源引用。

下面给出核心代码,使用了一个小技巧来保证材质资源引用正确,希望对大家有用
using System.Collections.Generic;using System.Linq;using System.Text;using System.IO;using System;using UnityEngine;using UnityEditor;static class MaterialCleaner { public static bool ClearMaterialAsset( Material m ) { if ( m == null ) { return false; } var path = AssetDatabase.GetAssetPath( m ); if ( String.IsNullOrEmpty( path ) ) { return false; } var deps = AssetDatabase.GetDependencies( new String[] { path } ); var deps_textures = deps.Where( s => IsTextureAsset( s ) ).ToList(); var used_textures = new HashSet<String>(); var shader = m.shader; var newMat = new Material( shader ); var c = ShaderUtil.GetPRopertyCount( shader ); for ( int i = 0; i < c; ++i ) { var type = ShaderUtil.GetPropertyType( shader, i ); var name = ShaderUtil.GetPropertyName( shader, i ); var value = m.GetProperty( i ); switch ( type ) { case ShaderUtil.ShaderPropertyType.Color: { newMat.SetColor( name, m.GetColor( name ) ); } break; case ShaderUtil.ShaderPropertyType.Float: { newMat.SetFloat( name, m.GetFloat( name ) ); } break; case ShaderUtil.ShaderPropertyType.Range: { newMat.SetFloat( name, ( float )value ); } break; case ShaderUtil.ShaderPropertyType.TexEnv: { newMat.SetTexture( name, ( Texture )value ); newMat.SetTextureOffset( name, m.GetTextureOffset( name ) ); newMat.SetTextureScale( name, m.GetTextureScale( name ) ); var tpath = AssetDatabase.GetAssetPath( ( Texture )value ); if ( !String.IsNullOrEmpty( tpath ) ) { used_textures.Add( tpath ); } } break; case ShaderUtil.ShaderPropertyType.Vector: { newMat.SetVector( name, ( Vector4 )value ); } break; } } bool rebuild = false; if ( used_textures.Count != deps_textures.Count ) { for ( int i = 0; i < deps_textures.Count; ++i ) { var _fn = deps_textures[ i ]; if ( !used_textures.Contains( _fn ) ) { rebuild = true; UnityEngine.Debug.LogWarning( String.Format( "unused texture: {0}", _fn ) ); } } } if ( !rebuild ) { if ( newMat != null ) { UnityEngine.Object.DestroyImmediate( newMat ); } return false; } String basePath; String fn; String ext; SplitFullFilename( path, out fn, out ext, out basePath ); var tempAssetPath = String.Format( "{0}{1}_temp.{2}", basePath, fn, ext ); var _test = AssetDatabase.LoadAllAssetsAtPath( tempAssetPath ); if ( _test != null ) { AssetDatabase.DeleteAsset( tempAssetPath ); } // create a new material to replace it latter AssetDatabase.CreateAsset( newMat, tempAssetPath ); Resources.UnloadAsset( newMat ); var tempAssetDataPath = String.Format( "{0}{1}_datatemp.bytes", basePath, fn, ext ); if ( File.Exists( tempAssetPath ) ) { // rename it to .bytes File.Copy( tempAssetPath, tempAssetDataPath, true ); // delete temp material AssetDatabase.DeleteAsset( tempAssetPath ); if ( File.Exists( tempAssetDataPath ) ) { // delete original material File.Delete( path ); // replace original material with .bytes file File.Copy( tempAssetDataPath, path, true ); // remove bytes file File.Delete( tempAssetDataPath ); AssetDatabase.Refresh(); // make sure the temp file has been removed correctly if ( File.Exists( tempAssetDataPath ) ) { UnityEngine.Debug.Log( String.Format( "AssetDatabase.DeleteAsset failed: {0}", tempAssetDataPath ) ); File.Delete( tempAssetDataPath ); AssetDatabase.Refresh(); return true; } } } return false; } static void SplitFilename( String qualifiedName, out String outBasename, out String outPath ) { String path = qualifiedName.Replace( '//', '/' ); int i = path.LastIndexOf( '/' ); if ( i == -1 ) { outPath = String.Empty; outBasename = qualifiedName; } else { outBasename = path.Substring( i + 1, path.Length - i - 1 ); outPath = path.Substring( 0, i + 1 ); } } static void SplitBaseFilename( String fullName, out String outBasename, out String outExtention ) { int i = fullName.LastIndexOf( '.' ); if ( i == -1 ) { outExtention = String.Empty; outBasename = fullName; } else { outExtention = fullName.Substring( i + 1 ); outBasename = fullName.Substring( 0, i ); } } static void SplitFullFilename( String qualifiedName, out String outBasename, out String outExtention, out String outPath ) { String fullName = String.Empty; SplitFilename( qualifiedName, out fullName, out outPath ); SplitBaseFilename( fullName, out outBasename, out outExtention ); } static object GetProperty( this Material material, int index ) { var name = ShaderUtil.GetPropertyName( material.shader, index ); var type = ShaderUtil.GetPropertyType( material.shader, index ); switch ( type ) { case ShaderUtil.ShaderPropertyType.Color: return material.GetColor( name ); case ShaderUtil.ShaderPropertyType.Vector: return material.GetVector( name ); case ShaderUtil.ShaderPropertyType.Range: case ShaderUtil.ShaderPropertyType.Float: return material.GetFloat( name ); case ShaderUtil.ShaderPropertyType.TexEnv: return material.GetTexture( name ); } return null; } static bool IsTextureAsset( String assetPath ) { var ext = Path.GetExtension( assetPath ).ToLower(); return ext == ".png" || ext == ".tga" || ext == ".jpg" || ext == ".bmp" || ext == ".PSD" || ext == ".dds" || ext == ".exr"; }}
上一篇:Behavio深入理解

下一篇:View的绘制

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表