在很多情况下,我们为了提高渲染效率,一般都会让美术同学在制作场景时,设置场景相关节点的lightmap static属性,提前给整个场景烘培出静态的光照贴图lightmap,烘培的数据保存在场景目录下的LightmapSnapshot文件中,主要包括的数据有:
lightmaps:烘培出的光照贴图数组;
gameobject uid:被烘培的gameobject的唯一标识;
renderer的lightmapIndex:被烘培的gameobject的renderer组件所指向的光照贴图数组的索引;
renderer的lightmapScaleOffset:被烘培的gameobject的renderer组件所指向的光照贴图用于采样的区域坐标和宽高;
这个文件目前没有相关api读写,如果你想烘培完场景之后,把场景里面的gameobjet抽出来做成prefab,等切换完场景之后再用于动态加载是不可行的,因为抽出来的prefab在Instantiate之后将会是一个新的gameobject,uid自然和LightmapSnapshot文件里面记录的不一样,导致找不到对应的光照数据而造成模型没光照变暗或渲染错乱。
还有一种比较常见的需求是,在游戏运行时,通过更换光照贴图数据,营造场景在不同时间或季节的光照氛围,例如白天和黑夜等。
so,就算场景烘培完之后,我们还是要“动”它。
做法大概是,既然LightmapSnapshot文件我们不能动,那就把上面提到的光照数据保存到我们可以控制的文件里面,例如prefab。
首先,给场景根节点挂一个自定义组件,用于保存烘培出的光照贴图数组和烘培模式:
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
70
71
72
73
|
using
UnityEngine
;
using
System
.
Collections
;
[
ExecuteInEditMode
]
public
class
SceneLightMapSetting
:
MonoBehaviour
{
public
Texture2D
[
]
lightmapFar
,
lightmapNear
;
public
LightmapsMode
mode
;
public
void
SaveSettings
(
)
{
mode
=
LightmapSettings
.
lightmapsMode
;
lightmapFar
=
null
;
lightmapNear
=
null
;
if
(
LightmapSettings
.
lightmaps
!=
null
&&
LightmapSettings
.
lightmaps
.
Length
>
0
)
{
int
l
=
LightmapSettings
.
lightmaps
.
Length
;
lightmapFar
=
new
Texture2D
[
l
]
;
lightmapNear
=
new
Texture2D
[
l
]
;
for
(
int
i
=
0
;
i
<
l
;
i
++
)
{
lightmapFar
[
i
]
=
LightmapSettings
.
lightmaps
[
i
]
.
lightmapFar
;
lightmapNear
[
i
]
=
LightmapSettings
.
lightmaps
[
i
]
.
lightmapNear
;
}
}
RendererLightMapSetting
[
]
savers
=
Transform
.
FindObjectsOfType
<
RendererLightMapSetting
>
(
)
;
foreach
(
RendererLightMapSetting
s
in
savers
)
{
s
.
SaveSettings
(
)
;
}
}
public
void
LoadSettings
(
)
{
LightmapSettings
.
lightmapsMode
=
mode
;
int
l1
=
(
lightmapFar
==
null
)
?
0
:
lightmapFar
.
Length
;
int
l2
=
(
lightmapNear
==
null
)
?
0
:
lightmapNear
.
Length
;
int
l
=
(
l1
<
l2
)
?
l2
:
l1
;
LightmapData
[
]
lightmaps
=
null
;
if
(
l
>
0
)
{
lightmaps
=
new
LightmapData
[
l
]
;
for
(
int
i
=
0
;
i
<
l
;
i
++
)
{
lightmaps
[
i
]
=
new
LightmapData
(
)
;
if
(
i
<
l1
)
lightmaps
[
i
]
.
lightmapFar
=
lightmapFar
[
i
]
;
if
(
i
<
l2
)
lightmaps
[
i
]
.
lightmapNear
=
lightmapNear
[
i
]
;
}
LightmapSettings
.
lightmaps
=
lightmaps
;
}
}
void
OnEnable
(
)
{
#if UNITY_EDITOR
UnityEditor
.
Lightmapping
.
completed
+=
SaveSettings
;
#endif
}
void
OnDisable
(
)
{
#if UNITY_EDITOR
UnityEditor
.
Lightmapping
.
completed
-=
SaveSettings
;
#endif
}
void
Awake
(
)
{
if
(
Application
.
isPlaying
)
{
LoadSettings
(
)
;
}
}
}
|
再给场景里面被烘培的gameobject挂一组件,用于保存光照贴图数组的索引和光照贴图的区域坐标和宽高数据:
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
|
using
UnityEngine
;
using
System
.
Collections
;
[
ExecuteInEditMode
]
public
class
RendererLightMapSetting
:
MonoBehaviour
{
public
int
lightmapIndex
;
public
Vector4
lightmapScaleOffset
;
public
void
SaveSettings
(
)
{
if
(
!
IsLightMapGo
(
gameObject
)
)
{
return
;
}
Renderer
renderer
=
GetComponent
<
Renderer
>
(
)
;
lightmapIndex
=
renderer
.
lightmapIndex
;
lightmapScaleOffset
=
renderer
.
lightmapScaleOffset
;
}
public
void
LoadSettings
(
)
{
if
(
!
IsLightMapGo
(
gameObject
)
)
{
return
;
}
Renderer
renderer
=
GetComponent
<
Renderer
>
(
)
;
renderer
.
lightmapIndex
=
lightmapIndex
;
renderer
.
lightmapScaleOffset
=
lightmapScaleOffset
;
}
public
static
bool
IsLightMapGo
(
GameObject
go
)
{
if
(
go
==
null
)
{
return
false
;
}
Renderer
renderer
=
go
.
GetComponent
<
Renderer
>
(
)
;
if
(
renderer
==
null
)
{
return
false
;
}
return
true
;
}
void
Awake
(
)
{
if
(
Application
.
isPlaying
)
{
LoadSettings
(
)
;
}
}
}
|
如果手动挂上面两个脚本的话,繁琐且容易出错,so,写工具接口,用工具扫场景挂脚本:
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
70
71
72
73
74
75
76
|
using
UnityEngine
;
using
UnityEditor
;
using
System
.
IO
;
using
System
.
Text
;
using
System
.
Linq
;
using
System
.
Collections
;
using
System
.
Collections
.
Generic
;
using
System
;
public
class
SceneTools
{
static
string
[
]
_sNeedAssetType
=
new
string
[
1
]
{
".unity"
,
}
;
[
MenuItem
(
"场景相关/1. 保存场景光照贴图信息"
,
false
,
111
)
]
public
static
void
SaveSceneMapLightSetting
(
)
{
UnityEngine
.
Object
[
]
selObjs
=
Selection
.
GetFiltered
(
typeof
(
UnityEngine
.
Object
)
,
SelectionMode
.
DeepAssets
)
;
if
(
selObjs
==
null
||
selObjs
.
Length
==
0
)
{
Debug
.
LogError
(
"请到\"Scenes\" 目录下选中需要保存场景光照贴图信息的场景!"
)
;
return
;
}
string
assetPath
;
UnityEngine
.
Object
assetObj
=
null
;
GameObject
gameObj
=
null
;
SceneLightMapSetting
slms
=
null
;
RendererLightMapSetting
rlms
=
null
;
bool
needSave
=
false
;
for
(
int
i
=
0
;
i
<
selObjs
.
Length
;
++
i
)
{
needSave
=
false
;
assetPath
=
AssetDatabase
.
GetAssetPath
(
selObjs
[
i
]
)
;
foreach
(
string
extName
in
_sNeedAssetType
)
{
if
(
assetPath
.
EndsWith
(
extName
)
)
{
needSave
=
true
;
break
;
}
}
if
(
!
needSave
)
continue
;
assetObj
=
AssetDatabase
.
LoadAssetAtPath
(
assetPath
,
typeof
(
UnityEngine
.
Object
)
)
as
UnityEngine
.
Object
;
EditorApplication
.
OpenScene
(
assetPath
)
;
gameObj
=
GameObject
.
Find
(
"scene_root"
)
;
if
(
gameObj
==
null
)
{
Debug
.
LogError
(
"不合法的场景:场景没有scene_root根节点!!"
)
;
continue
;
}
slms
=
gameObj
.
GetComponent
<
SceneLightMapSetting
>
(
)
;
if
(
slms
==
null
)
{
slms
=
gameObj
.
AddComponent
<
SceneLightMapSetting
>
(
)
;
}
Renderer
[
]
savers
=
Transform
.
FindObjectsOfType
<
Renderer
>
(
)
;
foreach
(
Renderer
s
in
savers
)
{
if
(
s
.
lightmapIndex
!=
-
1
)
{
rlms
=
s
.
gameObject
.
GetComponent
<
RendererLightMapSetting
>
(
)
;
if
(
rlms
==
null
)
{
rlms
=
s
.
gameObject
.
AddComponent
<
RendererLightMapSetting
>
(
)
;
}
}
}
slms
.
SaveSettings
(
)
;
EditorApplication
.
SaveScene
(
)
;
Debug
.
Log
(
string
.
Format
(
"场景{0}的光照贴图信息保存完成"
,
assetObj
.
name
)
)
;
}
EditorApplication
.
SaveAssets
(
)
;
}
}
|
没截图从觉得少点什么,来,上图,先打开一个被烘培过的场景,场景根节点为scene_root,被烘培的gameobjet是地板、一颗小草、带logo的立方体:
然后使用工具给场景里面的相关节点挂脚本存数据:
把场景根节点scene_root拖出来做成prefab,然后删掉场景里面的scene_root,再把scene_root.prefab拖进场景里面:
此时模型是丢失了光照数据的,原因截图已说明。接着运行场景:
模型的光照又回来啦,原因截图已说明。