Unity中Shader旋转矩阵(二维旋转矩阵)


前言

在Shader中,我们经常对顶点进行旋转变换。我们在这篇文章中了解一下旋转使用的旋转矩阵。


一、旋转矩阵的原理

我们先在2D平面下,了解2D原理

1、我们以原点为中心,旋转坐标轴θ度

我们需要求的就是坐标系旋转后,P点在旋转后坐标系中的位置。

因为,我们旋转物体时,我们是围绕着一个轴旋转。
旋转后,我们需要求的就是物体顶点围绕该轴旋转后的坐标位置。
这里我们的 P 点就像 模型的顶点一样,得到P点的位置,就相当于得到了模型顶点在旋转后的位置。

  • 旋转前 P1(2,3)
  • 旋转后 P2 (?,?)
    在这里插入图片描述

2、求 P2x

  • 求 P2x 可以化为求 OB + BC
    在这里插入图片描述
  • 求OB:

c o s ( θ ) = O B O A cos(θ) = \frac{OB}{OA} cos(θ)=OAOB

O B = c o s ( θ ) ∗ O A OB = cos(θ)*OA OB=cos(θ)OA

O B = c o s ( θ ) ∗ P 1 x OB = cos(θ)*P_{1x} OB=cos(θ)P1x

  • 求BC:
    s i n ( θ ) = A D A P sin(θ) = \frac{AD}{AP} sin(θ)=APAD

A D = s i n ( θ ) ∗ A P AD = sin(θ)*AP AD=sin(θ)AP

A D = s i n ( θ ) ∗ P 1 y AD = sin(θ)*P_{1y} AD=sin(θ)P1y

  • P2x = OB + BC
    P 2 x = O B + B C = c o s ( θ ) P 1 x + s i n ( θ ) P 1 y P_{2x} = OB + BC = cos(θ)P_{1x} + sin(θ)P_{1y} P2x=OB+BC=cos(θ)P1x+sin(θ)P1y

3、求P2y:

  • 求P2y可以化为求 OC - AC
    在这里插入图片描述
  • 求OC:

c o s ( θ ) = O C O B cos(θ)= \frac{OC}{OB} cos(θ)=OBOC

O C = c o s ( θ ) ∗ O B OC=cos(θ)*OB OC=cos(θ)OB

O C = c o s ( θ ) ∗ P 1 y OC=cos(θ)*P_{1y} OC=cos(θ)P1y

  • 求AC:

s i n ( θ ) = B D B P sin(θ) = \frac{BD}{BP} sin(θ)=BPBD

B D = s i n ( θ ) ∗ B P BD = sin(θ)*BP BD=sin(θ)BP

A C = s i n ( θ ) ∗ B P AC = sin(θ)*BP AC=sin(θ)BP

A C = s i n ( θ ) ∗ P 1 x AC = sin(θ)*P_{1x} AC=sin(θ)P1x

  • P2y = OC - AC

P 2 y = O C − A C = c o s ( θ ) P 1 y − s i n ( θ ) P 1 x P_{2y} = OC - AC = cos(θ)P_{1y} - sin(θ)P_{1x} P2y=OCAC=cos(θ)P1ysin(θ)P1x

4、最后得到 P2点 的点阵

这个点阵就是我们旋转矩阵的核心

  • P 2 x = O B + B C = c o s ( θ ) P 1 x + s i n ( θ ) P 1 y P_{2x} = OB + BC = cos(θ)P_{1x} + sin(θ)P_{1y} P2x=OB+BC=cos(θ)P1x+sin(θ)P1y
  • P 2 y = O C − A C = c o s ( θ ) P 1 y − s i n ( θ ) P 1 x P_{2y} = OC - AC = cos(θ)P_{1y} - sin(θ)P_{1x} P2y=OCAC=cos(θ)P1ysin(θ)P1x

