Unity Shaders and Effects Cookbook (4-4)在Cubemap 上使用 法线贴图 (法线贴图与反射)

法线贴图 在之前学习过了,我们使用法线贴图在低分辨率的模型上 模拟 高分辨率的效果。

Unity中 通过 UnpackNormal 函数 来使用法线贴图。


之前学习法线贴图的记录

Unity Shaders and Effects Cookbook (2-5) 如何使用法线贴图

这一节讲的是 在Cubemap 上使用法线贴图。模拟凹凸效果。

最终效果如图


一起来做吧。

首先搭建好场景,和上一节一样。


导入法线贴图



创建材质 、Shader 。

复制上一节的 Shader 就行。然后修改成下面的内容。

Shader "CookBookShaders/Chapt4-4/Cubemap_NormalMap" 
{
	Properties 
	{
		_MainTint("Diffuse Color",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

		float4 _MainTint;
		sampler2D _MainTex;

		sampler2D _NormalMap;

		samplerCUBE _Cubemap;

		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);

			//从法线贴图中提取法线信息,UnpackNormal这个函数在 CGInclude 文件夹中的Lighting中。
			float3 normals=UnpackNormal(tex2D(_NormalMap,IN.uv_NormalMap));

			o.Normal=normals;

			//上面使用法线贴图中的法线数据 替代了 原来的法线数据。

			//法线被修改了,就不能 直接用原来的 内置属性 worldRefl 这个反射向量,而是要通过 WorldReflectionVector(IN,o.Normal)来获取。

			//使用WorldReflectionVector 获得 基于法线贴图中的反射向量的 世界反射向量
			o.Emission = texCUBE(_Cubemap,WorldReflectionVector(IN,o.Normal)).rgb * _ReflAmount;

			o.Albedo = c.rgb * _MainTint;
			o.Alpha = c.a;
		}
		ENDCG
	} 
	FallBack "Diffuse"
}

和上一节的Shader 相比,修改了如下几处:

1、在 Properties 属性块中添加了法线贴图

_NormalMap("Normal Map",2D) = "bump"{}

然后在 SubShader 中添加对应的变量

sampler2D _NormalMap;


2、在 Input 中 添加了 法线贴图的UV,以及一个奇怪的字段 INTERNAL_DATA

		struct Input 
		{
			float2 uv_MainTex;

			float2 uv_NormalMap;

			float3 worldRefl;

			INTERNAL_DATA
		};

uv_NormalMap 是用来读取 法线贴图数据的。

转自http://blog.csdn.net/huutu http://www.thisisgame.com.cn

INTERNAL_DATA

书上有两点解释

A:通过在Input 中添加 INTERNAL_DATA ,我们就可以访问由法线贴图修改后的表面法线。

这个很奇怪,为什么要添加 INTERNAL_DATA,才可以访问由法线贴图修改后的表面法线?上次学习法线贴图的时候可没有这个东西。

习惯性到 CGIncludes 里面去查找,发现并没有 INTERNAL_DATA 。


在官方文档

http://docs.unity3d.com/Manual/SL-SurfaceShaderExamples.html

找到这一句话

If you want to do reflections that are affected by normal maps, it needs to be slightly more involved: INTERNAL_DATA needs to be added to the Input structure, and WorldReflectionVector function used to compute per-pixel reflection vector after you’ve written the Normal output.


就是说计算世界反射向量的时候 使用了法线贴图就要加上 这个。


谷歌一下,发现网友说在 生成的代码中能看出来,然后就查看生成的代码

