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 "inc"
#include "inc"
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);
= TRANSFORM_rd, _MainTex);
o.uv.zw = * _ + _BumpMap_ST.zw;
o.tangent = mul(_Object2World,v.tangent).xyz;//--计算世界控件的切线
o.binormal = al,o.tangent) * v.tangent.w;//--⽤叉乘计算世界空间的副切线o.worldpos = mul(_Object2World,v.vertex).xyz;//--计算世界空间中的顶点坐标位置
return o;
}
//⽚段⼊⼝函数
fixed4 frag (v2f i) : SV_Target
{
float3 worldnormal = al);
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;
< *= _BumpScale;
normal.z = sqrt(1 - saturate(,)));
normal = normalize(mul(rotation,normal));//--讲提取出来的法线转换到切线空间
float3 ambient = UNITY_;
float3 albedo = tex2D(_MainTex,).xyz;
float3 lightdir;//光的⽅向
float atten;//衰减系数
#ifdef USING_DIRECTIONAL_LIGHT
lightdir = normalize(_WorldSpaceLightPos0).xyz;
atten = 1;
#el
//把衰减贴图的顶点坐标转换到光照空间
lightdir = normalize(_ - worldpos).xyz;
float3 lightCoord = mul(_LightMatrix0,float4(worldpos,1));
atten = tex2D(_LightTexture0,dot(lightCoord,lightCoord).rr).UNITY_ATTEN_CHANNEL; #endif
//计算视线⽅向
float3 viewdir = normalize( _ - i.worldpos);
奶骑治疗手法
float3 diffu = _b * (max(0,dot(normal,lightdir))) * albedo ;
float3 halfdir = reflect(-lightdir,normal);
float spec = saturate(dot(halfdir,viewdir));
//计算⾼光贴图系数
float specularmask = tex2D(_SpecularMap,).r;
float3 specular = _b * pow(spec,_Shiniess) * specularmask;
float4 finlcolor = float4( diffu +specular+ ambient,1);
return finlcolor;
}
ENDCG
}
}
这⾥我们在“o.normal = mul(_World2Object,al,0)).xyz;//--计算世界空间的法线”和“normal =
normalize(mul(rotation,normal));//--讲提取出来的法线转换到切线空间”是⽤来处理法线的两个地⽅,所采⽤的⽅向是”矩阵在左,向量在右边“,我们会发现效果如下:
⾼光会乱跑,⽽且两⾯都有⾼光,我在这⼉纠结了很久,后来查了⼀些资料,发现是矩阵和向量的乘法⽅向所导致的,⼀开始还以为计算视线的⽅法不对最后改成这样之后“o.normal =
中国人民公安大学录取分数线
mul(al,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" = "ForwardBa"}
CGPROGRAM
#pragma vertex vert
#pragma multi_compile_fwdba
#include "inc"
#include "inc"
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);
= TRANSFORM_rd, _MainTex);
o.uv.zw = * _ + _BumpMap_ST.zw;
o.tangent = mul(_Object2World,v.tangent).xyz;//--计算世界控件的切线
o.binormal = al,o.tangent) * v.tangent.w;//--⽤叉乘计算世界空间的副切线o.worldpos = mul(_Object2World,v.vertex).xyz;//--计算世界空间中的顶点坐标位置return o;
}
//⽚段⼊⼝函数
fixed4 frag (v2f i) : SV_Target
{
float3 worldnormal = al);
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;
< *= _BumpScale;
normal.z = sqrt(1 - saturate(,)));
normal = normalize(mul(normal,rotation));//--讲提取出来的法线转换到切线空间
float3 ambient = UNITY_;
float3 albedo = tex2D(_MainTex,).xyz;
float3 lightdir;//光的⽅向
#ifdef USING_DIRECTIONAL_LIGHT
lightdir = normalize(_WorldSpaceLightPos0).xyz;
atten = 1;
#el
//把衰减贴图的顶点坐标转换到光照空间
lightdir = normalize(_ - worldpos).xyz;
float3 lightCoord = mul(_LightMatrix0,float4(worldpos,1));
atten = tex2D(_LightTexture0,dot(lightCoord,lightCoord).rr).UNITY_ATTEN_CHANNEL; #endif
//计算视线⽅向
float3 viewdir = normalize( _ - i.worldpos);
float3 diffu = _b * (max(0,dot(normal,lightdir))) * albedo *atten;
float3 halfdir = reflect(-lightdir,normal);
float spec = saturate(dot(halfdir,viewdir));
//计算⾼光贴图系数
float specularmask = tex2D(_SpecularMap,).r;
float3 specular = _b * pow(spec,_Shiniess) * specularmask * atten;
float4 finlcolor = float4( diffu +specular+ ambient,1);
return finlcolor;
}
ENDCG
}
Pass
全面小康社会
{
Tags{"LightMode" = "ForwardAdd"}
Blend One One
CGPROGRAM
#pragma vertex vert
ps缩放
#pragma fragment frag
#pragma multi_compile_fwdadd
#include "inc"
#include "inc"
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;
};