UnityShader入门精要——立方体纹理

在图形学中,立方体纹理(Cubemap)是环境映射(Environment Mapping)的一种实现方法。环境映射可以模拟物体周围的环境,而使用了环境映射的物体可以看起来像镀了层金属一样反射出周围的环境。

和之前见到的纹理不同,立方体纹理一共包含了6张图像,这些图像对应了一个立方体的6个面,立方体纹理的名称也由此而来。立方体的每个面表示沿着世界空间下的轴向(上、下、左、右、前、后)观察所得的图像。

对立方体纹理采样:理坐标不同,对立方体纹理采样我们需要提供一个三维的纹理坐标,这个三维纹理坐标表示了我们在世界空间下的一个3D方向。这个方向矢量从立方体的中心出发,当它向外部延伸时就会和立方体的6个纹理之一发生相交,而采样得到的结果就是由该交点计算而来的。图10.1给出了使用方向矢量对立方体纹理采样的过程。

立方体纹理也仅可以反射环境,但不能反射使用了该立方体纹理的物体本身。这是因为,立方体纹理不能模拟多次反射的结果,例如两个金属球互相反射的情况(事实上,Unity5引入的全局光照系统允许实现这样的自反射效果)。由于这样的原因,.想要得到令人信服的渲染结果,我们应该尽量对凸面体而不要对凹面体使用立方体纹理(因为凹面体会反射自身)。立方体纹理在实时渲染中有很多应用,最常见的是用于天空盒子(Skybox)以及环境映射。

天空盒子:使用天空盒子时只需创建SkyBox的材质,再把它赋给该场景相关的设置即可。该材质需要6张纹理,并要注意6张纹理的正确位置,为了让天空盒子正常渲染,我们需要把这6张纹理的Wrap Mode设置为Clamp,以防止在接缝处出现不匹配的现象。并按照下图设置。

上面的材质中,除了6张纹理属性外还有3个属性: Tint Color,用于控制该材质的整体颜色; Exposure, 用于调整天空盒子的亮度;Rotation,用于调整天空盒子沿+y轴方向的旋转角度。

需要说明的是,在Window→Lighting →Skybox 中设置的天空盒子会应用于该场景中的所有摄像机。如果我们希望某些摄像机可以使用不同的天空盒子,可以通过向该摄像机添加Skybox组件来覆盖掉之前的设置。也就是说,我们可以在摄像机上单击Component →Rendering →Skybox来完成对场景默认天空盒子的覆盖。

环境映射:这种方法可以模拟金属质感的物体。在Unity 5中,创建用于环境映射的立方体纹理的方法有三种:第-一种方法是直接由一些特殊布局的纹理创建;第二种方法是手动创建一一个Cubemap资源,再把6张图赋给它;第三种方法是由脚本生成。

1.方法一:我们需要提供一张具有特殊布局的纹理,例如类似立方体展开图的交叉布局、全景布局等。然后,我们只需要把该纹理的Texture Type设置为Cubemap即可,Unity会为我们做好剩下的事情。在基于物理的渲染中,我们通常会使用一张HDR图像来生成高质量的Cubemap。

2.方法二:第二种方法是Unity5之前的版本中使用的方法。我们首先需要在项目资源中创建一个Cubemap,然后把6张纹理拖曳到它的面板中。

3.方法三:在Unity中使用脚本来创建,通过利用Unity提供的Camera.RenderToCubemap函数来实现。Camera.RenderToCubemap 函数可以把从任意位置观察到的场景图像存储到6张图像中,从而创建出该位置上对应的立方体纹理。

using UnityEngine;
using UnityEditor;
using System.Collections;

public class RenderCubemapWizard : ScriptableWizard {
	
	public Transform renderFromPosition;
	public Cubemap cubemap;
	
	void OnWizardUpdate () {
		helpString = "Select transform to render from and cubemap to render into";
		isValid = (renderFromPosition != null) && (cubemap != null);
	}
	
	void OnWizardCreate () {
		// create temporary camera for rendering
		GameObject go = new GameObject( "CubemapCamera");
		go.AddComponent<Camera>();
		// place it on the object
		go.transform.position = renderFromPosition.position;
		// render into cubemap		
		go.GetComponent<Camera>().RenderToCubemap(cubemap);
		
		// destroy temporary camera
		DestroyImmediate( go );
	}
	
	[MenuItem("GameObject/Render into Cubemap")]
	static void RenderCubemap () {
		ScriptableWizard.DisplayWizard<RenderCubemapWizard>(
			"Render cubemap", "Render!");
	}
}

在上面的代码中,我们在renderFromPosition (由用户指定)位置处动态创建一个摄像机, 并调用Camera.RenderToCubemap函数把从当前位置观察到的图像渲染到用户指定的立方体纹理cubemap中,完成后再销毁临时摄像机。由于该代码需要添加菜单栏条目,因此我们需要把它放在Editor文件夹下才能正确执行

创建立方体纹理:创建空的GameObject,我们使用该位置信息渲染立方体纹理。Create->Legacy->Cubemap,并勾选Readable选项。从Unity菜单栏选择GameObject -> Render into Cubemap,打开我们在脚本中实现的用于渲染立方体纹理的窗口,并把GameObject和立方体纹理分别拖曳到窗口中的Render From Position和Cubemap选项。单击Render!,即可完成渲染到立方体材质中。

需要注意的是,我们需要为Cubemap设置大小,即图10.6中的Face size选项。Face size值越大,渲染出来的立方体纹理分辨率越大,效果可能更好,但需要占用的内存也越大,这可以由面板最下方显示的内存大小得到。

猜你喜欢

转载自blog.csdn.net/weixin_51327051/article/details/123537190