UnityShader学习教程之<矩阵的左乘还是右乘所导致的效果问题>

总结:矩阵的左乘还是右乘

首先,在《3d数学基础:图形与游戏开发》一书的第七章矩阵的7.1.7节中讲到了关于矩阵和向量的乘法问题。结论是“行向量左乘矩阵时,结果是行向量,列向量右乘矩阵时,结果是列向量,反过来是不行的”,在DirectX中使用的是行向量,在OpenGL中使用的是列向量。

接下来我解析一下在实例中遇到的问题:

Shader "Unlit/任务15光照衰减的两种处理方式"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_BumpMap("BumpMap",2D) = "bump"{}
_BumpScale("BumpScale",Float) = 1 
_Shiniess("Shiniess",Range(1,258)) = 20
_SpecularMap("SpecularMap",2D) = "white"{}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100


Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
sampler2D _MainTex; float4 _MainTex_ST;
sampler2D _BumpMap; float4 _BumpMap_ST; 
sampler2D _SpecularMap; float4 _SpecularMap_ST;
float _BumpScale;
float _Shiniess;
uniform float4x4 _LightMatrix0;
uniform sampler2D _LightTexture0; float4 _LightTexture0_ST;



//定点输出结构体,传给片段入口函数的
struct v2f
{
float4 uv : TEXCOORD0;
float3 normal:TEXCOORD1;
float3 tangent:TEXCOORD2;
float3 binormal:TEXCOORD3;
float3 worldpos:TEXCOORD4;
float3 worldniormal:TEXCOORD5;
float4 vertex : SV_POSITION;
};

//定点入口函数
v2f vert (appdata_full v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;
o.normal = mul(_World2Object,float4(v.normal,0)).xyz;//--计算世界空间的法线
o.tangent = mul(_Object2World,v.tangent).xyz;//--计算世界控件的切线
o.binormal = cross(o.normal,o.tangent) * v.tangent.w;//--用叉乘计算世界空间的副切线
o.worldpos = mul(_Object2World,v.vertex).xyz;//--计算世界空间中的顶点坐标位置
return o;
}

//片段入口函数
fixed4 frag (v2f i) : SV_Target
{
   float3 worldnormal = normalize(i.normal);
float3 worldtangent = normalize(i.tangent);
float3 binormal = normalize(i.binormal);
float3x3 rotation = float3x3(worldtangent,binormal,worldnormal);
float3 worldpos = i.worldpos;
float3 normal = UnpackNormal(tex2D(_BumpMap,i.uv.zw)).xyz;
normal.xy *= _BumpScale;
normal.z = sqrt(1 - saturate(dot(normal.xy,normal.xy)));
normal = normalize(mul(rotation,normal));//--讲提取出来的法线转换到切线空间
float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
float3 albedo = tex2D(_MainTex,i.uv.xy).xyz;
float3 lightdir;//光的方向
float atten;//衰减系数
#ifdef USING_DIRECTIONAL_LIGHT
lightdir = normalize(_WorldSpaceLightPos0).xyz;
atten = 1;
#else
//把衰减贴图的顶点坐标转换到光照空间
lightdir = normalize(_WorldSpaceLightPos0.xyz - worldpos).xyz;
float3 lightCoord = mul(_LightMatrix0,float4(worldpos,1));
atten = tex2D(_LightTexture0,dot(lightCoord,lightCoord).rr).UNITY_ATTEN_CHANNEL;
#endif
//计算视线方向
float3 viewdir = normalize( _WorldSpaceCameraPos.xyz - i.worldpos); 
   float3 diffuse = _LightColor0.rgb * (max(0,dot(normal,lightdir))) * albedo ;
float3 halfdir = reflect(-lightdir,normal);
float spec = saturate(dot(halfdir,viewdir));
//计算高光贴图系数
float specularmask = tex2D(_SpecularMap,i.uv.xy).r;
float3 specular = _LightColor0.rgb * pow(spec,_Shiniess) * specularmask;
float4 finlcolor = float4( diffuse +specular+ ambient,1);
return finlcolor;
}
ENDCG
}
}
}

