《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

相关推荐
mxwin12 小时前
Unity Shader 手写基于 PBR 的 URP Lit Shader 核心光照计算
unity·游戏引擎·shader
小贺儿开发12 小时前
Unity3D 智能云端数字标牌系统
unity·阿里云·人机交互·视频·oss·广告·互动
魔士于安13 小时前
Unity windows 同步 异步 打开文件文件夹工具
游戏·unity·游戏引擎·贴图·模型
笑虾13 小时前
cocos2d-x lua 加载 Cocos Studio 导出的 csb
游戏引擎·lua·cocos2d
魔士于安13 小时前
unity lowpoly 风格 城市 建筑 道路 交通标志
游戏·unity·游戏引擎·贴图·模型
mxwin13 小时前
Unity GPU Shader 性能优化指南
unity·游戏引擎·shader
董董女友1 天前
unity mcp 配置指南
unity·游戏引擎
垂葛酒肝汤1 天前
Unity的可视化网格和文字标签
unity·游戏引擎
魔士于安1 天前
Unity UI图片 复活节UI,卡通风格
游戏·ui·unity·游戏引擎·材质·贴图
weixin_423995001 天前
unity 团结开发小游戏,加载AssetBundles(第二种方法)
unity·游戏引擎