【Unity Shaders】Reflecting Your World —— Unity3D中的法线贴图和反射

摘要:
同时,还需要创建一个新的Shader和Material,命名为NormalMappedReflection。这是基于法线贴图的反射的精华所在。通过使用INTERNAL_DATA声明,我们可以访问经过法线贴图修改后的平面的法线信息:structInput{float2uv_MainTex;float2uv_NormalMap;float3worldRefl;INTERNAL_DATA};最后,我们需要修改surf函数,来得到最后的基于法线贴图的反射效果:voidsurf{half4c=tex2D;float3normals=UnpackNormal.rgb;o.Normal=normals;o.Emission=texCUBE.rgb*_ReflAmount;o.Albedo=c.rgb*_MainTint;o.Alpha=c.a;}最后,整体代码如下:Shader"Custom/NormalMappedReflection"{Properties{_MainTint=_MainTex="white"{}_NormalMap="bump"{}_Cubemap=""{}_ReflAmount=0.5}SubShader{Tags{"RenderType"="Opaque"}LOD200CGPROGRAM#pragmasurfacesurfLambertsamplerCUBE_Cubemap;sampler2D_MainTex;sampler2D_NormalMap;float4_MainTint;float_ReflAmount;structInput{float2uv_MainTex;float2uv_NormalMap;float3worldRefl;INTERNAL_DATA};voidsurf{half4c=tex2D;float3normals=UnpackNormal.rgb;o.Normal=normals;o.Emission=texCUBE.rgb*_ReflAmount;o.Albedo=c.rgb*_MainTint;o.Alpha=c.a;}ENDCG}FallBack"Diffuse"}效果如下:而下图是不加法线影响的效果:解释你可能已经注意到这一节的Shader和上一节中的非常类似,而有一个非常重要的不同点。

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

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

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

写在前面

有很多情况你可能想要使用法线去影响反射效果。比如,你想要模拟一个被霜雪覆盖的玻璃材质的表面,或者一个冰块。如果你根据物理知识真实的模拟这个平面的每一个细节部分,那么你就不要希望你游戏的FPS还可以达到60帧了。相反的,我们可以使用法线贴图来伪造一个视觉体验,因此我们需要学习如何将法线贴图的信息传递给反射效果。
为了完成这个任务,我们将要学习Input结构体的另一个内置参数,它可以将由法线贴图技术生成的修改后的平面法线信息传递进来。下面,让我们来具体看一下如何修改Input结构体来达到这个效果吧!
准备
  1. 首先,我们需要一个Cubemap来产生反射效果。所以你可以使用前一节中的Cubemap,或者生成一个新的。这一节中,我们使用的如下所示(你可以在本书资源中找到它):
    【Unity Shaders】Reflecting Your World —— Unity3D中的法线贴图和反射第1张
  2. 我们还需要一张法线贴图来产生基于法线的反射效果。
    【Unity Shaders】Reflecting Your World —— Unity3D中的法线贴图和反射第2张
  3. 最后,创建一个新的场景、一个球体、一个平面以及一个平行光。同时,还需要创建一个新的Shader和Material,命名为NormalMappedReflection。
实现
  1. 首先让我们添加新的properties,使得我们能够添加自己的Cubemap和法线贴图。这个步骤你应该非常熟悉了。向Properties块添加下列代码:
    	Properties {
    		_MainTint ("Diffuse Tint", Color) = (1,1,1,1)
    		_MainTex ("Base (RGB)", 2D) = "white" {}
    		_NormalMap ("Normal Map", 2D) = "bump" {}
    		_Cubemap ("Cubemap", CUBE) = ""{}
    		_ReflAmount ("Reflection Amount", Range(0,1)) = 0.5
    	}
  2. 然后,我们需要在SubShader块声明这些properties,使得我们能够访问Properties块中的这些数据:
    		CGPROGRAM
    		#pragma surface surf Lambert
    
    		samplerCUBE _Cubemap;
    		sampler2D _MainTex;
    		sampler2D _NormalMap;
    		float4 _MainTint;
    		float _ReflAmount;

  3. 然后,修改Input结构体。这是基于法线贴图的反射的精华所在。通过使用INTERNAL_DATA声明,我们可以访问经过法线贴图修改后的平面的法线信息:
    		struct Input {
    			float2 uv_MainTex;
    			float2 uv_NormalMap;
    			float3 worldRefl;
    			INTERNAL_DATA
    		};

  4. 最后,我们需要修改surf函数,来得到最后的基于法线贴图的反射效果:
    		void surf (Input IN, inout SurfaceOutput o) {
    			half4 c = tex2D (_MainTex, IN.uv_MainTex);
    			
    			float3 normals = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap)).rgb;
    			o.Normal = normals;
    			
    			o.Emission = texCUBE (_Cubemap, WorldReflectionVector (IN, o.Normal)).rgb * _ReflAmount;
    			o.Albedo = c.rgb * _MainTint;
    			o.Alpha = c.a;
    		}

最后,整体代码如下:
Shader "Custom/NormalMappedReflection" {
	Properties {
		_MainTint ("Diffuse Tint", Color) = (1,1,1,1)
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_NormalMap ("Normal Map", 2D) = "bump" {}
		_Cubemap ("Cubemap", CUBE) = ""{}
		_ReflAmount ("Reflection Amount", Range(0,1)) = 0.5
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 200
		
		CGPROGRAM
		#pragma surface surf Lambert

		samplerCUBE _Cubemap;
		sampler2D _MainTex;
		sampler2D _NormalMap;
		float4 _MainTint;
		float _ReflAmount;

		struct Input {
			float2 uv_MainTex;
			float2 uv_NormalMap;
			float3 worldRefl;
			INTERNAL_DATA
		};

		void surf (Input IN, inout SurfaceOutput o) {
			half4 c = tex2D (_MainTex, IN.uv_MainTex);
			
			float3 normals = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap)).rgb;
			o.Normal = normals;
			
			o.Emission = texCUBE (_Cubemap, WorldReflectionVector (IN, o.Normal)).rgb * _ReflAmount;
			o.Albedo = c.rgb * _MainTint;
			o.Alpha = c.a;
		}
		ENDCG
	} 
	FallBack "Diffuse"
}