Shader "CookBookShaders/Chapt4-4/Cubemap_NormalMap" 
{
	Properties 
	{
		_MainTint("Diffuse Color",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
		
		
	// ------------------------------------------------------------
	// Surface shader code generated out of a CGPROGRAM block:
	

	// ---- forward rendering base pass:
	Pass {
		Name "FORWARD"
		Tags { "LightMode" = "ForwardBase" }

CGPROGRAM
// compile directives
#pragma vertex vert_surf
#pragma fragment frag_surf
#pragma multi_compile_fwdbase
#include "HLSLSupport.cginc"
#include "UnityShaderVariables.cginc"
#define UNITY_PASS_FORWARDBASE
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"

#define INTERNAL_DATA half3 TtoW0; half3 TtoW1; half3 TtoW2;
#define WorldReflectionVector(data,normal) reflect (data.worldRefl, half3(dot(data.TtoW0,normal), dot(data.TtoW1,normal), dot(data.TtoW2,normal)))
#define WorldNormalVector(data,normal) fixed3(dot(data.TtoW0,normal), dot(data.TtoW1,normal), dot(data.TtoW2,normal))

// Original surface shader snippet:
#line 18 ""
#ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING
#endif

		//#pragma surface surf Lambert

		float4 _MainTint;
		sampler2D _MainTex;

		sampler2D _NormalMap;

		samplerCUBE _Cubemap;

		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);

			//从法线贴图中提取法线信息,UnpackNormal这个函数在 CGInclude 文件夹中的Lighting中。
			float3 normals=UnpackNormal(tex2D(_NormalMap,IN.uv_NormalMap));

			o.Normal=normals;

			//上面使用法线贴图中的法线数据 替代了 原来的法线数据。

			//法线被修改了,就不能 直接用原来的 内置属性 worldRefl 这个反射向量,而是要通过 WorldReflectionVector(IN,o.Normal)来获取。

			//使用WorldReflectionVector 获得 基于法线贴图中的反射向量的 世界反射向量
			o.Emission = texCUBE(_Cubemap,WorldReflectionVector(IN,o.Normal)).rgb * _ReflAmount;

			o.Albedo = c.rgb * _MainTint;
			o.Alpha = c.a;
		}
		

// vertex-to-fragment interpolation data
#ifdef LIGHTMAP_OFF
struct v2f_surf {
  float4 pos : SV_POSITION;
  float4 pack0 : TEXCOORD0;
  fixed4 TtoW0 : TEXCOORD1;
  fixed4 TtoW1 : TEXCOORD2;
  fixed4 TtoW2 : TEXCOORD3;
  fixed3 lightDir : TEXCOORD4;
  fixed3 vlight : TEXCOORD5;
  LIGHTING_COORDS(6,7)
};
#endif
#ifndef LIGHTMAP_OFF
struct v2f_surf {
  float4 pos : SV_POSITION;
  float4 pack0 : TEXCOORD0;
  fixed4 TtoW0 : TEXCOORD1;
  fixed4 TtoW1 : TEXCOORD2;
  fixed4 TtoW2 : TEXCOORD3;
  float2 lmap : TEXCOORD4;
  LIGHTING_COORDS(5,6)
};
#endif
#ifndef LIGHTMAP_OFF
float4 unity_LightmapST;
#endif
float4 _MainTex_ST;
float4 _NormalMap_ST;

// vertex shader
v2f_surf vert_surf (appdata_full v) {
  v2f_surf o;
  o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
  o.pack0.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
  o.pack0.zw = TRANSFORM_TEX(v.texcoord, _NormalMap);
  float3 viewDir = -ObjSpaceViewDir(v.vertex);
  float3 worldRefl = mul ((float3x3)_Object2World, viewDir);
  TANGENT_SPACE_ROTATION;
  o.TtoW0 = float4(mul(rotation, _Object2World[0].xyz), worldRefl.x)*unity_Scale.w;
  o.TtoW1 = float4(mul(rotation, _Object2World[1].xyz), worldRefl.y)*unity_Scale.w;
  o.TtoW2 = float4(mul(rotation, _Object2World[2].xyz), worldRefl.z)*unity_Scale.w;
  #ifndef LIGHTMAP_OFF
  o.lmap.xy = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
  #endif
  float3 worldN = mul((float3x3)_Object2World, SCALED_NORMAL);
  float3 lightDir = mul (rotation, ObjSpaceLightDir(v.vertex));
  #ifdef LIGHTMAP_OFF
  o.lightDir = lightDir;
  #endif

  // SH/ambient and vertex lights
  #ifdef LIGHTMAP_OFF
  float3 shlight = ShadeSH9 (float4(worldN,1.0));
  o.vlight = shlight;
  #ifdef VERTEXLIGHT_ON
  float3 worldPos = mul(_Object2World, v.vertex).xyz;
  o.vlight += Shade4PointLights (
    unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
    unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
    unity_4LightAtten0, worldPos, worldN );
  #endif // VERTEXLIGHT_ON
  #endif // LIGHTMAP_OFF

  // pass lighting information to pixel shader
  TRANSFER_VERTEX_TO_FRAGMENT(o);
  return o;
}
#ifndef LIGHTMAP_OFF
sampler2D unity_Lightmap;
#ifndef DIRLIGHTMAP_OFF
sampler2D unity_LightmapInd;
#endif
#endif

// fragment shader
fixed4 frag_surf (v2f_surf IN) : SV_Target {
  // prepare and unpack data
  #ifdef UNITY_COMPILER_HLSL
  Input surfIN = (Input)0;
  #else
  Input surfIN;
  #endif
  surfIN.uv_MainTex = IN.pack0.xy;
  surfIN.uv_NormalMap = IN.pack0.zw;
  surfIN.worldRefl = float3(IN.TtoW0.w, IN.TtoW1.w, IN.TtoW2.w);
  surfIN.TtoW0 = IN.TtoW0.xyz;
  surfIN.TtoW1 = IN.TtoW1.xyz;
  surfIN.TtoW2 = IN.TtoW2.xyz;
  #ifdef UNITY_COMPILER_HLSL
  SurfaceOutput o = (SurfaceOutput)0;
  #else
  SurfaceOutput o;
  #endif
  o.Albedo = 0.0;
  o.Emission = 0.0;
  o.Specular = 0.0;
  o.Alpha = 0.0;
  o.Gloss = 0.0;

  // call surface function
  surf (surfIN, o);

  // compute lighting & shadowing factor
  fixed atten = LIGHT_ATTENUATION(IN);
  fixed4 c = 0;

  // realtime lighting: call lighting function
  #ifdef LIGHTMAP_OFF
  c = LightingLambert (o, IN.lightDir, atten);
  #endif // LIGHTMAP_OFF || DIRLIGHTMAP_OFF
  #ifdef LIGHTMAP_OFF
  c.rgb += o.Albedo * IN.vlight;
  #endif // LIGHTMAP_OFF

  // lightmaps:
  #ifndef LIGHTMAP_OFF
    #ifndef DIRLIGHTMAP_OFF
      // directional lightmaps
      fixed4 lmtex = tex2D(unity_Lightmap, IN.lmap.xy);
      fixed4 lmIndTex = tex2D(unity_LightmapInd, IN.lmap.xy);
      half3 lm = LightingLambert_DirLightmap(o, lmtex, lmIndTex, 1).rgb;
    #else // !DIRLIGHTMAP_OFF
      // single lightmap
      fixed4 lmtex = tex2D(unity_Lightmap, IN.lmap.xy);
      fixed3 lm = DecodeLightmap (lmtex);
    #endif // !DIRLIGHTMAP_OFF

    // combine lightmaps with realtime shadows
    #ifdef SHADOWS_SCREEN
      #if defined(UNITY_NO_RGBM)
      c.rgb += o.Albedo * min(lm, atten*2);
      #else
      c.rgb += o.Albedo * max(min(lm,(atten*2)*lmtex.rgb), lm*atten);
      #endif
    #else // SHADOWS_SCREEN
      c.rgb += o.Albedo * lm;
    #endif // SHADOWS_SCREEN
  c.a = o.Alpha;
  #endif // LIGHTMAP_OFF

  c.rgb += o.Emission;
  return c;
}

ENDCG

}

	// ---- forward rendering additive lights pass:
	Pass {
		Name "FORWARD"
		Tags { "LightMode" = "ForwardAdd" }
		ZWrite Off Blend One One Fog { Color (0,0,0,0) }

CGPROGRAM
// compile directives
#pragma vertex vert_surf
#pragma fragment frag_surf
#pragma multi_compile_fwdadd
#include "HLSLSupport.cginc"
#include "UnityShaderVariables.cginc"
#define UNITY_PASS_FORWARDADD
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"

#define INTERNAL_DATA
#define WorldReflectionVector(data,normal) data.worldRefl
#define WorldNormalVector(data,normal) normal

// Original surface shader snippet:
#line 18 ""
#ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING
#endif

		//#pragma surface surf Lambert

		float4 _MainTint;
		sampler2D _MainTex;

		sampler2D _NormalMap;

		samplerCUBE _Cubemap;

		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);

			//从法线贴图中提取法线信息,UnpackNormal这个函数在 CGInclude 文件夹中的Lighting中。
			float3 normals=UnpackNormal(tex2D(_NormalMap,IN.uv_NormalMap));

			o.Normal=normals;

			//上面使用法线贴图中的法线数据 替代了 原来的法线数据。

			//法线被修改了,就不能 直接用原来的 内置属性 worldRefl 这个反射向量,而是要通过 WorldReflectionVector(IN,o.Normal)来获取。

			//使用WorldReflectionVector 获得 基于法线贴图中的反射向量的 世界反射向量
			o.Emission = texCUBE(_Cubemap,WorldReflectionVector(IN,o.Normal)).rgb * _ReflAmount;

			o.Albedo = c.rgb * _MainTint;
			o.Alpha = c.a;
		}
		

