Unity烘焙材质到贴图脚本
这个脚本可以将复杂的材质,比如有法线贴图的材质进行"烘焙",转变为单一的贴图,可用来将Unity的游戏移植到移动平台时候使用.
讲脚本放Editor文件夹里,使用时选择一个Material材质,然后在菜单种"Custom/Bake Material"打开并调整照明和其他参数,点击Bake按钮,就会生成一个单一的贴图.
脚本:BakeMaterial.js
js 代码
讲脚本放Editor文件夹里,使用时选择一个Material材质,然后在菜单种"Custom/Bake Material"打开并调整照明和其他参数,点击Bake按钮,就会生成一个单一的贴图.
脚本:BakeMaterial.js

001 |
class BakeMaterialSettings |
002 |
{ |
003 |
private static var kEditorPrefsName = "BakeMaterialSettings" ; |
004 |
|
005 |
static var kBakingLayerShouldBeUnusedInScene = 30; |
006 |
static var kStandardTexNames = new Array ( "_MainTex" , "_BumpMap" , "_Detail" , "_ParallaxMap" , "_Parallax" ); |
007 |
008 |
var bakeAlpha = false ; |
009 |
var bakeMainTexAsWhite = false ; |
010 |
var minTextureResolution = 8; |
011 |
var maxTextureResolution = 2048; |
012 |
013 |
var emptyScene = false ; |
014 |
var useCustomLights = false ; |
015 |
var ambient = Color.black; |
016 |
|
017 |
static var kLights = 3; |
018 |
var enableLight = new boolean[kLights]; |
019 |
var colorLight = new Color[kLights]; |
020 |
var dirLight = new Vector2[kLights]; |
021 |
|
022 |
function BakeMaterialSettings () |
023 |
{ |
024 |
Load (); |
025 |
} |
026 |
|
027 |
function Load () |
028 |
{ |
029 |
bakeAlpha = EditorPrefs.GetBool(kEditorPrefsName + ".bakeAlpha" ); |
030 |
bakeMainTexAsWhite = EditorPrefs.GetBool(kEditorPrefsName + ".bakeMainTexAsWhite" ); |
031 |
minTextureResolution = EditorPrefs.GetInt(kEditorPrefsName + ".minTextureResolution" , 8); |
032 |
maxTextureResolution = EditorPrefs.GetInt(kEditorPrefsName + ".maxTextureResolution" , 2048); |
033 |
034 |
emptyScene = EditorPrefs.GetBool(kEditorPrefsName + ".emptyScene" ); |
035 |
useCustomLights = EditorPrefs.GetBool(kEditorPrefsName + ".useCustomLights" ); |
036 |
ambient.r = EditorPrefs.GetFloat(kEditorPrefsName + ".ambient.r" ); |
037 |
ambient.g = EditorPrefs.GetFloat(kEditorPrefsName + ".ambient.g" ); |
038 |
ambient.b = EditorPrefs.GetFloat(kEditorPrefsName + ".ambient.b" ); |
039 |
ambient.a = EditorPrefs.GetFloat(kEditorPrefsName + ".ambient.a" , 1.0f); |
040 |
|
041 |
for ( var q = 0; q < kLights; ++q) |
042 |
{ |
043 |
enableLight[q] = EditorPrefs.GetBool(kEditorPrefsName + ".enableLight" + q); |
044 |
colorLight[q].r = EditorPrefs.GetFloat(kEditorPrefsName + ".color.r" + q, 0.5f); |
045 |
colorLight[q].g = EditorPrefs.GetFloat(kEditorPrefsName + ".color.g" + q, 0.5f); |
046 |
colorLight[q].b = EditorPrefs.GetFloat(kEditorPrefsName + ".color.b" + q, 0.5f); |
047 |
colorLight[q].a = EditorPrefs.GetFloat(kEditorPrefsName + ".color.a" + q, 1.0f); |
048 |
dirLight[q].x = EditorPrefs.GetFloat(kEditorPrefsName + ".dir.x" + q); |
049 |
dirLight[q].y = EditorPrefs.GetFloat(kEditorPrefsName + ".dir.y" + q); |
050 |
} |
051 |
} |
052 |
|
053 |
function Save () |
054 |
{ |
055 |
EditorPrefs.SetBool(kEditorPrefsName + ".bakeAlpha" , bakeAlpha); |
056 |
EditorPrefs.SetBool(kEditorPrefsName + ".bakeMainTexAsWhite" , bakeMainTexAsWhite); |
057 |
EditorPrefs.SetInt(kEditorPrefsName + ".minTextureResolution" , minTextureResolution); |
058 |
EditorPrefs.SetInt(kEditorPrefsName + ".maxTextureResolution" , maxTextureResolution); |
059 |
060 |
EditorPrefs.GetBool(kEditorPrefsName + ".emptyScene" , emptyScene); |
061 |
EditorPrefs.SetBool(kEditorPrefsName + ".useCustomLights" , useCustomLights); |
062 |
EditorPrefs.SetFloat(kEditorPrefsName + ".ambient.r" , ambient.r); |
063 |
EditorPrefs.SetFloat(kEditorPrefsName + ".ambient.g" , ambient.g); |
064 |
EditorPrefs.SetFloat(kEditorPrefsName + ".ambient.b" , ambient.b); |
065 |
EditorPrefs.SetFloat(kEditorPrefsName + ".ambient.a" , ambient.a); |
066 |
067 |
for ( var q = 0; q < kLights; ++q) |
068 |
{ |
069 |
EditorPrefs.SetBool(kEditorPrefsName + ".enableLight" + q, enableLight[q]); |
070 |
EditorPrefs.SetFloat(kEditorPrefsName + ".color.r" + q, colorLight[q].r); |
071 |
EditorPrefs.SetFloat(kEditorPrefsName + ".color.g" + q, colorLight[q].g); |
072 |
EditorPrefs.SetFloat(kEditorPrefsName + ".color.b" + q, colorLight[q].b); |
073 |
EditorPrefs.SetFloat(kEditorPrefsName + ".color.a" + q, colorLight[q].a); |
074 |
EditorPrefs.SetFloat(kEditorPrefsName + ".dir.x" + q, dirLight[q].x); |
075 |
EditorPrefs.SetFloat(kEditorPrefsName + ".dir.y" + q, dirLight[q].y); |
076 |
} |
077 |
} |
078 |
} |
079 |
080 |
class BakeMaterial extends EditorWindow |
081 |
{ |
082 |
private static var kMateriBakeNodeName = "__MateriaBakeSetup" ; |
083 |
private static var kWindowMinSize = Vector2 (300, 386); |
084 |
|
085 |
private static var settings : BakeMaterialSettings; |
086 |
private static var visible : boolean = false ; |
087 |
|
088 |
private var camera : GameObject; |
089 |
private var plane : GameObject; |
090 |
private var previewTexture : Texture; |
091 |
private var lights : GameObject[] = new GameObject[BakeMaterialSettings.kLights]; |
092 |
private var stateChanged = false ; |
093 |
|
094 |
private var texViewScrollPosition = Vector2.zero; |
095 |
private var lastMaterial : Material; |
096 |
|
097 |
private var originalScene = "" ; |
098 |
|
099 |
private var scheduleBakeOnNextUpdate = false ; |
100 |
101 |
|
102 |
private function SetupScene () |
103 |
{ |
104 |
DestroyScene (); |
105 |
var oldGo = GameObject.Find(kMateriBakeNodeName); |
106 |
if (oldGo) |
107 |
DestroyImmediate (oldGo); |
108 |
camera = new GameObject (kMateriBakeNodeName, Camera); |
109 |
plane = GameObject.CreatePrimitive (PrimitiveType.Plane); |
110 |
111 |
var cam = camera; |
112 |
cam.camera.backgroundColor = Color.black; |
113 |
cam.camera.clearFlags = CameraClearFlags.SolidColor; |
114 |
cam.camera.orthographic = true ; |
115 |
cam.camera.orthographicSize = 5.0; |
116 |
cam.camera.cullingMask = 1 << settings.kBakingLayerShouldBeUnusedInScene; |
117 |
|
118 |
plane.transform.parent = cam.transform; |
119 |
plane.transform.position = Vector3.forward * 10.0; |
120 |
plane.transform.rotation = Quaternion.Euler (0, 0, 180) * Quaternion.Euler (-90, 0, 0); |
121 |
plane.layer = settings.kBakingLayerShouldBeUnusedInScene; |
122 |
|
123 |
for ( var l in lights) |
124 |
{ |
125 |
l = new GameObject ( "Light" , Light); |
126 |
l.light.type = LightType.Directional; |
127 |
l.light.cullingMask = 1 << settings.kBakingLayerShouldBeUnusedInScene; |
128 |
l.transform.parent = cam.transform; |
129 |
l.active = false ; |
130 |
} |
131 |
} |
132 |
|
133 |
private function UpdateScene (m : Material) |
134 |
{ |
135 |
for (q = 0; q < settings.kLights; ++q) |
136 |
{ |
137 |
lights[q].active = settings.useCustomLights & settings.enableLight[q]; |
138 |
lights[q].light.color = settings.colorLight[q]; |
139 |
lights[q].transform.rotation = |
140 |
Quaternion.AngleAxis(settings.dirLight[q].x, Vector3.up) * |
141 |
Quaternion.AngleAxis(settings.dirLight[q].y, Vector3.right); |
142 |
} |
143 |
|
144 |
if (settings.useCustomLights) |
145 |
RenderSettings.ambientLight = settings.ambient; |
146 |
else if (settings.emptyScene) |
147 |
RenderSettings.ambientLight = Color.white; |
148 |
|
149 |
plane.renderer.material = m; |
150 |
} |
151 |
|
152 |
private function DestroyScene () |
153 |
{ |
154 |
GameObject.DestroyImmediate (camera); |
155 |
GameObject.DestroyImmediate (plane); |
156 |
GameObject.DestroyImmediate (previewTexture); |
157 |
} |
158 |
159 |
function UpdateMaterialPreview (m : Material) : RenderTexture |
160 |
{ |
161 |
if (!m) |
162 |
return ; |
163 |
|
164 |
var saveAmbientLight = RenderSettings.ambientLight; |
165 |
var saveMainTexture = m.mainTexture; |
166 |
if (settings.bakeMainTexAsWhite) |
167 |
m.mainTexture = null ; |
168 |
|
169 |
|
170 |
// setup |
171 |
if (!camera) |
172 |
SetupScene (); |
173 |
camera.SetActiveRecursively( true ); |
174 |
UpdateScene (m); |
175 |
|
176 |
var res = FindLargestTextureResolution (plane.renderer.sharedMaterial, settings.minTextureResolution, settings.maxTextureResolution); |
177 |
var rt = RenderCameraToRenderTexture (camera.camera, res.x, res.y); |
178 |
|
179 |
// restore |
180 |
camera.SetActiveRecursively( false ); |
181 |
RenderSettings.ambientLight = saveAmbientLight; |
182 |
m.mainTexture = saveMainTexture; |
183 |
|
184 |
previewTexture = rt; |
185 |
return rt; |
186 |
} |
187 |
|
188 |
function CaptureMaterial(m : Material) |
189 |
{ |
190 |
var matAssetPath = AssetDatabase.GetAssetPath (m); |
191 |
var assetPath = System.IO.Path.Combine (System.IO.Path.GetDirectoryName (matAssetPath), System.IO.Path.GetFileNameWithoutExtension (matAssetPath)); |
192 |
193 |
var rt = UpdateMaterialPreview (m); |
194 |
RenderTextureToPNG (rt, settings.bakeAlpha, assetPath + ".png" ); |
195 |
} |
196 |
197 |
function OnEnable () |
198 |
{ |
199 |
if (!settings) |
200 |
settings = new BakeMaterialSettings (); |
201 |
SetupScene (); |
202 |
visible = true ; |
203 |
} |
204 |
|
205 |
function OnDisable () |
206 |
{ |
207 |
DestroyScene (); |
208 |
settings.Save (); |
209 |
visible = false ; |
210 |
} |
211 |
212 |
static function GetTargetMaterial () : Material |
213 |
{ |
214 |
return EditorUtility.InstanceIDToObject (Selection.activeInstanceID) as Material; |
215 |
} |
216 |
217 |
function OnSelectionChange () |
218 |
{ |
219 |
Repaint (); |
220 |
} |
221 |
222 |
function Update () |
223 |
{ |
224 |
var rebuildScene = false ; |
225 |
if (scheduleBakeOnNextUpdate) |
226 |
{ |
227 |
Bake (); |
228 |
scheduleBakeOnNextUpdate = false ; |
229 |
rebuildScene = true ; |
230 |
} |
231 |
|
232 |
if (originalScene == "" && EditorApplication.currentScene == "" ) |
233 |
settings.emptyScene = true ; |
234 |
|
235 |
if (settings.emptyScene && originalScene == "" && EditorApplication.currentScene != "" ) |
236 |
{ |
237 |
DestroyScene (); |
238 |
if (EditorApplication.SaveCurrentSceneIfUserWantsTo ()) |
239 |
{ |
240 |
originalScene = EditorApplication.currentScene; |
241 |
EditorApplication.NewScene (); |
242 |
} |
243 |
else |
244 |
settings.emptyScene = false ; |
245 |
rebuildScene = true ; |
246 |
} |
247 |
else if (!settings.emptyScene && originalScene != "" ) |
248 |
{ |
249 |
EditorApplication.OpenScene (originalScene); |
250 |
rebuildScene = true ; |
251 |
originalScene = "" ; |
252 |
} |
253 |
|
254 |
if (rebuildScene) |
255 |
{ |
256 |
SetupScene (); |
257 |
} |
258 |
|
259 |
if (rebuildScene || stateChanged || !settings.emptyScene) |
260 |
{ |
261 |
UpdateMaterialPreview (lastMaterial); |
262 |
Repaint (); |
263 |
stateChanged = false ; |
264 |
} |
265 |
} |
266 |
|
267 |
function OnGUI () |
268 |
{ |
269 |
var material = GetTargetMaterial (); |
270 |
if (lastMaterial != material) |
271 |
UpdateMaterialPreview (material); |
272 |
if (material) |
273 |
lastMaterial = material; |
274 |
|
275 |
EditorGUILayout.BeginHorizontal(); |
276 |
EditorGUILayout.BeginVertical(GUILayout.MaxWidth(200)); |
277 |
if (!(originalScene == "" && EditorApplication.currentScene == "" )) |
278 |
{ |
279 |
settings.emptyScene = !EditorGUILayout.BeginToggleGroup( "Scene ligthing" , !settings.emptyScene); |
280 |
EditorGUILayout.EndToggleGroup(); |
281 |
} |
282 |
settings.useCustomLights = EditorGUILayout.BeginToggleGroup( "Custom lighting" , settings.useCustomLights); |
283 |
if (settings.useCustomLights) |
284 |
{ |
285 |
EditorGUI.indentLevel = 1; |
286 |
settings.ambient = EditorGUILayout.ColorField( "Ambient" , settings.ambient); |
287 |
for ( var q = 0; q < settings.kLights; ++q) |
288 |
{ |
289 |
settings.enableLight[q] = EditorGUILayout.BeginToggleGroup( "Light" , settings.enableLight[q]); |
290 |
EditorGUI.indentLevel = 2; |
291 |
settings.colorLight[q] = EditorGUILayout.ColorField( "Color" , settings.colorLight[q]); |
292 |
settings.dirLight[q] = EditorGUILayout.Vector2Field( "Direction" , settings.dirLight[q]); |
293 |
EditorGUILayout.EndToggleGroup(); |
294 |
} |
295 |
} |
296 |
EditorGUI.indentLevel = 0; |
297 |
EditorGUILayout.EndToggleGroup(); |
298 |
|
299 |
settings.bakeAlpha = EditorGUILayout.Toggle( "Bake Alpha" , settings.bakeAlpha); |
300 |
settings.bakeMainTexAsWhite = !EditorGUILayout.Toggle( "MainTex" , !settings.bakeMainTexAsWhite); |
301 |
settings.minTextureResolution = EditorGUILayout.IntField( "Min Resolution" , settings.minTextureResolution); |
302 |
settings.maxTextureResolution = EditorGUILayout.IntField( "Max Resolution" , settings.maxTextureResolution); |
303 |
settings.minTextureResolution = Mathf.Max(2, settings.minTextureResolution); |
304 |
settings.maxTextureResolution = Mathf.Max(settings.minTextureResolution, settings.maxTextureResolution); |
305 |
306 |
EditorGUILayout.BeginHorizontal(); |
307 |
if (GUILayout.Button( "Bake" )) |
308 |
{ |
309 |
CaptureMaterial (lastMaterial); |
310 |
} |
311 |
if (GUILayout.Button( "Bake Selected" )) |
312 |
{ |
313 |
scheduleBakeOnNextUpdate = true ; |
314 |
} |
315 |
EditorGUILayout.EndHorizontal(); |
316 |
|
317 |
EditorGUILayout.EndVertical(); |
318 |
|
319 |
texViewScrollPosition = EditorGUILayout.BeginScrollView (texViewScrollPosition); |
320 |
var r = GUILayoutUtility.GetAspectRect(1.0f); |
321 |
if (previewTexture) |
322 |
EditorGUI.DrawPreviewTexture(r, previewTexture); |
323 |
EditorGUILayout.EndScrollView(); |
324 |
EditorGUILayout.EndHorizontal(); |
325 |
|
326 |
if (GUI.changed) |
327 |
{ |
328 |
stateChanged = true ; |
329 |
} |
330 |
} |
331 |
|
332 |
@MenuItem( "Custom/Bake Material ..." , false , 5) |
333 |
static function CreateBakeEditor() |
334 |
{ |
335 |
var window = EditorWindow.GetWindow(BakeMaterial); |
336 |
window.title = "Bake Material" ; |
337 |
window.minSize = kWindowMinSize; |
338 |
window.Show(); |
339 |
} |
340 |
341 |
@MenuItem( "Custom/Bake Selected Materials" , false , 4) |
342 |
static function Bake() |
343 |
{ |
344 |
var instanceIDs = Selection.instanceIDs; |
345 |
var currentScene = EditorApplication.currentScene; |
346 |
|
347 |
var wasAlreadyVisible = BakeMaterial.visible; |
348 |
var window = EditorWindow.GetWindow(BakeMaterial); |
349 |
|
350 |
if (window.settings.emptyScene) |
351 |
{ |
352 |
if (!EditorApplication.SaveCurrentSceneIfUserWantsTo ()) |
353 |
return ; |
354 |
EditorApplication.NewScene (); |
355 |
} |
356 |
|
357 |
window.SetupScene (); |
358 |
for ( var i in instanceIDs) |
359 |
{ |
360 |
var m : Material = EditorUtility.InstanceIDToObject (i) as Material; |
361 |
if (m) |
362 |
window.CaptureMaterial (m); |
363 |
} |
364 |
window.DestroyScene (); |
365 |
|
366 |
if (window.settings.emptyScene && currentScene) |
367 |
{ |
368 |
EditorApplication.OpenScene (currentScene); |
369 |
} |
370 |
|
371 |
if (!wasAlreadyVisible) |
372 |
window.Close (); |
373 |
} |
374 |
|
375 |
static function FindLargestTextureResolution (m : Material, minTexRes : int, maxTexRes : int) : Vector2 |
376 |
{ |
377 |
var res = Vector2 (minTexRes, minTexRes); |
378 |
for ( var n in BakeMaterialSettings.kStandardTexNames) |
379 |
{ |
380 |
if (!m.HasProperty (n)) |
381 |
continue ; |
382 |
|
383 |
var t : Texture = m.GetTexture (n); |
384 |
if (!t) |
385 |
continue ; |
386 |
|
387 |
res.x = Mathf.Max (res.x, t.width); |
388 |
res.y = Mathf.Max (res.y, t.height); |
389 |
} |
390 |
res.x = Mathf.Min (res.x, maxTexRes); |
391 |
res.y = Mathf.Min (res.y, maxTexRes); |
392 |
return res; |
393 |
} |
394 |
|
395 |
static function RenderCameraToRenderTexture (cam : Camera, width : int, height : int) : RenderTexture |
396 |
{ |
397 |
var rt = cam.camera.targetTexture; |
398 |
if (rt && rt.width != width && rt.height != height) |
399 |
DestroyImmediate(rt); |
400 |
if (!rt) |
401 |
rt = new RenderTexture (width, height, 24); |
402 |
cam.camera.targetTexture = rt; |
403 |
cam.camera.Render (); |
404 |
return rt; |
405 |
} |
406 |
|
407 |
static function RenderTextureToPNG (rt : RenderTexture, bakeAlpha : boolean, assetPath : String) |
408 |
{ |
409 |
RenderTexture.active = rt; |
410 |
|
411 |
var screenShot = new Texture2D (rt.width, rt.height, bakeAlpha? TextureFormat.ARGB32 : TextureFormat.RGB24, false ); |
412 |
screenShot.ReadPixels (Rect (0, 0, rt.width, rt.height), 0, 0); |
413 |
|
414 |
RenderTexture.active = null ; |
415 |
|
416 |
var bytes = screenShot.EncodeToPNG (); |
417 |
System.IO.File.WriteAllBytes (assetPath, bytes); |
418 |
|
419 |
AssetDatabase.ImportAsset (assetPath, ImportAssetOptions.ForceUpdate); |
420 |
} |
421 |
} |