[ c o s ( θ ) ∗ P 1 x s i n ( θ ) ∗ P 1 y − s i n ( θ ) ∗ P 1 x c o s ( θ ) ∗ P 1 y ] \begin{bmatrix} cos(θ)*P_{1x}&sin(θ)*P_{1y}\\ -sin(θ)*P_{1x}&cos(θ)*P_{1y}\\ \end{bmatrix} [cos(θ)P1xsin(θ)P1xsin(θ)P1ycos(θ)P1y]

5、该点阵可以拆分为以下两个矩阵相乘的结果

因为,我们该点阵目前是在二维平面旋转。所以,可以认为我们是围绕 z 轴在旋转。

  • Mrotate就是我们的旋转矩阵
    M r o t a t e = [ c o s ( θ ) s i n ( θ ) − s i n ( θ ) c o s ( θ ) ] M_{rotate}= \begin{bmatrix} cos(θ)&sin(θ)\\ -sin(θ)&cos(θ)\\ \end{bmatrix} Mrotate=[cos(θ)sin(θ)sin(θ)cos(θ)]
  • P1 就是我们旋转前的点
    P 1 = [ P 1 x P 1 y ] P_1= \begin{bmatrix} P_{1x}\\ P_{1y}\\ \end{bmatrix} P1=[P1xP1y]

二、在Shader中,使用该旋转矩阵实现围绕 z 轴旋转

1、在属性面板定义 float 变量作为旋转的角度θ

_Angle(“Angle”,Float) = 0

2、在常量缓冲区申明该变量

CBUFFER_START(UnityPerMaterial)
float _Angle;
CBUFFER_END

3、在 顶点着色器 定义旋转矩阵

float2x2 M_rotate = float2x2
(
cos(_Angle),sin(_Angle),
-sin(_Angle),cos(_Angle)
);

4、用旋转矩阵乘以顶点的 xy 实现围绕z轴旋转

v.vertexOS.xy = mul(M_rotate,v.vertexOS.xy);

5、效果

围绕x轴旋转 和 围绕y轴旋转,我们可以修改 .xy 来实现。
请添加图片描述


三、测试代码

//平移变换
//缩放变换
//旋转变换
Shader "MyShader/URP/P3_5_5"
{
    Properties
    {
        _Translate("Translate(XYZ)",Vector) = (0,0,0,0)
        _Scale("Scale(XYZ)",Vector)= (1,1,1,1)
        _Angle("Angle",Float) = 0
    }
    SubShader
    {
        Tags
        {
            "PenderPipeline"="UniversalPipeline"
            "RenderType"="Opaque"
            "Queue"="Geometry"
        }
        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            struct Attribute
            {
                float4 vertexOS : POSITION;
            };

            struct Varying
            {
                float4 vertexCS : SV_POSITION;
            };

            CBUFFER_START(UnityPerMaterial)
            float4 _Translate;
            float4 _Scale;
            float _Angle;
            CBUFFER_END
            Varying vert (Attribute v)
            {
                Varying o;
                //平移变换
                float4x4 M_Translate = float4x4
                    (
                    1,0,0,_Translate.x,
                    0,1,0,_Translate.y,
                    0,0,1,_Translate.z,
                    0,0,0,1
                    );
                v.vertexOS = mul(M_Translate,v.vertexOS);
                //缩放交换
                float4x4 M_Scale = float4x4
                    (
                    _Scale.x,0,0,0,
                    0,_Scale.y,0,0,
                    0,0,_Scale.z,0,
                    0,0,0,1
                    );
                v.vertexOS = mul(M_Scale,v.vertexOS);
                //旋转变换
                float2x2 M_rotate = float2x2
                    (
                    cos(_Angle),sin(_Angle),
                    -sin(_Angle),cos(_Angle)
                    );
                v.vertexOS.xy = mul(M_rotate,v.vertexOS.xy);
                o.vertexCS = TransformObjectToHClip(v.vertexOS.xyz);
                return o;
            }

            half4 frag (Varying i) : SV_Target
            {
                return 1;
            }
            ENDHLSL
        }
    }
}

  • 34
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

楠溪泽岸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值