// vertex-to-fragment interpolation data
struct v2f_surf {
  float4 pos : SV_POSITION;
  float4 pack0 : TEXCOORD0;
  half3 lightDir : TEXCOORD1;
  LIGHTING_COORDS(2,3)
};
float4 _MainTex_ST;
float4 _NormalMap_ST;

// vertex shader
v2f_surf vert_surf (appdata_full v) {
  v2f_surf o;
  o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
  o.pack0.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
  o.pack0.zw = TRANSFORM_TEX(v.texcoord, _NormalMap);
  TANGENT_SPACE_ROTATION;
  float3 lightDir = mul (rotation, ObjSpaceLightDir(v.vertex));
  o.lightDir = lightDir;

  // pass lighting information to pixel shader
  TRANSFER_VERTEX_TO_FRAGMENT(o);
  return o;
}

// fragment shader
fixed4 frag_surf (v2f_surf IN) : SV_Target {
  // prepare and unpack data
  #ifdef UNITY_COMPILER_HLSL
  Input surfIN = (Input)0;
  #else
  Input surfIN;
  #endif
  surfIN.uv_MainTex = IN.pack0.xy;
  surfIN.uv_NormalMap = IN.pack0.zw;
  #ifdef UNITY_COMPILER_HLSL
  SurfaceOutput o = (SurfaceOutput)0;
  #else
  SurfaceOutput o;
  #endif
  o.Albedo = 0.0;
  o.Emission = 0.0;
  o.Specular = 0.0;
  o.Alpha = 0.0;
  o.Gloss = 0.0;

  // call surface function
  surf (surfIN, o);
  #ifndef USING_DIRECTIONAL_LIGHT
  fixed3 lightDir = normalize(IN.lightDir);
  #else
  fixed3 lightDir = IN.lightDir;
  #endif
  fixed4 c = LightingLambert (o, lightDir, LIGHT_ATTENUATION(IN));
  c.a = 0.0;
  return c;
}

