您的位置:首页 > 其它

shader实例(二十)法线贴图实现凹凸效果

2016-04-01 02:55 330 查看
上一篇讲了关于法线贴图的存储和使用,这一篇主要是学习法线贴图的整体实现过程。surf版的代码封装太多太简洁,我们学不到什么就不贴了,所以就找了顶点片段程序的shader进行学习。

 

1.基础概念

切线空间:以顶点为中心点的坐标系统

漫反射光照模型,反射高光模型,如下图:





2.实现原理

都在代码中...

 

3.具体实现

VS的编辑器颜色感觉比较清晰,就直接截图了。

顶点程序:





片段程序:





源码:

Shader "Custom/UsingNormalMaps" {

 Properties {

  _MainTex ("Base (RGB)", 2D) = "white" {}

  _Bump ("Bump", 2D) = "bump" {}

  _Specular ("Specular", Range(1.0, 500.0)) = 250.0

  _Gloss ("Gloss", Range(0.0, 1.0)) = 0.2

 }

 SubShader {

  Tags { "RenderType"="Opaque" }

  LOD 200

   

  Pass {

   Tags { "LightMode" = "ForwardBase" }

     

   CGPROGRAM

     

   #pragma vertex vert

   #pragma fragment frag

   #pragma multi_compile_fwdbase

      

   #include "UnityCG.cginc"

   #include "Lighting.cginc"

   #include "AutoLight.cginc"

     

   uniform float4x4 _LightMatrix0; // 引入光矩阵

   sampler2D _MainTex;

   sampler2D _Bump;

   float _Specular;

   float _Gloss;

   float4 _MainTex_ST;

     

   struct a2v {

    float4 vertex : POSITION;  // 输入的模型顶点信息

    fixed3 normal : NORMAL;   // 输入的法线信息

    fixed4 texcoord : TEXCOORD0; // 输入的坐标纹理集

    fixed4 tangent : TANGENT;  // 切线信息

   };

     

   struct v2f {

    float4 pos : POSITION; // 输出的顶点信息

    fixed2 uv : TEXCOORD0; // 输出的UV信息

    fixed3 lightDir: TEXCOORD1; // 输出的光照方向

    fixed3 viewDir : TEXCOORD2; // 输出的摄像机方向

    //LIGHTING_COORDS(3,4) // 封装了下面的写法

    float3 _LightCoord : TEXCOORD3;  // 光照坐标

    float4 _ShadowCoord : TEXCOORD4; // 阴影坐标

   };

     

   v2f vert(a2v v) {

    v2f o;

    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

    o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);

       

    // 创建一个正切空间的旋转矩阵,TANGENT_SPACE_ROTATION由下面两行组成

    //TANGENT_SPACE_ROTATION;

    float3 binormal = cross( v.normal, v.tangent.xyz ) * v.tangent.w;

    float3x3 rotation = float3x3( v.tangent.xyz, binormal, v.normal );

    

    // 将顶点的光方向,转到切线空间

    // 该顶点在对象坐标中的光方向向量,乘以切线空间旋转矩阵

    o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));

    // 该顶点在摄像机坐标中的方向向量,乘以切线空间旋转矩阵

    o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex));

       

    // 将照明信息给像素着色器,应该是用于下面片段中光衰弱atten的计算

      // TRANSFER_VERTEX_TO_FRAGMENT(o); // 由下面两行组成

    // 顶点转到世界坐标,再转到光坐标

    o._LightCoord = mul(_LightMatrix0, mul(_Object2World, v.vertex)).xyz;

    // 顶点转到世界坐标,再从世界坐标转到阴影坐标

    o._ShadowCoord = mul(unity_World2Shadow[0], mul(_Object2World, v.vertex));

    // 注:把上面两行代码注释掉,也看不出上面效果,或许我使用的是平行光

    return o;

   }

     

   fixed4 frag(v2f i) : COLOR {

    // 对主纹理进行采样

    fixed4 texColor = tex2D(_MainTex, i.uv);

    // 对法线图进行采样

    fixed3 norm = UnpackNormal(tex2D(_Bump, i.uv));

    // 光衰弱,卧槽,里面封装了比较深,暂时看不进去,就不拆开了

    fixed atten = LIGHT_ATTENUATION(i);

    // 环境光,Unity内置

    fixed3 ambi = UNITY_LIGHTMODEL_AMBIENT.xyz;

    // 求漫反射

    // 公式:漫反射色 = 光颜色*N,L的余弦值(取大于0的),所以夹角越小亮度越小

    fixed3 diff = _LightColor0.rgb * saturate (dot (normalize(norm),  normalize(i.lightDir))) * 2;

    // 计算反射光线向量

    // 公式:reflect(入射光方向,法线向量)

    fixed3 refl = reflect(-i.lightDir, norm);

    // 计算反射高光

    // 公式:反射高光 = 光颜色 * 【(反射光向量,摄像机方向向量)的余弦值】的【高光指数_Specular】次方 * 光泽度

    fixed3 spec = _LightColor0.rgb * pow(saturate(dot(normalize(refl), normalize(i.viewDir))), _Specular) * _Gloss;

    // 最终颜色

    // 公式:(环境光 + (漫反射 + 反射高光) * 光衰弱 ) * 材质主色

    fixed4 fragColor;

    fragColor.rgb = float3((ambi + (diff + spec) * atten) * texColor);

    fragColor.a = 1.0f;

    return fragColor;

    }

    ENDCG

  }

 }

 FallBack "Diffuse"

}

本文转载自:http://blog.sina.com.cn/s/blog_89d90b7c0102vedb.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: