zoukankan      html  css  js  c++  java
  • 我的第一个法线贴图

      2018.1.16更新:在看一本书时,里面提到了“增强法线贴图的强度”:采样normalTex后,归一化前,让法线分量的x和y乘以一个强度参数:

      normal = normal*fixed3(param,param,1);,然后再归一化,当param>1时,模型更加“凹凸”了,原因是:如果没凹凸,则normal=(0,0,1)(可以参考深入理解法线贴图),x和y乘以一个>1的数后,normal的z分量被“稀释”,即越发地偏离(0,0,1),即光照计算结果变大,所以就更加凹凸了。

      2017.11.8更新:以前不懂tangent.w的含义,读此文得到:“where m = ±1 represents the handedness of the tangent space”,大意是不同模型使用左手或右手螺旋得到B,所以必须根据模型确定B的方向,所以就把这个方向(±1)存到tangent.w里了;

      另外需要提及的是:

      下面的:

      float3x3 objectToTanM = float3x3(tangent, binnormal, normal);//tangent是行向量
      mul(objectToTanM, ObjSpaceLightDir(v.vertex));

      这里有几个点被省略了。因为这里的第二行使用的是矩阵右乘向量,所以构建切线空间基矩阵时应当使用列向量矩阵:[T,B,N](都是列向量),我们要转换向量到切线空间则需使用他的逆,又因为[T, B, N]基本正交,所以直接使用它的转置作为转换矩阵:[T', B', N'](T'是行向量),这就是上面构建objectToTanM这个转到切线空间矩阵所对应的。

      同理,如果第二行使用的是mul(ObjSpaceLightDir(v.vertex), objectToTanM),则是向量左乘变换矩阵,所以空间基应当是行向量矩阵:[T',B',N'],其逆用其转置代替:[T,B,N](都是列向量),这样objectToTanM就需要在原来的基础上转置一下了。

    先上简单的surf版法线贴图:

    Shader "Custom/NormalSurfShader" {
        Properties {
            _Color ("Color", Color) = (1,1,1,1)
            _MainTex ("Albedo (RGB)", 2D) = "white" {}
            _NormalMap("Normal Map", 2D) = "white" {}
        }
        SubShader {
            Tags { "RenderType"="Opaque" }
            LOD 200
            
            CGPROGRAM
            // Physically based Standard lighting model, and enable shadows on all light types
            #pragma surface surf BlinnPhong fullforwardshadows
    
            // Use shader model 3.0 target, to get nicer looking lighting
            #pragma target 3.0
            
            struct Input {
                float2 uv_MainTex;
                float2 uv_NormalMap;
            };
    
            sampler2D _MainTex;
            sampler2D _NormalMap;
            fixed4 _Color;
    
            void surf (Input IN, inout SurfaceOutput o) {
                o.Albedo = tex2D (_MainTex, IN.uv_MainTex) * _Color;
                o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap));
            }
            ENDCG
        }
        FallBack "Diffuse"
    }

    效果:

    然后上顶点shader:

    Shader "Unlit/NormalVFShader"
    {
        //法线贴图是,在主贴图基础上,先算出在切线空间的光照方向lightDir,视线方向viewDir
        //接着,通过Normal Map采样得到各个顶点的在切线空间的法线Normal,然后由此三者根据光照模型
        //算出该点漫反射和高光,则该点的像素值就是diff+specular+ambient(环境光)
        //所以法贴是为了计算漫反射和高光,即为光照模型服务。surf shader把这个过程都封装起来了。
        Properties
        {
            _MainTex ("Texture", 2D) = "white" {}
            _NormalMap("Normal Map", 2D) = "white" {}
            _SpecColor("Specular Color", Color) = (1,1,1,1)
            _Shininess("Shininess", Float) = 228
        }
        SubShader
        {
            Tags { "RenderType"="Opaque" }
            LOD 100
    
            Pass
            {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                
                #include "UnityCG.cginc"
    
                struct appdata
                {
                    float4 vertex : POSITION;
                    float2 uv : TEXCOORD0;
                };
    
                struct v2f
                {
                    float2 uv : TEXCOORD0;
                    float4 vertex : SV_POSITION;
                    float3 lightDir: TEXCOORD1;
                    float3 viewDir  : TEXCOORD2;
                };
    
                sampler2D _MainTex;
                sampler2D _NormalMap;
                float4 _SpecColor;
                float _Shininess;
                uniform float4 _LightColor0;
                float4 _MainTex_ST;
                
                v2f vert (appdata_tan v)
                {
                    v2f o;
                    o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                    o.uv = TRANSFORM_TEX(v.texcoord.xy, _MainTex);
                    //下面的6句是为了得到切线空间的光照方向和视角方向,为什么要转到切线空间?因为法贴中的法线是以切线空间坐标存的,
                    //后面计算漫反射,高光需要他们统一坐标系,所以其实也可以把大家(3者)都转到世界坐标系,但那样效率估计比这样慢一点
                    float3 normal = v.normal;
                    float3 tangent = v.tangent;
                    float3 binnormal = cross(normal, tangent) * v.tangent.w;//为什么乘tangent.w?记住就好
                    float3x3 objectToTanM = float3x3(tangent, binnormal, normal);
                    //TANGENT_SPACE_ROTATION 这个宏可以代替上面4句
                    o.lightDir = mul(objectToTanM, ObjSpaceLightDir(v.vertex));
                    o.viewDir = mul(objectToTanM, ObjSpaceViewDir(v.vertex));
                    return o;
                }
                
                fixed4 frag (v2f i) : SV_Target
                {
                    // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                float3 ambient = col * UNITY_LIGHTMODEL_AMBIENT.rgb;
                fixed4 encodeNormal = tex2D(_NormalMap, i.uv);//切线空间的法线
                fixed4 normal = encodeNormal * 2 - 1;//(0-1)=》(-1,1),
                normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy)));//解压出z
                //漫反射
                // I = k*texColor*LightColor*(NL),这里的texColor我是抄Lighting.cginc里的
                float3 diff = col.rgb * _LightColor0.rgb * max(0, dot(normal, i.lightDir)) * 2;//*2是我自己加的,为了亮一点
                //计算高光,使用Blinn-Phong光照模型
                //                      (ns)
                //I = k*LightColor*(NH) , H = (L+V)/|L+V|
                float3 H = normalize(i.lightDir+i.viewDir);
                float3 specular = _SpecColor * _LightColor0 * pow(max(0, dot(normal, H)), _Shininess);
                return float4(ambient + diff + specular, col.a);
                }
                ENDCG
            }
        }
    }

    效果:

    当然,因为资源、光照等原因,这样的对比是完全不靠谱的,但我想说的是,vfshader是可控的,这点就足够好了,关键注释都在代码里了。

  • 相关阅读:
    空间换时间之反范式设计之路/合理冗余/去除外键
    WebAPI接口设计:SwaggerUI文档 / 统一响应格式 / 统一异常处理 / 统一权限验证
    开放api接口签名验证
    EasyUI开发踩过的坑(EasyUI开发笔记)
    nuget挂了吗?
    C# 实体/集合差异比较,比较两个实体或集合值是否一样,将实体2的值动态赋值给实体1(名称一样的属性进行赋值)
    从应用的角度讲创业公司该如何选择域名?
    疑似easyui本身bug:easyui时间控件问题,试了几个版本都不行
    工作三年对程序的理解特来求证
    控制器读取视图表单中的数据的几种方式
  • 原文地址:https://www.cnblogs.com/Tearix/p/6860641.html
Copyright © 2011-2022 走看看