ENDCG

}

	// ---- deferred lighting base geometry pass:
	Pass {
		Name "PREPASS"
		Tags { "LightMode" = "PrePassBase" }
		Fog {Mode Off}

CGPROGRAM
// compile directives
#pragma vertex vert_surf
#pragma fragment frag_surf

#pragma exclude_renderers flash
#include "HLSLSupport.cginc"
#include "UnityShaderVariables.cginc"
#define UNITY_PASS_PREPASSBASE
#include "UnityCG.cginc"
#include "Lighting.cginc"

#define INTERNAL_DATA
#define WorldReflectionVector(data,normal) data.worldRefl
#define WorldNormalVector(data,normal) normal

// Original surface shader snippet:
#line 18 ""
#ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING
#endif

		//#pragma surface surf Lambert

		float4 _MainTint;
		sampler2D _MainTex;

		sampler2D _NormalMap;

		samplerCUBE _Cubemap;

		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);

			//从法线贴图中提取法线信息,UnpackNormal这个函数在 CGInclude 文件夹中的Lighting中。
			float3 normals=UnpackNormal(tex2D(_NormalMap,IN.uv_NormalMap));

			o.Normal=normals;

			//上面使用法线贴图中的法线数据 替代了 原来的法线数据。

			//法线被修改了,就不能 直接用原来的 内置属性 worldRefl 这个反射向量,而是要通过 WorldReflectionVector(IN,o.Normal)来获取。

			//使用WorldReflectionVector 获得 基于法线贴图中的反射向量的 世界反射向量
			o.Emission = texCUBE(_Cubemap,WorldReflectionVector(IN,o.Normal)).rgb * _ReflAmount;

			o.Albedo = c.rgb * _MainTint;
			o.Alpha = c.a;
		}
		

// vertex-to-fragment interpolation data
struct v2f_surf {
  float4 pos : SV_POSITION;
  float2 pack0 : TEXCOORD0;
  float3 TtoW0 : TEXCOORD1;
  float3 TtoW1 : TEXCOORD2;
  float3 TtoW2 : TEXCOORD3;
};
float4 _NormalMap_ST;