这里我们在“o.normal = mul(_World2Object,float4(v.normal,0)).xyz;//--计算世界空间的法线”和“normal = normalize(mul(rotation,normal));//--讲提取出来的法线转换到切线空间”是用来处理法线的两个地方,所采用的方向是”矩阵在左,向量在右边“,我们会发现效果如下:

高光会乱跑,而且两面都有高光,我在这儿纠结了很久,后来查了一些资料,发现是矩阵和向量的乘法方向所导致的,一开始还以为计算视线的方法不对最后改成这样之后“o.normal = mul(float4(v.normal,0),_World2Object).xyz”和“normal = normalize(mul(normal,rotation))”效果如下:

发现效果正常了:那么我总结出来的结论是,法线是“矩阵在右边,向量在左边”,其他的是“矩阵在左边,向量在右边”,有人说unity使用的列向量,我试了下,除了法线需要上面的方法之外,其他的向量左右好像影响不大。这里反过来乘的原因是要乘以逆矩阵的反置矩阵,_objecttoworld的逆矩阵就是_worldtoobject,反置矩阵则是用mul反过来乘。

下面是完整的代码:

Shader "Unlit/任务15光照衰减的两种处理方式"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_BumpMap("BumpMap",2D) = "bump"{}
_BumpScale("BumpScale",Float) = 1 
_Shiniess("Shiniess",Range(1,258)) = 20
_SpecularMap("SpecularMap",2D) = "white"{}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100


