UnityShader个人学习笔记2

UnityShader笔记2

Shader "BlinnPhone_Lsh"{
    Properties{
        //Tips : 不要加;
        _Diffuse("Diffuse",Color) = (1.0,1.0,1.0,1.0)
        _Spacular("Spacular",Color) = (1.0,1.0,1.0,1.0)
        _Gloss("Gloss",Range(8.0,255)) = 10   
    }

    SubShader{
        Pass{
            Tags{"LightMode" = "ForwardBase"}

            CGPROGRAM
            
            //Tips : 不要加;
            #include "Lighting.cginc"
			
            #pragma vertex vert
            #pragma fragment frag

            fixed4 _Diffuse;
            fixed4 _Spacular;
            float _Gloss;

            //使用appdata_full,可以少写一个结构体

            struct v2f{
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
            };

            v2f vert(appdata_full v){
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);

                o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);

                o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
                

                return o;
            }
			
            //SV_Target作为语义,一定不能落下
            fixed4 frag(v2f i): SV_Target{
                //ambient term
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

                //diffuse term
                fixed3 diffuse = _Diffuse.rgb * _LightColor0.rgb * max(0,dot(worldNormal,worldLightDir));

                //fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
                //fixed3 viewDir = _WorldSpaceCameraPos.xyz;
                fixed3 hlafDir = normalize(viewDir + worldLightDir);

                //specular term
                fixed3 specular = _Spacular.rgb * _LightColor0.rgb * pow(max(0,dot(worldNormal,hlafDir)),_Gloss);

                return fixed4(ambient + diffuse + specular,1.0);
            }

            ENDCG
        }
    }

    Fallback "Specular"

}
  1. 注意到这两个,很像,但是明显不同

    一个是法线的转换,一个是位置的转换,那么为什么不同写法?

           o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
           o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;

​ 回答,在查看资料的时候,有人提到这个函数UnityObjectToWorldNormal

// Transforms normal from object to world space
inline float3 UnityUnityObjectToWorldNormal( in float3 norm )
{
#ifdef UNITY_ASSUME_UNIFORM_SCALING
    return UnityObjectToWorldDir(norm);
#else
    // mul(IT_M, norm) => mul(norm, I_M) => {dot(norm, I_M.col0), dot(norm, I_M.col1), dot(norm, I_M.col2)}
    return normalize(mul(norm, (float3x3)unity_WorldToObject));
#endif
}
 
// Transforms direction from object to world space
inline float3 UnityObjectToWorldDir( in float3 dir )
{
    return normalize(mul((float3x3)unity_ObjectToWorld, dir));
}

明显里面有一个normalize(mul(norm, (float3x3)unity_WorldToObject)),作为其中一个选择来做,那么看来官方肯定也是这么去写的,代表着如果模型是非等比缩放的情况,所以UnityShader里面这样写也是有这个原因:避免模型是非等比缩放带来的影响。