// vertex shader
v2f_surf vert_surf (appdata_full v) {
  v2f_surf o;
  o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
  o.pack0.xy = TRANSFORM_TEX(v.texcoord, _NormalMap);
  TANGENT_SPACE_ROTATION;
  o.TtoW0 = mul(rotation, ((float3x3)_Object2World)[0].xyz)*unity_Scale.w;
  o.TtoW1 = mul(rotation, ((float3x3)_Object2World)[1].xyz)*unity_Scale.w;
  o.TtoW2 = mul(rotation, ((float3x3)_Object2World)[2].xyz)*unity_Scale.w;
  return o;
}

// fragment shader
fixed4 frag_surf (v2f_surf IN) : SV_Target {
  // prepare and unpack data
  #ifdef UNITY_COMPILER_HLSL
  Input surfIN = (Input)0;
  #else
  Input surfIN;
  #endif
  surfIN.uv_NormalMap = IN.pack0.xy;
  #ifdef UNITY_COMPILER_HLSL
  SurfaceOutput o = (SurfaceOutput)0;
  #else
  SurfaceOutput o;
  #endif
  o.Albedo = 0.0;
  o.Emission = 0.0;
  o.Specular = 0.0;
  o.Alpha = 0.0;
  o.Gloss = 0.0;

  // call surface function
  surf (surfIN, o);
  fixed3 worldN;
  worldN.x = dot(IN.TtoW0, o.Normal);
  worldN.y = dot(IN.TtoW1, o.Normal);
  worldN.z = dot(IN.TtoW2, o.Normal);
  o.Normal = worldN;

  // output normal and specular
  fixed4 res;
  res.rgb = o.Normal * 0.5 + 0.5;
  res.a = o.Specular;
  return res;
}

ENDCG

}

	// ---- deferred lighting final pass:
	Pass {
		Name "PREPASS"
		Tags { "LightMode" = "PrePassFinal" }
		ZWrite Off

CGPROGRAM
// compile directives
#pragma vertex vert_surf
#pragma fragment frag_surf
#pragma multi_compile_prepassfinal
#pragma exclude_renderers flash
#include "HLSLSupport.cginc"
#include "UnityShaderVariables.cginc"
#define UNITY_PASS_PREPASSFINAL
#include "UnityCG.cginc"
#include "Lighting.cginc"

#define INTERNAL_DATA half3 TtoW0; half3 TtoW1; half3 TtoW2;
#define WorldReflectionVector(data,normal) reflect (data.worldRefl, half3(dot(data.TtoW0,normal), dot(data.TtoW1,normal), dot(data.TtoW2,normal)))
#define WorldNormalVector(data,normal) fixed3(dot(data.TtoW0,normal), dot(data.TtoW1,normal), dot(data.TtoW2,normal))

// Original surface shader snippet:
#line 18 ""
#ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING
#endif

		//#pragma surface surf Lambert

		float4 _MainTint;
		sampler2D _MainTex;

		sampler2D _NormalMap;

		samplerCUBE _Cubemap;

		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);

			//从法线贴图中提取法线信息,UnpackNormal这个函数在 CGInclude 文件夹中的Lighting中。
			float3 normals=UnpackNormal(tex2D(_NormalMap,IN.uv_NormalMap));

			o.Normal=normals;

			//上面使用法线贴图中的法线数据 替代了 原来的法线数据。

			//法线被修改了,就不能 直接用原来的 内置属性 worldRefl 这个反射向量,而是要通过 WorldReflectionVector(IN,o.Normal)来获取。

			//使用WorldReflectionVector 获得 基于法线贴图中的反射向量的 世界反射向量
			o.Emission = texCUBE(_Cubemap,WorldReflectionVector(IN,o.Normal)).rgb * _ReflAmount;

			o.Albedo = c.rgb * _MainTint;
			o.Alpha = c.a;
		}
		

// vertex-to-fragment interpolation data
struct v2f_surf {
  float4 pos : SV_POSITION;
  float4 pack0 : TEXCOORD0;
  float4 screen : TEXCOORD1;
  fixed4 TtoW0 : TEXCOORD2;
  fixed4 TtoW1 : TEXCOORD3;
  fixed4 TtoW2 : TEXCOORD4;
#ifdef LIGHTMAP_OFF
  float3 vlight : TEXCOORD5;
#else
  float2 lmap : TEXCOORD5;
#ifdef DIRLIGHTMAP_OFF
  float4 lmapFadePos : TEXCOORD6;
#endif
#endif
};
#ifndef LIGHTMAP_OFF
float4 unity_LightmapST;
#endif
float4 _MainTex_ST;
float4 _NormalMap_ST;

