ShaderLab学习小结(十五)法线贴图的简单Shader-创新互联

目标:赋予材质法线贴图,并能响应光照的变化,体现出凹凸感。
场景中只有一个主平行光
找了一张法线贴图(网上荡的)
ShaderLab学习小结(十五)法线贴图的简单Shader
在unity里别忘了把这张图设为normalmap
ShaderLab学习小结(十五)法线贴图的简单Shader
先看一下,如果只是作为普通贴图,赋在Diffuse材质上是啥效果
ShaderLab学习小结(十五)法线贴图的简单Shader
ShaderLab学习小结(十五)法线贴图的简单Shader
转动平行光,看看有啥变化
ShaderLab学习小结(十五)法线贴图的简单Shader
如上图,只是普通的贴图,随着平行光的转动全体变暗变亮,没有凹凸可言,平面就是平面
那就要编个shader来实现这张法线贴图的价值了

创新互联建站是一家专业提供宁远企业网站建设,专注与成都网站设计、网站制作、H5页面制作、小程序制作等业务。10年已为宁远众多企业、政府机构等服务。创新互联专业网站建设公司优惠进行中。
Shader "Custom/TestBumpShader" {
    Properties {
        _NormalMap("Bump", 2D)=""{}      //1.
    }
    SubShader {
        pass{
            Tags{"LightMode"="ForwardBase"}     //2.
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "unitycg.cginc"
            #include "lighting.cginc"
            sampler2D _NormalMap;
            struct v2f{                                        //3.
                float4 pos:POSITION;
                float2 uv:TEXCOORD0;
                float3 lightdir:TEXCOORD1;
                float3 wpos:TEXCOORD2;
            };
            v2f vert(appdata_tan v)
            {
                v2f o;
                o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
                o.wpos=mul(unity_ObjectToWorld, v.vertex).xyz;
                o.uv=v.texcoord.xy;

                //4.
                TANGENT_SPACE_ROTATION;
                float3 objLight=ObjSpaceLightDir(v.vertex);
                o.lightdir=mul(rotation,objLight);
                return o;
            }
            fixed4 frag(v2f IN):COLOR
            {
                float3 N =normalize(UnpackNormal(tex2D(_NormalMap,IN.uv)));   //5.
                float3 L = normalize(IN.lightdir);
                float ndotl=saturate(dot(N,L));
                fixed4 col = _LightColor0*ndotl;

                col+=UNITY_LIGHTMODEL_AMBIENT;
                return col;
            }
            ENDCG
        }
    }
}

按照代码里的注释位置

1

_NormalMap("Bump", 2D)=""{}      //1.

定义一个贴图,当然,下面的CG程序中要声明一下

2

法线贴图就必然涉及到光照,还是用forwardbase吧

3

struct v2f{                                        //3.
                float4 pos:POSITION;
                float2 uv:TEXCOORD0;
                float3 lightdir:TEXCOORD1;
                float3 wpos:TEXCOORD2;
            };

结构体中定义一个uv对应纹理,一个lightdir是光照方向,一个wpos是世界坐标

4

世界坐标的计算和uv值的获取就不说了,以前有,代码里也有
为了使法线贴图产生作用,要在切线空间中进行计算,这里要把光照向量L转到切线空间

TANGENT_SPACE_ROTATION;
float3 objLight=ObjSpaceLightDir(v.vertex);
o.lightdir=mul(rotation,objLight);

这里的TANGENT_SPACE_ROTATION定义在unitycg.cginc中

// Declares 3x3 matrix 'rotation', filled with tangent space basis
#define TANGENT_SPACE_ROTATION \
    float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) ) * v.tangent.w; \
    float3x3 rotation = float3x3( v.tangent.xyz, binormal, v.normal )

所以前面忘说的一点就是vert函数的参数不用appdata_base了,而改用appdata_tan,因为TANGENT_SPACE_ROTATION中用到了v.tangent,这个在appdata_base中是没有的。
然后用这里的这个rotation去变换模型坐标空间中的光照方向,将其变换到切线空间中,得到我们要用的光照向量

5

float3 N =normalize(UnpackNormal(tex2D(_NormalMap,IN.uv)));   //5.

这里要用unpacknormal函数来通过法线贴图计算法线,而不是根据物体的坐标值计算
这个UnpackNormal也是定义在unitycg.cginc中
看到网上有人的解释:

//将材质贴图对应的法线 绘制在一张贴图上
//将贴图对应点的单位法线向量信息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]中,所以需要转换

原因我也不太明,还在学习中,反正先当固定用法记吧
再用这个处理后的法线向量和光向量进行漫反射计算就得出最终颜色。
我们看下效果:
ShaderLab学习小结(十五)法线贴图的简单Shader
可以看到明暗变化显出凹凸,而不是平面了,其实模型还是平面,这就是视觉欺骗吧。

另外有需要云服务器可以了解下创新互联cdcxhl.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。


分享文章:ShaderLab学习小结(十五)法线贴图的简单Shader-创新互联
标题网址:http://ybzwz.com/article/doicij.html