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

发表于2016-07-22
评论0 5.5k浏览
  开这篇主要是介绍下矩阵,众所周知,三维变换缺少不了矩阵,但是各种各样的矩阵就很让人头痛
  比如拿unity举例,unity脚本中有transform.localToWorldMatrix
  也有GetComponent().worldToLocalMatrix
  而在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().sharedMaterial.SetMatrix("_World2Local", rec.GetComponent().worldToLocalMatrix); 
        GetComponent().sharedMaterial.SetMatrix("_Local2World", rec.GetComponent().localToWorldMatrix); 
    
  这里说下transform.localToWorldMatrix其实是等同于GetComponent().worldToLocalMatrix的效果的
  同时这个物体也要放下面的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; 
    }; 
    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 
    
   
    
}
  如果没错的话应该是这个效果


  下面我们把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 
    
   
    
  下面自己把物体放在原点,然后选中下,在Global设置下移动
  改动注释位置语句
  //屏幕最前面出现红色物体(形状和物体本身有关) 在屏幕上 旋转后的物体如果移动 那么影子会沿着世界的方向移动
  pos = mul(_Object2World, v.vertex);
  //屏幕最前面出现红色物体(形状和物体本身有关) 旋转后的物体如果移动 那么影子会沿着物体本身的方向移动
  pos = mul(_World2Object, v.vertex);
  //物体被盖上红色
  pos = mul(UNITY_MATRIX_MVP, v.vertex);
  分别移动可以看到下面效果
  _Object2World 没被旋转过


  _Object2World_rotate 被旋转过


  _World2Object


  _World2Object_rotate 被旋转过


  然后大概就能知道_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

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引