// vertex shader
v2f_surf vert_surf (appdata_full v) {
  v2f_surf o;
  o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
  o.pack0.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
  o.pack0.zw = TRANSFORM_TEX(v.texcoord, _NormalMap);
  float3 viewDir = -ObjSpaceViewDir(v.vertex);
  float3 worldRefl = mul ((float3x3)_Object2World, viewDir);
  TANGENT_SPACE_ROTATION;
  o.TtoW0 = float4(mul(rotation, _Object2World[0].xyz), worldRefl.x)*unity_Scale.w;
  o.TtoW1 = float4(mul(rotation, _Object2World[1].xyz), worldRefl.y)*unity_Scale.w;
  o.TtoW2 = float4(mul(rotation, _Object2World[2].xyz), worldRefl.z)*unity_Scale.w;
  o.screen = ComputeScreenPos (o.pos);
#ifndef LIGHTMAP_OFF
  o.lmap.xy = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
  #ifdef DIRLIGHTMAP_OFF
    o.lmapFadePos.xyz = (mul(_Object2World, v.vertex).xyz - unity_ShadowFadeCenterAndType.xyz) * unity_ShadowFadeCenterAndType.w;
    o.lmapFadePos.w = (-mul(UNITY_MATRIX_MV, v.vertex).z) * (1.0 - unity_ShadowFadeCenterAndType.w);
  #endif
#else
  float3 worldN = mul((float3x3)_Object2World, SCALED_NORMAL);
  o.vlight = ShadeSH9 (float4(worldN,1.0));
#endif
  return o;
}
sampler2D _LightBuffer;
#if defined (SHADER_API_XBOX360) && defined (HDR_LIGHT_PREPASS_ON)
sampler2D _LightSpecBuffer;
#endif
#ifndef LIGHTMAP_OFF
sampler2D unity_Lightmap;
sampler2D unity_LightmapInd;
float4 unity_LightmapFade;
#endif
fixed4 unity_Ambient;

// fragment shader
fixed4 frag_surf (v2f_surf IN) : SV_Target {
  // prepare and unpack data
  #ifdef UNITY_COMPILER_HLSL
  Input surfIN = (Input)0;
  #else
  Input surfIN;
  #endif
  surfIN.uv_MainTex = IN.pack0.xy;
  surfIN.uv_NormalMap = IN.pack0.zw;
  surfIN.worldRefl = float3(IN.TtoW0.w, IN.TtoW1.w, IN.TtoW2.w);
  surfIN.TtoW0 = IN.TtoW0.xyz;
  surfIN.TtoW1 = IN.TtoW1.xyz;
  surfIN.TtoW2 = IN.TtoW2.xyz;
  #ifdef UNITY_COMPILER_HLSL
  SurfaceOutput o = (SurfaceOutput)0;
  #else
  SurfaceOutput o;
  #endif
  o.Albedo = 0.0;
  o.Emission = 0.0;
  o.Specular = 0.0;
  o.Alpha = 0.0;
  o.Gloss = 0.0;

  // call surface function
  surf (surfIN, o);
  half4 light = tex2Dproj (_LightBuffer, UNITY_PROJ_COORD(IN.screen));
#if defined (SHADER_API_MOBILE)
  light = max(light, half4(0.001));
#endif
#ifndef HDR_LIGHT_PREPASS_ON
  light = -log2(light);
#endif
#if defined (SHADER_API_XBOX360) && defined (HDR_LIGHT_PREPASS_ON)
  light.w = tex2Dproj (_LightSpecBuffer, UNITY_PROJ_COORD(IN.screen)).r;
#endif

  // add lighting from lightmaps / vertex / ambient:
  #ifndef LIGHTMAP_OFF
    #ifdef DIRLIGHTMAP_OFF
      // dual lightmaps
      fixed4 lmtex = tex2D(unity_Lightmap, IN.lmap.xy);
      fixed4 lmtex2 = tex2D(unity_LightmapInd, IN.lmap.xy);
      half lmFade = length (IN.lmapFadePos) * unity_LightmapFade.z + unity_LightmapFade.w;
      half3 lmFull = DecodeLightmap (lmtex);
      half3 lmIndirect = DecodeLightmap (lmtex2);
      half3 lm = lerp (lmIndirect, lmFull, saturate(lmFade));
      light.rgb += lm;
    #else
      // directional lightmaps
      fixed4 lmtex = tex2D(unity_Lightmap, IN.lmap.xy);
      fixed4 lmIndTex = tex2D(unity_LightmapInd, IN.lmap.xy);
      half4 lm = LightingLambert_DirLightmap(o, lmtex, lmIndTex, 1);
      light += lm;
    #endif
  #else
    light.rgb += IN.vlight;
  #endif
  half4 c = LightingLambert_PrePass (o, light);
  c.rgb += o.Emission;
  return c;
}

