unity shader学习 NormalMapTangentSpace

本文详细介绍了如何在Unity Shader中使用法线贴图技术,特别是在切线空间下进行光照计算的过程。通过具体的Shader代码解析,展示了如何构建切线空间变换矩阵,以及如何从法线贴图中读取并处理法线信息,最终实现高质量的表面细节表现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shader Book/Chapter 7/NormalMapTangentSpace"
{
    Properties
    {
        _Diffuse("Diffuse", 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, 256)) = 20
    }
        SubShader
        {
            Pass
            {
                Tags{ "LightMode" = "ForwardBase" }

                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #include "Lighting.cginc"

                struct a2v
                {
                    float4 vertex : POSITION;   //顶点位置
                    float3 normal : NORMAL;     //顶点方向
                    float4 tangent : TANGENT;   //顶点切线方向  tangent.w 分量来决定切线空间中的第三个坐标轴(福切线的方向性)
                    float4 texcoord : TEXCOORD0;//纹理坐标
                };

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

                fixed4 _Diffuse;
                sampler2D _MainTex;
                sampler2D _BumpMap;
                float _BumpScale;
                float4 _MainTex_ST;  //纹理名_ST 声明某个纹理的属性   纹理名_ST.xy:缩放值     纹理名_ST.zw:偏移值
                float4 _BumpMap_ST;  //纹理名_ST 声明某个纹理的属性   纹理名_ST.xy:缩放值     纹理名_ST.zw:偏移值
                fixed4 _Specular;
                float _Gloss;

                v2f vert(a2v v)
                {
                    v2f o;
                    o.pos = UnityObjectToClipPos(v.vertex); //当前模型的位置
                    //主贴图MainTex uv坐标 = 当前模型的缩放 * 贴图的缩放 + 偏移值
                    o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                    //凹凸图BumpMap uv坐标 = 当前模型的缩放 * 贴图的缩放 + 偏移值
                    o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;
                    // o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                    //副法线 = 叉积(模型顶点方向, 纹理坐标方向) * 副切线
                    float3 binormal = cross(normalize(v.normal), normalize(v.tangent.xyz)) * v.tangent.w;
                    //变换矩阵 = float3x3(纹理坐标, 副法线, 模型顶点方向);
                    float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal);
                    //TANGENT_SPACE_ROTATION;

                    //灯光投影方向 = 矩阵(变换矩阵,光照方向)  矩阵和向量相乘
                    o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));
                    //视角投影方向 = 矩阵(变换矩阵,视角方向) 
                    o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex));

                    return o;
                }

                fixed4 frag(v2f i) : SV_Target
                {
                    fixed3 viewDir = normalize(i.viewDir);  //统一化视角方向
                    fixed3 lightDir = normalize(i.lightDir);//统一化灯光方向
                    //法线纹理采样
                    fixed4 packedNormal = tex2D(_BumpMap, i.uv.zw);
                    //UnpackNormal()函数是对法线纹理的采样结果的一个反映射操作,其对应的法线纹理需要设置为Normal map的格式,才能使用该函数
                    fixed3 tangentNormal = UnpackNormal(packedNormal); //获取法线映射方向
                    //映射方向 * 凹凸程度
                    tangentNormal.xy *= _BumpScale;
                    //点积:点乘的几何意义是可以用来表征或计算两个向量之间的夹角,以及在b向量在a向量方向上的投影
                    //法线都是单位矢量,tangentNormal.z分量可以由tangentNormal.xy 计算而得。
                    //使用的是切线空间下的法线纹理,因此可以保证法线方向的z分量为正
                    tangentNormal.z = sqrt(1 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
                    //主贴图颜色 + 设置颜色 = 自身反照颜色
                    fixed3 albedo = tex2D(_MainTex, i.uv).xyz * _Diffuse.xyz;
                    //周围环境颜色 = 系统光源颜色(天空盒子) * 自身反照颜色
                    fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                    //  漫反射颜色 = 灯光颜色 * 反照颜色 * 法线映射方向到灯光投影方向角度(0-1取值)
                    fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(tangentNormal, lightDir));
                    //反射方向
                    fixed3 reflectDir = normalize(reflect(-lightDir, tangentNormal));
                    //反射计算 灯光颜色 * 设置颜色 * pow(反射角度(取值范围0~1)) = 镜面高光反射颜色
                    fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
                    //周围环境颜色 + 漫反射颜色 + 高光反射颜色
                    return fixed4(ambient + diffuse + specular, 1);
                }
                ENDCG
            }
        }

            Fallback "Diffuse"
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值