效果如下(一个表明凹凸不平的反射球):
【Unity Shaders】Reflecting Your World —— Unity3D中的法线贴图和反射第3张
而下图是不加法线影响的效果(一个光滑的反射球):
【Unity Shaders】Reflecting Your World —— Unity3D中的法线贴图和反射第4张
解释
你可能已经注意到这一节的Shader和上一节中的非常类似,而有一个非常重要的不同点(上一节直接使用IN.worldRefl来查找Cubemap)。我们想要使用逐像素的法线贴图来修改我们的反射贴图。为了完成这个目的,我们需要得到在应用法线贴图后、物体的平面法线信息。这意味着,我们需要下列代码:
			float3 normals = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap)).rgb;
			o.Normal = normals;

一旦上述代码在Shader中执行后,物体的平面法线将被修改;因此,我们需要使用它来影响我们的反射。我们可以通过声明INTERNAL_DATA来访问修改后的法线信息,然后使用WorldReflectionVector(IN,o.Normal)去查找Cubemap中对应的反射信息。这是Unity提供给我们的另一个内置函数,因此我们不需要再自己写那些冗长的代码,而仅仅需要关注编写Shader中产生关键效果的部分。
更多…
结构体中还有很多内置函数(变量),而在将来的章节中我们的确会接触到它们。下面的表格描述了每一个内置函数的作用以及如何使用它们。你也可以访问官网来得到更多的关于内置函数的信息:
float3 viewDir包含了视角的观察方向,主要用于计算视差效应(Parallax effects),边缘光照,等等。
float4 COLOR包含了经过内插值(interpolated)的每个顶点的颜色值。
float4 screenPos包含了用于反射效果的屏幕坐标系的位置信息。例如,在Unity专业版的WetStreet shader中使用了它。
float3 worldPos包含了世界坐标系中的位置。
float3 worldRefl包含了世界坐标系中的反射向量,如果Surface Shader没有重写o.Normal。参考Reflect-Diffuse shader。
float3 worldNormal包含了世界坐标系中的法线向量,如果Surface Shader没有重写o.Normal。
float3 worldRefl;
INTERNAL_DATA
包含了世界坐标系的反射向量,如果Surface Shader重写了o.Normal。为了得到基于逐像素的法线贴图的反射向量,请使用WorldReflectionVector (IN,o.Normal) 。参考Reflect-Bumped shader。
float3 worldNormal;
INTERNAL_DATA
包含了世界坐标系的发现向量,如果Surface Shader重写了o.Normal。为了得到基于逐像素的法线贴图的法线向量,请使用WorldNormalVector(IN,o.Normal) 。参考Reflect-Bumped shader。

免责声明:文章转载自《【Unity Shaders】Reflecting Your World —— Unity3D中的法线贴图和反射》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇win7下安装 oracle 10g:permission deniedyum仓库管理下篇

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

随便看看

14款优秀的JavaScript调试工具大盘点

官方网站:http://www.jshint.com/4.Grunt Grunt是一个基于任务的命令行构建工具,适用于JavaScript项目。Venkman旨在为Mozilla提供一个基于浏览器的强大JavaScript调试环境。官方网站:http://www.my-debugbar.com/wiki/CompanionJS/HomePage10.Simp...

Dapper系列之一:Dapper的入门(多表批量插入)

Dapper只是一个完全开源的代码文件。您可以在项目中的任何位置实现数据到对象ORM操作,其大小小,速度快。Dapper的优点:1。Dapper是一个轻量级ORM类。该代码是一个SQLMapper.cs文件,编译后通常约为40k dll;2.Dapper,快点,你为什么说得快?因为Dapper的速度接近IDataReader,所以列表的数据比DataTabl...

cocosCreator中Protobuf的简单使用

CocosCreatorwebStorm或VSCode安装node.js下载protobufjs6.8.6步骤:...

LaTex学习笔记(1)——LaTeX文档插入图片的几种常用方法

2,插入bmp图片还没有找到直接插入bmp图片的方法。用gimp或fastoneimageviewer,将jpg质量选为最高,转换之后得到的图片质量较好。3,同时插入jpg和eps图片插入的命令不变。编译时使用Latex,dvi2pdf,两种格式的图片都可以显示。...

常用的Maven 插件

Maven本质上是一个插件框架。其核心不执行任何特定的施工任务。所有这些任务都委托给插件。多年来,Maven社区积累了大量经验,随后形成了成熟的插件生态系统。这个插件在一些Ant到Maven的迁移项目中特别有用。只要在父POM中配置规则,然后由每个人继承,Maven就会在规则被破坏时报告错误。...

记一次Arcgis Server10.2许可过期导致发布图层失败

1.今天,当使用arcmap将地图服务发布到arcgisserver时,发布突然失败。在arcgisserver的管理页面的日志选项中发现错误:未能初始化服务器对象“System/PublicingTools”:0x80004005:错误:(-8003)YourArcGISServerlicense已过期。2.然后在服务器路径中查找文件:...