《Unity Shader》7.2.3 实践 在切线空间下计算

( 1)在Unity中新建一个场景。在本书资源中,该场景名为Scene_7_2_3。在Unity 5.2中,默认情况下场景将包含一个摄像机和一个平行光,并且使用了内置的天空盒子。在Window ->Lighting -> Skybox中去掉场景中的天空盒子。

点击 window-rendering-lighting,点击 environment,点击 skybox material

(2)新建一个材质。在本书资源中,该材质名为NormalMapTangentSpaceMat。

(3)新建一个Unity Shader。在本书资源中,该Unity Shader名为Chapter7-NormalMapTangentSpace。把新的Unity Shader赋给第2步中创建的材质。

(4)在场景中创建一个胶囊体,并把第2步中的材质赋给该胶囊体。

(5)保存场景。

打开新建的Chapter7-NormalMapTangentSpace,删除所有已有代码,并进行如下修改。

(1)首先,我们为该Unity Shader定义一个名字:

(2)然后,我们在Properties语义块中添加了法线纹理的属性,以及用于控制凹凸程度的属性:

(3)我们在SubShader语义块中定义了一个Pass语义块,并且在Pass的第一行指明了该Pass的光照模式:

(4)接着,我们使用CGPROGRAM和ENDCG来包围住CG代码片,以定义最重要的顶点着色器和片元着色器代码。首先,我们使用#pragma指令来告诉Unity,我们定义的顶点着色器和片元着色器叫什么名字。在本例中,它们的名字分别是vert和frag:

(5)为了使用Unity内置的一些变量,如_LightColor0,还需要包含进Unity的内置文件Lighting.cginc:

(6)为了和Properties语义块中的属性建立联系,我们在CG代码块中声明了和上述属性类型匹配的变量:

(7)我们已经知道,切线空间是由顶点法线和切线构建出的一个坐标空间,因此我们需要得到顶点的切线信息。为此,我们修改顶点着色器的输入结构体a2v:

(8)我们需要在顶点着色器中计算切线空间下的光照和视角方向,因此我们在v2f结构体中添加了两个变量来存储变换后的光照和视角方向:

(9)定义顶点着色器:

(10)由于我们在顶点着色器中完成了大部分工作,因此片元着色器中只需要采样得到切线空间下的法线方向,再在切线空间下进行光照计算即可:

(11)最后,我们为该Unity Shader设置合适的Fallback:

