unity脚本矩阵和shader内置矩阵区别联系 思考和梳理

开这篇主要是介绍下矩阵,众所周知,三维变换缺少不了矩阵,但是各种各样的矩阵就很让人头痛

比如拿unity举例,unity脚本中有transform.localToWorldMatrix

也有GetComponent<renderer style="box-sizing: border-box;">().worldToLocalMatrix</renderer>

而在shader中也有_Local2World或者_World2Local或者UNITY_MATRIX_MVP等等矩阵

而这些矩阵关系有些人很难搞清楚,所以在此梳理下

内容可能会随着深入理解而更新,可以关注查看,有错误理解请大家指出,希望和大家交流

这里首先需要放个工程,也就是简单的平面投影

相关内容参考(

http://tgerm.org/U3D-Cg-04/

http://blog.sina.com.cn/s/blog_89d90b7c0102v8at.html

物体摆放尽量在原点周围,不然会看不到效果影响测试

需要一个脚本放在需要投影的物体上(这个脚本的rec放被投影的平面)

?

| |

using UnityEngine;

using System.Collections;

//放在需要显示阴影的对象上

public class PlaneShadowCaster : MonoBehaviour {

//接受阴影的物体 测试的时候可以放这个脚本的物体本身

//如果是测试平面投影,这个地方应该放作为被投影物体地面

public Transform rec;

void Update()

{

GetComponent<renderer style="box-sizing: border-box;">().sharedMaterial.SetMatrix(</renderer>``"_World2Local"``, rec.GetComponent<renderer style="box-sizing: border-box;">().worldToLocalMatrix); </renderer>

GetComponent<renderer style="box-sizing: border-box;">().sharedMaterial.SetMatrix(</renderer>``"_Local2World"``, rec.GetComponent<renderer style="box-sizing: border-box;">().localToWorldMatrix); </renderer>

}

}

|

这里说下transform.localToWorldMatrix其实是等同于GetComponent<renderer style="box-sizing: border-box;">().worldToLocalMatrix的效果的</renderer>

同时这个物体也要放下面的shader

?

| |

Shader "Custom/ShadowTest" {

//物体本身

Properties{

_MainTex(``"Base (RGB)"``, 2D) = "white" {}

_Instensity(``"Shininess"``, Range(2, 4)) = 2.1

}

SubShader{

Pass{

Tags{ "LightMode" = "ForwardBase" }

Material{

Ambient(1,0,1,1)

Specular(1,1,1,1)

}

Lighting On

}

//物体投影

Pass{

Tags{ "LightMode" = "ForwardBase" }

Blend DstColor SrcColor

Offset -1, -1``//深度偏移 Z缩放的最大斜率的值 表示可分辨的最小深度缓冲区的值

CGPROGRAM

#pragma vertex vert

#pragma fragment frag

#include "UnityCG.cginc"

float4x4 _World2Local;

float4x4 _Local2World;

float4x4 _TestMatrix;

float _Instensity;

sampler2D _MainTex;

float4 _MainTex_ST;

struct v2f {

float atten : TEXCOORD0;

float2 uv : TEXCOORD1;

};

struct appdata

{

float2 uv : TEXCOORD0;

float4 vertex:POSITION;

};

//[http://blog.csdn.net/shenmifangke ](http://blog.csdn.net/shenmifangke%C2%A0)

v2f vert(out float4 pos : SV_POSITION,appdata v)

{

float3 litDir;

litDir = WorldSpaceLightDir(v.vertex);``//世界空间主光照相对于当前物体的方向

litDir = mul(_World2Local, float4(litDir, 0)).xyz;``//光源方向转换到接受阴影的平面空间

//litDir = normalize(litDir);//归一

float4 vt;

vt = mul(_Object2World, v.vertex);``//将当前物体转换到世界空间

vt = mul(_World2Local, vt);``//将物体在世界空间的矩阵转换到地面空间

//vt.xz = vt.xz - (vt.y / litDir.y)*litDir.xz;//用三角形相似计算沿光源方向投射后的XZ

vt.x = vt.x - (vt.y / litDir.y)*litDir.x;

vt.z = vt.z - (vt.y / litDir.y)*litDir.z;

//vt.x = vt.x - (vt.y / litDir.y)*litDir.x;

//vt.xz=vt.xz * float2(1, 1);

vt.y = 0;

//vt=mul(vt,_World2Ground);//back to world

vt = mul(_Local2World, vt);``// 阴影顶点矩阵返回到世界空间

vt = mul(_World2Object, vt);``// 返回到物体的坐标

v2f o; //_World2Object _Object2World _Local2World _World2Local

//pos = mul(UNITY_MATRIX_MVP, v.vertex);//_Local2World

//pos = mul(_Local2World, v.vertex);//_Local2World

//pos = mul(_Object2World * UNITY_MATRIX_V * UNITY_MATRIX_P, v.vertex);

pos = mul(UNITY_MATRIX_MVP, v.vertex);``//_Local2World

//pos = mul(_Object2World*UNITY_MATRIX_MVP, v.vertex);//_Local2World

//pos = mul(UNITY_MATRIX_MVP*_Local2World, v.vertex);//_Local2World

//pos = mul(_World2Local,v.vertex);//_Local2World

pos = mul(UNITY_MATRIX_MVP, vt);

return o;

}

float4 frag(v2f i) :COLOR

{

return float4(1,0,0,1);``//可以看到透明的方块 透明是受blend影响

}

ENDCG

}

}

}

|

如果没错的话应该是这个效果

[图片上传失败...(image-9f34e9-1559180547726)]

下面我们把shader改一下来做测试,另外需要把物体上rec改成需要投影的物体本身才行(之前是被投影的平面)

?

| |

Shader "Custom/ShadowTest" {

//物体本身

Properties{

_MainTex(``"Base (RGB)"``, 2D) = "white" {}

_Instensity(``"Shininess"``, Range(2, 4)) = 2.1

}

SubShader{

Pass {

Tags{ "LightMode" = "ForwardBase" }

Material{

Ambient(1,0,1,1)

Specular(1,1,1,1)

}

Lighting On

}

//物体投影

Pass{

Tags{ "LightMode" = "ForwardBase" }

Blend DstColor SrcColor

Offset -1, -1``//深度偏移 Z缩放的最大斜率的值 表示可分辨的最小深度缓冲区的值

CGPROGRAM

#pragma vertex vert

#pragma fragment frag

#include "UnityCG.cginc"

float4x4 _World2Local;

float4x4 _Local2World;

float4x4 _TestMatrix;

float _Instensity;

sampler2D _MainTex;

float4 _MainTex_ST;

struct v2f {

float atten : TEXCOORD0;

float2 uv : TEXCOORD1;

};

struct appdata

{

float2 uv : TEXCOORD0;

float4 vertex:POSITION;

};

v2f vert(out float4 pos : SV_POSITION,appdata v)

{

v2f o;

////////////////////////////////////////////////////////////////////////////////////////////改这里

//_World2Object _Object2World _Local2World _World2Local

pos = mul(UNITY_MATRIX_MVP, v.vertex);

////////////////////////////////////////////////////////////////////////////////////////////

return o;

}

float4 frag(v2f i) :COLOR

{

return float4(1,0,0,1);

}

ENDCG

}

}

}``//[http://blog.csdn.net/shenmifangke](http://blog.csdn.net/shenmifangke)

|

下面自己把物体放在原点,然后选中下,在Global设置下移动

改动注释位置语句

//屏幕最前面出现红色物体(形状和物体本身有关) 在屏幕上 旋转后的物体如果移动 那么影子会沿着世界的方向移动

pos = mul(_Object2World, v.vertex);

//屏幕最前面出现红色物体(形状和物体本身有关) 旋转后的物体如果移动 那么影子会沿着物体本身的方向移动

pos = mul(_World2Object, v.vertex);

//物体被盖上红色

pos = mul(UNITY_MATRIX_MVP, v.vertex);

分别移动可以看到下面效果

_Object2World 没被旋转过

[图片上传失败...(image-c9b8d4-1559180547726)]

_Object2World_rotate 被旋转过

[图片上传失败...(image-b2f91d-1559180547726)]

[图片上传失败...(image-fb81fb-1559180547726)]

_World2Object

[图片上传失败...(image-155bcc-1559180547726)]

_World2Object_rotate 被旋转过

[图片上传失败...(image-4ebe43-1559180547726)]

然后大概就能知道_World2Object和_Object2World这两个内置矩阵的区别了吧

注意点

_Local2World(transform.localToWorldMatrix)等同于_Local2World

_World2Local(transform.worldToLocalMatrix)等同于_World2Object

但是通过脚本得到的矩阵必须在运行时才能看到正确效果,区别就是脚本矩阵可以取到其他物体的矩阵,而shader只能是自己的

注意矩阵相乘需要用mul

float4 mvp;

mvp = mul(_Object2World, v.vertex);

mvp = mul(UNITY_MATRIX_V, mvp);

mvp = mul(UNITY_MATRIX_P, mvp);

pos = mvp;

而不能用乘法只是用来做行列式相乘的,并不是矩阵的乘法

上面那段等同于pos = mul(UNITY_MATRIX_MVP, v.vertex);

可以参考(HLSL中的MUL指令深层剖析

http://blog.csdn.net/boyuejiang/article/details/8908339

再给个参考 简单翻译了下

I'm trying to learn the main concepts used behind Unity3D camera, in particular its matrix and having a few doubts that afaik aren't answered within Unity documention:

unity基于opengl摄像机吗,举例来说,opengl教程能应用在unity上吗,如果是的,可以在unity中表现ModelViewProjection如下面

1、Is Unity camera based on OpenGL camera? For example, can the OpenGL tutorial on cameras found here be applied in Unity? If yes, then when representing a ModelViewProjection matrix, in Unity we have:

MVPmatrix = mainCamera.projectionMatrix * mainCamera.worldToCameraMatrix;

mvp是四乘四矩阵,opengl是下面的公式

where MVP is a 4x4 matrix, while in OpenGL is represented through the following code:

MVPmatrix = projection * view * model;

为什么这两个有什么区别

Why the differences between the two methods?

当前者相乘时,并给出mvp矩阵的时候,我们是否是做了同类的转换

3、When multiplying the previously mentioned MVPmatrix with a given Vector3[4] type, are we doing a conversion to homogeneous coordinates?

如果问题1是的话,我从哪里可以知道摄像机的矩阵

2、If the answer to 1) was "no", where can I get additional information on Unity3D's camera matrix?

在opengl中并没有这样的相机,顶点变换是用顶点shader完成,可以使用任意数量的矩阵,甚至可以在一个程序中使用有不同矩阵数量的不同shader

There is no such a thing as camera in modern OpenGL. Vertex transformations are done using vertex shaders. Which can use any number of matrices. And you can even have different shaders in one application which require different sets of matrices.

特别说下,模型视图投影矩阵是在很多3D应用中常见的矩阵,你可以认为就是3D编程的一个内置变量

In the particular tutorial 3 matrices are used: Model, View and Projection. This is quite a common approach that is used in a lot of 3D applications. You can think of it as of common 3D graphics programming pattern.

Unity3D

幸运的是,unity也有下面的内置变量

Luckily, Unity3D follows this pattern:

Model matrix. In scripts: Transform.localToWorldMatrix. In vertex shaders: _Object2World.

View matrix. In scripts: Camera.worldToCameraMatrix. In vertex shaders: UNITY_MATRIX_V.

Projection matrix. In scripts: Camera.projectionMatrix. In vertex shaders: UNITY_MATRIX_P.

其实shader内置矩阵和脚本中的内置矩阵有上面的对应关系

但是为什么有了shader内置矩阵,还要有脚本矩阵呢,因为shader内部矩阵只是物体本身用的,有时候也需要一个物体用到了一个物体的矩阵(比如地面或者其他参考物体),所以其他物体的矩阵需要通过脚本上的矩阵传递进来

参考地址

http://stackoverflow.com/questions/24165915/advanced-info-on-unity3ds-camera-matrix

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容