【Unity Shaders】Reflecting Your World —— 在Unity3D中创建一个简单的动态Cubemap系统

摘要:
一个最基本的方法就是根据在房间中的位置替换Cubemap。最后,创建一个脚本,并命名为SwapCubemaps.cs,把它赋给球体。最后的准备效果如下:实现首先在类名之前添加[ExecuteInEditMode]:[ExecuteInEditMode]publicclassSwapCubemaps:MonoBehaviour{声明一些变量来存储系统需要的变量。这将告诉Unity我们想要在编辑器状态时也运行我们的Cubemap替换脚本,而不仅仅是在Play状态。然后我们来看一下脚本中真正替换Cubemaps的函数。然后它检查哪个距离更近,并返回那个位置对应的Cubemap。

本系列主要参考《Unity Shaders and Effects Cookbook》一书(感谢原书作者),同时会加上一点个人理解或拓展。

这里是本书所有的插图。这里是本书所需的代码和资源(当然你也可以从官网下载)。

========================================== 分割线==========================================

写在前面

我们已经学了很多关于反射的内容,但是我们现在的反射并不能实时反射,即当反射物体移动时它们不能正确反射周围的环境。例如,如果你有一个由很多房间和走廊组成的环境,我们不可能提前渲染所有的Cubemap然后放到一个Cubemap中。这意味着我们不能随着房间的移动而正确反射。我们得到的是一个静态的、令人乏味的反射效果。

有很多方法可以解决这个问题,即一个房间的反射不同于另一个房间的反射。一个最基本的方法就是根据在房间中的位置替换Cubemap。因此,当你从一个房间移动到另一个房间时,Cubemap应该被换成当前房间的Cubemap。第二种方法是当我们角色在环境中移动时,实时更新Cubemap,最终在游戏进行的每一帧得到一个新的Cubemap。尽管第二种方法听起来非常吸引人,但是它比较消耗性能,因此你需要在其他游戏资源之间进行权衡。

这一篇将会讲述第一种方法,并且向你展示如何搭建一个非常简单的系统来基于你在环境中的不同位置去替换两个Cubemaps。更多的关于实时反射系统的内容你可以在本节最后找到,因此如果你对实时反射感兴趣并且想要看看两种技术之间的差别,你可以在那里找到!

准备工作
  1. 我们需要创建一个新的场景、一个新的平面以及一个球体。除此之外还需要一个平行光。
  2. 继续添加两个空对象,并分别命名为pos001和pos002。
  3. 给球体添加一个新的材质,并使用Fresnel Shader(上一篇)。这样你的场景应该看起来像下面这样。
  4. 最后,创建一个脚本,并命名为SwapCubemaps.cs,把它赋给球体。
最后的准备效果如下:
【Unity Shaders】Reflecting Your World —— 在Unity3D中创建一个简单的动态Cubemap系统第1张
实现
  1. 首先在类名之前添加[ExecuteInEditMode]
    [ExecuteInEditMode]
    public class SwapCubemaps : MonoBehaviour {

  2. 声明一些变量来存储系统需要的变量。我们将在下面解释它们的用途。
    	public Cubemap cubeA;
    	public Cubemap cubeB;
    	
    	public Transform posA;
    	public Transform posB;
    	
    	private Material curMat;
    	private Cubemap curCube;

  3. 为了可以直观地看到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);
    		}
    	}

  4. 现在,我们需要创建一个新的方程来决定在不同的位置我们应该使用哪个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;
    		}
    		
    	}

  5. 最后,我们仅仅需要每一帧的时候计算物体距离每一个预定位置的距离,然后为球体的Material替换合适的Cubemap:
    	void Update () {
    		curMat = renderer.sharedMaterial;
    		if(curMat)
    		{
    			curCube = CheckProbeDistance();
    			curMat.SetTexture("_Cubemap", curCube);
    			
    		}
    	}