Pass
{
   Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
sampler2D _MainTex; float4 _MainTex_ST;
sampler2D _BumpMap; float4 _BumpMap_ST; 
sampler2D _SpecularMap; float4 _SpecularMap_ST;
float _BumpScale;
float _Shiniess;
uniform float4x4 _LightMatrix0;
uniform sampler2D _LightTexture0; float4 _LightTexture0_ST;



//定点输出结构体,传给片段入口函数的
struct v2f
{
float4 uv : TEXCOORD0;
float3 normal:TEXCOORD1;
float3 tangent:TEXCOORD2;
float3 binormal:TEXCOORD3;
float3 worldpos:TEXCOORD4;
float3 worldniormal:TEXCOORD5;
float4 vertex : SV_POSITION;
};

//定点入口函数
v2f vert (appdata_full v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;
o.normal = mul(float4(v.normal,0),_World2Object).xyz;//--计算世界空间的法线
o.tangent = mul(_Object2World,v.tangent).xyz;//--计算世界控件的切线
o.binormal = cross(o.normal,o.tangent) * v.tangent.w;//--用叉乘计算世界空间的副切线
o.worldpos = mul(_Object2World,v.vertex).xyz;//--计算世界空间中的顶点坐标位置
return o;
}

//片段入口函数
fixed4 frag (v2f i) : SV_Target
{
   float3 worldnormal = normalize(i.normal);
float3 worldtangent = normalize(i.tangent);
float3 binormal = normalize(i.binormal);
float3x3 rotation = float3x3(worldtangent,binormal,worldnormal);
float3 worldpos = i.worldpos;
float3 normal = UnpackNormal(tex2D(_BumpMap,i.uv.zw)).xyz;
normal.xy *= _BumpScale;
normal.z = sqrt(1 - saturate(dot(normal.xy,normal.xy)));
normal = normalize(mul(normal,rotation));//--讲提取出来的法线转换到切线空间
float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
float3 albedo = tex2D(_MainTex,i.uv.xy).xyz;
float3 lightdir;//光的方向
float atten;//衰减系数
#ifdef USING_DIRECTIONAL_LIGHT
lightdir = normalize(_WorldSpaceLightPos0).xyz;
atten = 1;
#else
//把衰减贴图的顶点坐标转换到光照空间
lightdir = normalize(_WorldSpaceLightPos0.xyz - worldpos).xyz;
float3 lightCoord = mul(_LightMatrix0,float4(worldpos,1));
atten = tex2D(_LightTexture0,dot(lightCoord,lightCoord).rr).UNITY_ATTEN_CHANNEL;
#endif
//计算视线方向
float3 viewdir = normalize( _WorldSpaceCameraPos.xyz - i.worldpos); 
   float3 diffuse = _LightColor0.rgb * (max(0,dot(normal,lightdir))) * albedo *atten;
float3 halfdir = reflect(-lightdir,normal);
float spec = saturate(dot(halfdir,viewdir));
//计算高光贴图系数
float specularmask = tex2D(_SpecularMap,i.uv.xy).r;
float3 specular = _LightColor0.rgb * pow(spec,_Shiniess) * specularmask * atten;
float4 finlcolor = float4( diffuse +specular+ ambient,1);
return finlcolor;
}
ENDCG
}
Pass
{
   Tags{"LightMode" = "ForwardAdd"}
Blend One One
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdadd
#include "UnityCG.cginc"
#include "Lighting.cginc"
sampler2D _MainTex; float4 _MainTex_ST;
sampler2D _BumpMap; float4 _BumpMap_ST; 
sampler2D _SpecularMap; float4 _SpecularMap_ST;
float _BumpScale;
float _Shiniess;
uniform float4x4 _LightMatrix0;
uniform sampler2D _LightTexture0; float4 _LightTexture0_ST;



//定点输出结构体,传给片段入口函数的
struct v2f
{
float4 uv : TEXCOORD0;
float3 normal:TEXCOORD1;
float3 tangent:TEXCOORD2;
float3 binormal:TEXCOORD3;
float3 worldpos:TEXCOORD4;
float3 worldniormal:TEXCOORD5;
float4 vertex : SV_POSITION;
};

//定点入口函数
v2f vert (appdata_full v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;
o.normal = mul(float4(v.normal,0),_World2Object).xyz;//--计算世界空间的法线
o.tangent = mul(_Object2World,v.tangent).xyz;//--计算世界控件的切线
o.binormal = cross(o.normal,o.tangent) * v.tangent.w;//--用叉乘计算世界空间的副切线
o.worldpos = mul(_Object2World,v.vertex).xyz;//--计算世界空间中的顶点坐标位置
return o;
}

//片段入口函数
fixed4 frag (v2f i) : SV_Target
{
   float3 worldnormal = normalize(i.normal);
float3 worldtangent = normalize(i.tangent);
float3 binormal = normalize(i.binormal);
float3x3 rotation = float3x3(worldtangent,binormal,worldnormal);
float3 worldpos = i.worldpos;
float3 normal = UnpackNormal(tex2D(_BumpMap,i.uv.zw)).xyz;
normal.xy *= _BumpScale;
normal.z = sqrt(1 - saturate(dot(normal.xy,normal.xy)));
normal = normalize(mul(normal,rotation));//--讲提取出来的法线转换到切线空间
float3 albedo = tex2D(_MainTex,i.uv.xy).xyz;
float3 lightdir;//光的方向
float atten;//衰减系数
#ifdef USING_DIRECTIONAL_LIGHT
lightdir = normalize(_WorldSpaceLightPos0).xyz;
atten = 1;
#else
//把衰减贴图的顶点坐标转换到光照空间
lightdir = normalize(_WorldSpaceLightPos0.xyz - worldpos).xyz;
float3 lightCoord = mul(_LightMatrix0,float4(worldpos,1));
atten = tex2D(_LightTexture0,dot(lightCoord,lightCoord).rr).UNITY_ATTEN_CHANNEL;
#endif
//计算视线方向
float3 viewdir = normalize( _WorldSpaceCameraPos.xyz - i.worldpos); 
   float3 diffuse = _LightColor0.rgb * (max(0,dot(normal,lightdir))) * albedo *atten;
float3 halfdir = reflect(-lightdir,normal);
float spec = saturate(dot(halfdir,viewdir));
//计算高光贴图系数
float specularmask = tex2D(_SpecularMap,i.uv.xy).r;
float3 specular = _LightColor0.rgb * pow(spec,_Shiniess) * specularmask *atten;
float4 finlcolor = float4( diffuse +specular,1);
return finlcolor;
}
ENDCG
}
}
}

这部分代码支持多光源,光照衰减处理,以及法线和光照模型!有不对的欢迎指出,大家相互学习!本人的shader学习新手群《601408323》,欢迎爱好学习的萌新!又不懂的可以来找我。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值