本系列主要参考《Unity Shaders and Effects Cookbook》一书(感谢原书作者),同时会加上一点个人理解或拓展。
这里是本书所有的插图。这里是本书所需的代码和资源(当然你也可以从官网下载)。
========================================== 分割线 ==========================================
写在前面
我们已经学了很多关于反射的内容,但是我们现在的反射并不能实时反射,即当反射物体移动时它们不能正确反射周围的环境。例如,如果你有一个由很多房间和走廊组成的环境,我们不可能提前渲染所有的Cubemap然后放到一个Cubemap中。这意味着我们不能随着房间的移动而正确反射。我们得到的是一个静态的、令人乏味的反射效果。
有很多方法可以解决这个问题,即一个房间的反射不同于另一个房间的反射。一个最基本的方法就是根据在房间中的位置替换Cubemap。因此,当你从一个房间移动到另一个房间时,Cubemap应该被换成当前房间的Cubemap。第二种方法是当我们角色在环境中移动时,实时更新Cubemap,最终在游戏进行的每一帧得到一个新的Cubemap。尽管第二种方法听起来非常吸引人,但是它比较消耗性能,因此你需要在其他游戏资源之间进行权衡。
这一篇将会讲述第一种方法,并且向你展示如何搭建一个非常简单的系统来基于你在环境中的不同位置去替换两个Cubemaps。更多的关于实时反射系统的内容你可以在本节最后找到,因此如果你对实时反射感兴趣并且想要看看两种技术之间的差别,你可以在那里找到!
准备工作
- 我们需要创建一个新的场景、一个新的平面以及一个球体。除此之外还需要一个平行光。
- 继续添加两个空对象,并分别命名为pos001和pos002。
- 给球体添加一个新的材质,并使用Fresnel Shader(上一篇)。这样你的场景应该看起来像下面这样。
- 最后,创建一个脚本,并命名为SwapCubemaps.cs,把它赋给球体。
实现
- 首先在类名之前添加[ExecuteInEditMode]:
[ExecuteInEditMode]
public class SwapCubemaps : MonoBehaviour { - 声明一些变量来存储系统需要的变量。我们将在下面解释它们的用途。
public Cubemap cubeA;
public Cubemap cubeB; public Transform posA;
public Transform posB; private Material curMat;
private Cubemap curCube; - 为了可以直观地看到Cubemaps所在的位置,我们需要利用Unity3D提供的gizmos 。因此,在脚本的最下方添加下面的代码:
void OnDrawGizmos()
{
Gizmos.color = Color.green; if(posA)
{
Gizmos.DrawWireSphere(posA.position, 0.5f);
} if(posB)
{
Gizmos.DrawWireSphere(posB.position, 0.5f);
}
} - 现在,我们需要创建一个新的方程来决定在不同的位置我们应该使用哪个Cubemap:
private Cubemap CheckProbeDistance()
{
float distA = Vector3.Distance(transform.position, posA.position);
float distB = Vector3.Distance(transform.position, posB.position); if(distA < distB)
{
return cubeA;
}
else if(distB < distA)
{
return cubeB;
}
else
{
return cubeA;
} } - 最后,我们仅仅需要每一帧的时候计算物体距离每一个预定位置的距离,然后为球体的Material替换合适的Cubemap:
void Update () {
curMat = renderer.sharedMaterial;
if(curMat)
{
curCube = CheckProbeDistance();
curMat.SetTexture("_Cubemap", curCube); }
}