保存脚本,返回Unity编辑器。当编译完成后,点击Play按钮,并前后左右移动球体。你将会看到类似下面的效果:
【Unity Shaders】Reflecting Your World —— 在Unity3D中创建一个简单的动态Cubemap系统第2张
解释
一开始,我们为类声明了[ExecuteInEditMode]属性。这将告诉Unity我们想要在编辑器状态时也运行我们的Cubemap替换脚本,而不仅仅是在Play状态。这可以使我们不需要点击Play按钮就可以检验Cubemap替换的脚本——这很方便不是吗~
这个脚本包含一些变量,包括两个Cubemaps和两个位置变量(用于比较距离),我们需要提前赋值给它们。最后我们需要两个私有变量来追踪运行时刻当前的球体材质和Cubemap。
当我们给四个共有变量赋值后,就可以使用内置的OnDrawGizmos()函数来真正显示两个空对象(位置对象)的位置。这些位置将会指导脚本什么时候去替换Cubemaps。
然后我们来看一下脚本中真正替换Cubemaps的函数。我们声明了自己的函数CheckProbeDistance(),它将使用Vector3.Distance计算球体和其他两个位置点的距离。然后它检查哪个距离更近,并返回那个位置对应的Cubemap。
最后,在Update()函数中我们得到当前球体的材质,或者其他该脚本赋值的对象,然后根据返回的自定义Cubemap来赋值给该材质。
这仅仅是一个非常简单的脚本来参数这个概念,但是它可以被扩展成一个完整的系统,例如你可以每一个房间对应多个Cubemaps。这个系统可以实时自动生成所有的Cubemaps(但不需要每一帧都生成),这对于无法进行完全实时反射的游戏系统将非常有用。
更多……
你还可以看一下,如何创建一个实时反射系统,在这样的系统中每一帧都需要渲染更新Cubemap。这的确是一个非常吸引人的系统,但是需要以牺牲性能为代价:
http://docs.unity3d.com/Documentation/ScriptReference/Camera.RenderToCubemap.html

免责声明:文章转载自《【Unity Shaders】Reflecting Your World —— 在Unity3D中创建一个简单的动态Cubemap系统》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇van-datetime-picker 的confirm事件传多个参数5G信令(就是用户身份信息)风暴——就是客户端通过公钥加密的消息(携带手机IMSI号)发给服务端,服务器需用私钥解密,这个解密比较消耗资源,如果短时间大量请求到来就会触发信令风暴下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

”该证书已被签发机构吊销“错误解决方案

昨天安装和配置Outlook时,公司要求使用修改后的Outlook。在官网上下载安装证书后,始终没有成功,始终存在“证书已被颁发机构吊销”的错误,但很明显,证书尚未过期。一天下来,我对这件事很不高兴。我匆忙地把钥匙留在了公司,回家后,我决定不进门。SB房东说没有备用钥匙,让我找到开锁公司。开锁公司想打开我的烂门100次,所以我去网吧玩了一晚。今天早上我上班时...

Visual studio之C#实现数字输入模拟键盘

所以我想自己实现软键盘。这篇文章是来做记录的。在Load event表单中,添加所有标签控件的click event mybutton _ clicked,privatevoidlazerctrl _ Load{//注册键盘,单击事件keyb1。单击+=newEventHandler;keyb2。单击+=newEventHandler,keyb3。单击++=...

beego

Charset=utf8“)56//参数4(可选)设置最大空闲连接7//参数5modelorm.RegisterModelRegisterModelWithPrefix。使用表名前缀orm.RegisterModelWithPrefixbeego自动创建表。1//参数1使用默认数据库ORM接口使用1//查询操作2funread(){3o:=ORM.NewOr...

Winform知识点

BringToFront()将控件移动到Z顺序的前面。...

uniapp打包h5 出现'连接服务器超时,点击屏幕重试'的页面

跟踪以首先找出原因全局组件AsyncErrorNew在中注册。js文件可以自定义。我很快就过去了,所以我添加了一个空白页面,然后在清单中介绍了组件。json文件...

node 访问第三方API

如果没有提供头,将检测文件后缀,并在PUT请求中设置相应的内容类型。...