Shader 内置 Shader 之 Bumped Diffuse 学习

2014-6-23 雨辰 Unity3D

说明:Bumped Diffuse 基于 Lambert 光照模型,同时使用了 normal mapping 技术来增加物体表面细节。相对于通过增加剖分来表现物体表面细节的方式,normal mapping 并不改变物体的形状,而是使用一张称为 Normal Map 的特殊纹理来达到这种效果。在 normal map 中,每个象素的颜色代表了该像素所在物体表面的法线,然后通过这个法线(而不是通过物体模型计算而来的法线)来计算光照。可以说 Normal Map 在计算光照的过程中“高效地修改”了整个模型。
01 Shader "Customer/Bumped Diffuse"
02 {
03     Properties
04     {
05         _Color ("Main Color", Color) = (1,1,1,1) //主色调
06         _MainTex ("Base (RGB)", 2D) = "white" {} //主纹理
07         _BumpMap ("Normalmap", 2D) = "bump" {}   //凹凸纹理
08     }
09     SubShader
10     {
11         Tags { "RenderType"="Opaque" } // 表示渲染非透明对象时调用
12         LOD 300 //着色器的细节层次
13  
14         CGPROGRAM
15         // 声明是表面着色器,并且着色器的方法名是 surf,光照模型是内置 Lambert
16         #pragma surface surf Lambert
17  
18         fixed4 _Color; //与 Properties 的 _Color 对应
19         sampler2D _MainTex; //与 Properties 的 _MainTex 对应
20         sampler2D _BumpMap; //与 Properties 的 _BumpMap 对应
21          
22         //输入结构
23         struct Input
24         {
25             float2 uv_MainTex; //纹理贴图,与 Properties 的 _MainTex 对应
26             float2 uv_BumpMap; //纹理贴图,与 Properties 的 _BumpMap 对应
27         };
28  
29         void surf (Input IN, inout SurfaceOutput o)
30         {
31             fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
32             o.Albedo = c.rgb; //光源反射率(颜色值)
33             o.Alpha = c.a; //透明度
34              
35             //说明:UnpackNormal(fixed4 packednormal) 标准法线解压函数存储在 UnityCG.cginc 中
36             //inline fixed3 UnpackNormal(fixed4 packednormal)
37             //{
38             //  #if defined(SHADER_API_GLES) && defined(SHADER_API_MOBILE)
39             //      return packednormal.xyz * 2 - 1;
40             //  #else
41             //      fixed3 normal;
42             //      normal.xy = packednormal.wy * 2 - 1;
43             //      normal.z = sqrt(1 - normal.x*normal.x - normal.y * normal.y);
44             //      return normal;
45             //  #endif
46             //}
47              
48             o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));  //法线
49         }
50         ENDCG 
51     }
52     FallBack "Diffuse"
53 }

以下选自风宇冲博客,部分内容有修改,原文链接:http://blog.sina.com.cn/s/blog_471132920101dij9.html

法线贴图的实现

将材质贴图对应的法线绘制在一张贴图上,将贴图对应点的单位法线向量信息 float3(x,y,z) 储存在图对应的颜色 color(r,g,b) 里,其中法线向量的 x、y、z 分别对应颜色的 r、g、b。单位法线向量 float3(x,y,z) 的 x、y、z 取值范围是 -1 至 1 。被压缩在颜色空间里的法线范围是 0 至 1,所以需要通过计算来进行转换:
颜色 = 0.5 * 法线 + 0.5;
法线 = 2 * (颜色 - 0.5);

法线贴图的使用(主要步骤)

1、对法线贴图进行采样,取得压缩在颜色空间里的法线(取值范围是 0 至 1)float4 packedNormal = tex2D(_NormalMap, IN.uv_MainTex);
2、将压缩在颜色空间里的法线转换至 3D 空间中(取值范围是 -1 至 1)float3 expand(float3 v) { return (v - 0.5) * 2; }
3、之后使用该法线即可

压缩法线贴图的原理

法线 (x,y,z) 是一条单位向量,故x2 + y2 +z2 = 1,所以知道了 x、y、z 里的任意两个,剩下的那个就可以通过计算得出。所以我们可以使用 2 个通道的图储存 x、y、z 里的两个值,将 x、y、z 里剩余的值省略,剩下的可以通过计算得出。

压缩法线贴图的好处

压缩后的法线贴图,大小只有原来的1/4左右,故可以使用更大或者更多的贴图来提升画面品质。

Unity3D 中的法线贴图

Unity3D 使用的压缩法线贴图是 DXT5nm 格式的。有 A 和 G 两个通道。相对于法线 (x,y,z),A 对应法线中的 x,G 对应法线中的 y。UnityCG.cginc 中的 UnpackNormal 函数定义的如果是移动平台或者OpenGL ES,那么断定使用的是 RGB 法线贴图,否则则为 DXT5nm 贴图。但实际上移动平台也可以用压缩格式的法线贴图,而 Windows 也能使用 RGB 法线贴图。故不建议使用 UnpackNormal 函数,建议根据法线贴图的具体格式来使用自己写的对应函数。

1、RGB 法线贴图

1 float3 expand(float3 v) { return (v - 0.5) * 2; }

2、DXT5nm 法线贴图

1 float3 expand(float4 v)
2 {
3     fixed3 normal;
4     normal.xy = v.wy * 2 - 1;
5     normal.z = sqrt(1 - normal.x * normal.x - normal.y * normal.y);
6     return normal;
7 }

3、在例子中使用

1 float4 packedNormal = tex2D(_NormalMap, IN.uv_MainTex);
2 o.Normal = expand(packedNormal.xyz);

标签: Unity3D-优化性能 Unity3D-Editer Unity3D-Shader Unity3D

发表评论:

雨辰 joyimp|@2011-2018 京ICP备16030765号