参考了WIKI:https://en.wikibooks.org/wiki/GLSL_Programming/Unity/Many_Light_Sources
具体的可以到这个wiki上面去看,我这里只给结果。(写太多东西太麻烦。。。)
这是生成的Diffuse环境贴图:
这是生成的Specular环境贴图:
下面是代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public enum EnvMapType
{
Diffuse,
Specular,
}
public class BuildEnvMap
{
public static Cubemap originalCubeMap;
public static Cubemap filteredCubeMap;
public static string diffuseEnvMapPath = "Assets/Shader/PBR/Editor/DiffuseEnvMap.cubemap";
public static string specularEnvMapPath = "Assets/Shader/PBR/Editor/SpecularEnvMap.cubemap";
public static EnvMapType mapType;
[MenuItem("CubeMap/BuildDiffuseEnvMap")]
public static void BuildDiffuseEnvironmentMap()
{
mapType = EnvMapType.Diffuse;
originalCubeMap = Selection.activeObject as Cubemap;
if (originalCubeMap == null)
{
Debug.LogError ("没选中CubeMap吗???");
}
else
{
ComputeFilteredCubeMap ();
if (filteredCubeMap != null)
{
AssetDatabase.CreateAsset (filteredCubeMap, diffuseEnvMapPath);
Debug.Log("创建DiffuseEnvMap成功!!!");
}
else
{
Debug.LogError ("filteredCubeMap创建失败了?");
}
}
}
[MenuItem("CubeMap/BuildSpecularEnvMap")]
public static void BuildSpecularEnvironmentMap()
{
mapType = EnvMapType.Specular;
originalCubeMap = Selection.activeObject as Cubemap;
if (originalCubeMap == null)
{
Debug.LogError ("没选中CubeMap吗???");
}
else
{
ComputeFilteredCubeMap ();
if (filteredCubeMap != null)
{
AssetDatabase.CreateAsset (filteredCubeMap, specularEnvMapPath);
Debug.Log("创建SpecularEnvMap成功!!!");
}
else
{
Debug.LogError ("filteredCubeMap创建失败了?");
}
}
}
public static Cubemap ComputeFilteredCubeMap()
{
filteredCubeMap = new Cubemap(originalCubeMap.width,
originalCubeMap.format, true);
int filteredSize = filteredCubeMap.width;
int originalSize = originalCubeMap.width;
// Compute all texels of the diffuse environment cube map
// by itterating over all of them
for (int filteredFace = 0; filteredFace < 6; filteredFace++)
// the six sides of the cube
{
for (int filteredI = 0; filteredI < filteredSize; filteredI++)
{
for (int filteredJ = 0; filteredJ < filteredSize; filteredJ++)
{
Vector3 filteredDirection =
GetDirection(filteredFace,
filteredI, filteredJ, filteredSize).normalized;
float totalWeight = 0.0f;
Vector3 originalDirection;
Vector3 originalFaceDirection;
float weight;
Color filteredColor = new Color(0.0f, 0.0f, 0.0f);
// sum (i.e. integrate) the diffuse illumination
// by all texels in the original environment map
for (int originalFace = 0; originalFace < 6; originalFace++)
{
originalFaceDirection = GetDirection(
originalFace, 1, 1, 3).normalized;
//the normal vector of the face
for (int originalI = 0; originalI < originalSize; originalI++)
{
for (int originalJ = 0; originalJ < originalSize; originalJ++)
{
originalDirection = GetDirection(
originalFace, originalI,
originalJ, originalSize);
// direction to the texel
// (i.e. light source)
weight = 1.0f
/ originalDirection.sqrMagnitude;
// take smaller size of more
// distant texels into account
originalDirection =
originalDirection.normalized;
weight = weight * Vector3.Dot(
originalFaceDirection,
originalDirection);
// take tilt of texel compared
// to face into account
switch (mapType)
{
case EnvMapType.Diffuse:
weight = weight * Mathf.Max(0.0f,
Vector3.Dot(filteredDirection,
originalDirection));
break;
case EnvMapType.Specular:
weight = weight * Mathf.Pow(Mathf.Max(0.0f,
Vector3.Dot(filteredDirection,
originalDirection)), 50.0f);
break;
default:
break;
}
totalWeight = totalWeight + weight;
filteredColor = filteredColor + weight
* originalCubeMap.GetPixel(
(CubemapFace)originalFace,
originalI, originalJ);
}
}
}
filteredCubeMap.SetPixel(
(CubemapFace)filteredFace, filteredI,
filteredJ, filteredColor / totalWeight);
}
}
}
// Avoid seams between cube faces: average edge texels
// to the same color on each side of the seam
int maxI = filteredCubeMap.width - 1;
for (int i = 0; i < maxI; i++)
{
SetFaceAverage(ref filteredCubeMap,
0, i, 0, 2, maxI, maxI - i);
SetFaceAverage(ref filteredCubeMap,
0, 0, i, 4, maxI, i);
SetFaceAverage(ref filteredCubeMap,
0, i, maxI, 3, maxI, i);
SetFaceAverage(ref filteredCubeMap,
0, maxI, i, 5, 0, i);
SetFaceAverage(ref filteredCubeMap,
1, i, 0, 2, 0, i);
SetFaceAverage(ref filteredCubeMap,
1, 0, i, 5, maxI, i);
SetFaceAverage(ref filteredCubeMap,
1, i, maxI, 3, 0, maxI - i);
SetFaceAverage(ref filteredCubeMap,
1, maxI, i, 4, 0, i);
SetFaceAverage(ref filteredCubeMap,
2, i, 0, 5, maxI - i, 0);
SetFaceAverage(ref filteredCubeMap,
2, i, maxI, 4, i, 0);
SetFaceAverage(ref filteredCubeMap,
3, i, 0, 4, i, maxI);
SetFaceAverage(ref filteredCubeMap,
3, i, maxI, 5, maxI - i, maxI);
}
// Avoid seams between cube faces:
// average corner texels to the same color
// on all three faces meeting in one corner
SetCornerAverage(ref filteredCubeMap,
0, 0, 0, 2, maxI, maxI, 4, maxI, 0);
SetCornerAverage(ref filteredCubeMap,
0, maxI, 0, 2, maxI, 0, 5, 0, 0);
SetCornerAverage(ref filteredCubeMap,
0, 0, maxI, 3, maxI, 0, 4, maxI, maxI);
SetCornerAverage(ref filteredCubeMap,
0, maxI, maxI, 3, maxI, maxI, 5, 0, maxI);
SetCornerAverage(ref filteredCubeMap,
1, 0, 0, 2, 0, 0, 5, maxI, 0);
SetCornerAverage(ref filteredCubeMap,
1, maxI, 0, 2, 0, maxI, 4, 0, 0);
SetCornerAverage(ref filteredCubeMap,
1, 0, maxI, 3, 0, maxI, 5, maxI, maxI);
SetCornerAverage(ref filteredCubeMap,
1, maxI, maxI, 3, 0, 0, 4, 0, maxI);
filteredCubeMap.Apply(); //apply all SetPixel(..) commands
return filteredCubeMap;
}
public static void SetFaceAverage(ref Cubemap filteredCubeMap,
int a, int b, int c, int d, int e, int f)
{
Color average =
(filteredCubeMap.GetPixel((CubemapFace)a, b, c)
+ filteredCubeMap.GetPixel((CubemapFace)d, e, f)) / 2.0f;
filteredCubeMap.SetPixel((CubemapFace)a, b, c, average);
filteredCubeMap.SetPixel((CubemapFace)d, e, f, average);
}
public static void SetCornerAverage(ref Cubemap filteredCubeMap,
int a, int b, int c, int d, int e, int f, int g, int h, int i)
{
Color average =
(filteredCubeMap.GetPixel((CubemapFace)a, b, c)
+ filteredCubeMap.GetPixel((CubemapFace)d, e, f)
+ filteredCubeMap.GetPixel((CubemapFace)g, h, i)) / 3.0f;
filteredCubeMap.SetPixel((CubemapFace)a, b, c, average);
filteredCubeMap.SetPixel((CubemapFace)d, e, f, average);
filteredCubeMap.SetPixel((CubemapFace)g, h, i, average);
}
public static Vector3 GetDirection(int face, int i, int j, int size)
{
switch (face)
{
case 0:
return new Vector3(0.5f,
-((j + 0.5f) / size - 0.5f),
-((i + 0.5f) / size - 0.5f));
case 1:
return new Vector3(-0.5f,
-((j + 0.5f) / size - 0.5f),
((i + 0.5f) / size - 0.5f));
case 2:
return new Vector3(((i + 0.5f) / size - 0.5f),
0.5f, ((j + 0.5f) / size - 0.5f));
case 3:
return new Vector3(((i + 0.5f) / size - 0.5f),
-0.5f, -((j + 0.5f) / size - 0.5f));
case 4:
return new Vector3(((i + 0.5f) / size - 0.5f),
-((j + 0.5f) / size - 0.5f), 0.5f);
case 5:
return new Vector3(-((i + 0.5f) / size - 0.5f),
-((j + 0.5f) / size - 0.5f), -0.5f);
default:
return Vector3.zero;
}
}
}