为什么等比缩放会导致法线向量不在垂直模型表面?直观的看下图,三角形经过非等比缩放(0.6后,法线N已经不垂直模型表面了(图中,T是切线向量,N是法线向量)

img

为什么?(https://blog.csdn.net/zhetianyun/article/details/103054640)

  • 我们知道,一个向量可以表示成两点之间的差,以切线向量T为例,T可以表示成在图中三角形斜边上的两个点P1和P2之差:

T = P_2 - P_1

  • 向量可以表示成一个四元数表达的方式(x,y,z,w)其中w 设置为0.所以我们可以在上面表达式的基础上,左边和右边都右乘以一个ModelView 矩阵:

T * Modelview = (P_2-P_1)* Modelview

根据乘法规则,展开得到:

T * Modelview = P_2 * Modelview - P_1 * Modelview

简化得到:

T' = P_2' - P_1'

其中P_1'P_2'是三角形经过矩阵变换后的斜边上的点,T'由上图右边可见,保持这斜边切线不变。因此可以看出,ModelView矩阵保持了切线性质,而法线方向确不在垂直三角形斜边了。

同样的,法线向量N可以表示成
N = Q_2 - Q_1。主要问题是,通过转换变换后,点定义的向量不一定保持单位向量,如上图所示。法向量不是定义为两点之间的差,而是定义垂直于一个平面的向量。所以我们再单纯的乘以ModelView来处理非等比缩放来变换法线向量了。

解决思路

  • 假设有一个3x3矩阵G,我们来看看如果用这个矩阵来变换一个法线向量。
  • 因为法线向量N和切线向量垂直,所以T.N = 0,经过变幻后,N‘T’理应等于0,才是正常的。有上面的分析可以知道,T‘变换后保持了切线的方向正确性,N‘则 不在垂直表面,所以在非等比缩放下N’T’ = 0不在成立。
  • 现在我们在引入一个3x3矩阵M,切线向量T乘以该矩阵能够正常得到T’。在假设法线向量N能通过和第一步假设的矩阵G相乘得到一个向量N’,使得N’T’ =0;那么就有如下公式成立

N' . T' = (GN) . (MT) = 0

  • 使用矩阵乘法,两个向量的点击,可以写成a·b=aT*b,这里的aT指示矩阵a的转置。则上面的公式可以写成如下形式

    (GN).(MT) = (GN)^T * (MT)

  • 我们还知道矩阵乘法的转置等于矩阵转置的乘法,则有下列公式成立

(GN)^T (MT) = NTGTMT

  • 我们假设成立,I是单位向量,综上则有

  • 通过非等比缩放我们想要的结果是让变换后的法线向量N’满足下面等式

N'.T' = N.T = 0= N^TITG^TM = I

所以问题就转化成求出G,也就是矩阵G就是在非等比缩放下,把法线向量N转化为垂直模型边的向量N’的矩阵。推导如下:

G^TM M-1 = IM-1

G^T = M-1

G = (M-1)^T

至此我们得出了在非等比缩放下,变化法线向量的矩阵G就等于矩阵M的逆矩阵的转置。

如果矩阵M是正交矩阵,则我们又有:

M^{-1} = M^T \Longrightarrow G = M

总结

现在再回头来看看

在这里,

在等比缩放模式下nity_ObjectToWorld是我们的M矩阵,MN则得出正确的变换后的法向量N^',如下面源码所示

mul((float3x3)unity_ObjectToWorld, norm)

在非等比缩放模式下,unity_WorldToObject则是M-1

GN = (M-1)^TN= N^'而源码则调用的是mul(v,M)这个方法,参数v是向量,M是矩阵。同样一个向量和矩阵相乘,根据矩阵和向量相乘规则,mul(v,M)和mul(M,v)是在内部做了 一个转置处理。所以

    mul(norm, (float3x3)unity_WorldToObject);

这里的mul(n,M-1)就相当于了GN = (M-1)^TN= N^'

参考资料:http://www.lighthouse3d.com/tutorials/glsl-12-tutorial/the-normal-matrix/

所以直接用官方给的就完事(原理要了解,面试的时候被问过法线的转换)

UnityUnityObjectToWorldNormal( in float3 norm )

以及还有坐标点

 o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;

得出的是物体模型上的顶点对应的世界坐标。

在顶点着色器中,模型、观察、裁剪空间的相关变换矩阵一般为以下几个:

别名定义含义
UNITY_MATRIX_Munity_ObjectToWorld模型变换矩阵
UNITY_MATRIX_Vunity_MatrixV视图变换矩阵
UNITY_MATRIX_Pglstate_matrix_projection投影变换矩阵
UNITY_MATRIX_VPunity_MatrixVP视图投影变换矩阵
UNITY_MATRIX_MVmul(unity_MatrixV, unity_ObjectToWorld)模型视图变换
UNITY_MATRIX_MVPmul(unity_MatrixVP, unity_ObjectToWorld)模型视图投影变换
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值