java 复制代码
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Custom/Chapter7-NormalMapTangentSpace"{
    Properties {
        _Color  ("Color  Tint",  Color)  = (1,1,1,1)
        _MainTex  ("Main  Tex",  2D)  =  "white"  {}
        _BumpMap  ("Normal  Map",  2D)  =  "bump"  {}
        _BumpScale  ("Bump  Scale",  Float)  =  1.0
        _Specular  ("Specular",  Color)  =  (1,  1,  1,  1)
        _Gloss  ("Gloss",  Range(8.0,  256))  =  20
    }

    SubShader {
        Pass {
            Tags  {  "LightMode"="ForwardBase"  }
            CGPROGRAM
            #pragma  vertex  vert
            #pragma  fragment  frag
            #include  "Lighting.cginc"

            fixed4  _Color;
            sampler2D  _MainTex;
            float4  _MainTex_ST;
            sampler2D  _BumpMap;
            float4  _BumpMap_ST;
            float  _BumpScale;
            fixed4  _Specular;
            float  _Gloss;

            struct  a2v  {
                float4  vertex  :  POSITION;
                float3  normal  :  NORMAL;
                float4  tangent  :  TANGENT;
                float4  texcoord  :  TEXCOORD0;
            };

            struct  v2f  {
                float4  pos  :  SV_POSITION;
                float4  uv  :  TEXCOORD0;
                float3  lightDir:  TEXCOORD1;
                float3  viewDir  :  TEXCOORD2;
            };

            v2f vert(a2v  v)  {
                v2f  o;
                o.pos  =  UnityObjectToClipPos(v.vertex);
                o.uv.xy  =  v.texcoord.xy  *  _MainTex_ST.xy  +  _MainTex_ST.zw;
                o.uv.zw  =  v.texcoord.xy  *  _BumpMap_ST.xy  +  _BumpMap_ST.zw;
                //  Compute  the  binormal  
                //float3   binormal   =  cross( normalize(v.normal),  normalize(v.tangent.xyz)  ) * v.tangent.w;
                //  Construct  a  matrix  which  transform  vectors  from  object  space  to  tangent  space //   float3x3  rotation  =  float3x3(v.tangent.xyz,  binormal,  v.normal);
                // use  the  built-in  macro
               TANGENT_SPACE_ROTATION;
                //  Transform  the  light  direction  from  object  space  to  tangent  space
                o.lightDir  =  mul(rotation,  ObjSpaceLightDir(v.vertex)).xyz;
                //  Transform  the  view  direction  from  object  space  to  tangent  space
                o.viewDir  =  mul(rotation,  ObjSpaceViewDir(v.vertex)).xyz;
                return  o;
            }

            fixed4  frag(v2f  i)  :  SV_Target  {
                fixed3  tangentLightDir  =  normalize(i.lightDir);
                fixed3  tangentViewDir  =  normalize(i.viewDir);
                //  Get  the  texel  in  the  normal  map
                fixed4  packedNormal  =  tex2D(_BumpMap,  i.uv.zw); //法线纹理中存储的是把法线经过映射后得到的像素值
                fixed3  tangentNormal;
                //  If  the  texture  is  not  marked  as  "Normal  map",
                //tangentNormal.xy  =  (packedNormal.xy *  2  -  1)  *_BumpScale;  //把packedNormal的xy分量按之前提到的公式映射回法线方向,然后乘以_BumpScale(控制凹凸程度)来得到tangentNormal的xy分量
                //tangentNormal.z  =  sqrt(1.0  -  saturate(dot(tangentNormal.xy,  tangentNormal.xy))); //由于法线都是单位矢量,因此tangentNormal.z分量可以由tangentNormal.xy计算而得
                // "Normal  map",  and  use  the  built-in  funciton
                tangentNormal  =  UnpackNormal(packedNormal); //纹理类型标识成Normal map,使用Unity的内置函数UnpackNormal来得到正确的法线方向。
                tangentNormal.xy  *=  _BumpScale;
                tangentNormal.z  =  sqrt(1.0  -  saturate(dot(tangentNormal.xy,  tangentNormal.xy)));

                fixed3  albedo  =  tex2D(_MainTex,  i.uv).rgb  *  _Color.rgb;
                fixed3  ambient  =  UNITY_LIGHTMODEL_AMBIENT.xyz  *  albedo;
                fixed3  diffuse    =    _LightColor0.rgb    *    albedo    *    max(0, dot(tangentNormal,tangentLightDir));
                fixed3  halfDir  =  normalize(tangentLightDir  +  tangentViewDir);
                fixed3  specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(tangentNormal,halfDir)),  _Gloss);
                return  fixed4(ambient  +  diffuse  +  specular,  1.0);
            }

            ENDCG
        }
        
    }
    Fallback "Specular"
}

保存后返回Unity中查看。在NormalMapTangentSpaceMat的面板上,我们使用本书资源中的Brick_Diffuse.jpg和Brick_Normal.jpg纹理对其赋值。我们可以调整材质面板中的Bump Scale属性来改变模型的凹凸程度。

没去找资源就用了 unity 里自带的。

选择 select

相关推荐
在路上看风景6 小时前
1.5 Material
unity
WarPigs1 天前
Unity红点系统笔记
unity·游戏引擎
郭逍遥1 天前
[Godot] C#基于噪声的简单TileMap地图生成
游戏引擎·godot
作孽就得先起床1 天前
unity UnauthorizedAccessException: 拒绝访问路径
unity·游戏引擎
tealcwu1 天前
【Unity踩坑】Unity项目提示文件合并有冲突
elasticsearch·unity·游戏引擎
tealcwu2 天前
【Unity小技巧】如何将3D场景转换成2D场景
3d·unity·游戏引擎
全栈陈序员2 天前
用Rust和Bevy打造2D平台游戏原型
开发语言·rust·游戏引擎·游戏程序
鹿野素材屋2 天前
Unity模型中人形角色的嘴巴一直开着怎么办
unity
世洋Blog2 天前
Unity面经-List底层原理、如何基于数组、如何扩容、List存储泛型、List有关在内存中的结构
unity·面试·c#·list