ENDCG

}

	// ---- end of surface shader generated code

#LINE 64

	} 
	FallBack "Diffuse"
}

果然,原来 INTERNAL_DATA 就是一个宏……



而且会发现在生成的代码中不止一个 INTERNAL_DATA。这是因为生成的代码中有多个PASS

forward rendering additive lights pass:
deferred lighting base geometry pass:
deferred lighting final pass:
forward rendering base pass:


每个PASS 都有一个,所以有好几个。


那么,不使用  INTERNAL_DATA,而是直接使用 具体的内容,可以吗?来试一下。

首先,从Shader 中删掉 INTERNAL_DATA。

Unity 报了以下错误

Shader error in 'CookBookShaders/Chapt4-4/Cubemap_NormalMap': invalid subscript 'TtoW0' at line 59

是这一行的错

o.Emission = texCUBE(_Cubemap,WorldReflectionVector(IN,o.Normal)).rgb * _ReflAmount;

因为从上面 Shader 编译后的代码看到,WorldReflectionVector 也是一个宏

#define WorldReflectionVector(data,normal) reflect (data.worldRefl, half3(dot(data.TtoW0,normal), dot(data.TtoW1,normal), dot(data.TtoW2,normal)))


其实就是说,在WorldReflectionVector 这个函数里找不到 TtoW0 这个参数了。


然后,在 Input 结构体中添加 INTERNAL_DATA 的具体内容看看

		struct Input 
		{
			float2 uv_MainTex;

			float2 uv_NormalMap;

			float3 worldRefl;

			half3 TtoW0; half3 TtoW1; half3 TtoW2;
			
		};

法线 Shader 已经正常工作啦




B:在Input 中 添加 float3 worldRefl 和 INTERNAL_DATA ,给o.Normal 赋值的话,就可以 用 WorldReflectionVector(IN,o.Normal) 获取到 法线贴图 计算后的 反射向量。

原文是这样写的

在Input 结构体中还有更多的内置函数,其中一部分如下:

float3 viewDir :

Will contain view direction, for computing Parallax effects, rimlighting, and so on.

float4 COLOR :

Will contain interpolated per-vertex color.

float4 screenPos:

Will contain screen-space position for reflection effects. Used by WetStreet shader in Dark Unity, for example.

float3 worldPos :

Will contain world space position.

float3 worldRefl :

Will contain world reflection vector if Surface Shader does not write to o.Normal. See Reflect-Diffuse shader for example.

float3 worldNormal :

Will contain world normal vector if Surface Shader does not write to o.Normal.

float3 worldRef;INTERNAL_DATA:

Will contain world reflection vector if Surface Shader writes to o.Normal. To get the reflection vector based on per-pixel normal map, use WorldReflectionVector (IN,o.Normal). See Reflect-Bumped shader for example.

float3 worldNormal;INTERNAL_DATA:
Will contain world normal vector if Surface Shader writes to o.Normal. To get the normal vector based on per-pixel normal map, use WorldNormalVector (IN, o.Normal).



3、在 surf 函数中 添加了 读取 法线贴图的代码

//从法线贴图中提取法线信息,UnpackNormal这个函数在 CGInclude 文件夹中的Lighting中。
			float3 normals=UnpackNormal(tex2D(_NormalMap,IN.uv_NormalMap));

4、上一节中直接使用 Input 中的 worldRefl 这个反射向量来获取立方图 采样。这一次需要使用上面说的 WorldReflectionVector 来计算世界反射向量。

//使用WorldReflectionVector 获得 基于法线贴图中的反射向量的 世界反射向量
o.Emission = texCUBE(_Cubemap,WorldReflectionVector(IN,o.Normal)).rgb * _ReflAmount;


示例项目打包下载:

http://pan.baidu.com/s/1c1TcSgS


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值