unity shader中的矩阵变换知识

2023-11-01

提要

       在图形的计算中,比如旋转、缩放、平移、投影等操作,矩阵都扮演着极其重要的角色,它是操作图元的基本工具。虽然很多的图形API已经封装好了这些矩阵操作,但是理解这些矩阵操作的原理会非常非常有帮助,比如说我们可以通过一些矩阵的快捷计算来加速你的代码。

      如果你有一些线性代数的基础,看下面的内容的时候也不会很轻松,因为有点难且比较没意思,如果没有修过这门课,最好把线性代数这本书拿来看看,因为这些东西真是基础中的基础,而且非常的重要。

齐次记法(Homogeneous Notation)

       空间一个点对应的是一个空间的位置,一个向量对应一个方向,两者都可以用一个三维向量 V = (Vx, Vy, Vz)来表示.

       这两者如果对于变换(比如旋转,缩放),用一个 3*3 矩阵就可以搞定,但对于平移变换就不适用了,因为位置变换对于向量是没有意义的,而对于点才是有意义的。

       齐次记法就是用来解决这个问题的。

       所谓齐次记法就是用n+1维矢量表示n维矢量。

       在齐次记法下,空间点记为 = (Px, Py, Pz, Pw), 其中Pw = 1,4行x1列。

       空间向量记为 = (Vx, Vy, Vz, Vw),其中Vw = 0。 4行x1列。

       当出现Pw!=0 且Pw != 1时,就需要将坐标齐次化了,做法是同除以Pw,记为(Px/Pw, Py/Pw, Pz/Pw, 1).

       齐次记法下的变换矩阵如下所示:

给定一个移动变换矩阵

对于一个向量  = (Vx, Vy, Vz, Vw)和 相乘之后各值不变(TV)。

对于一个点   = (Px, Py, Pz, Pw)和 相乘之后结果变为 (Px+tx, Py+ty, Pz+tz, 1). (TP

       齐次坐标带来的便利:提供了用矩阵运算把二维、三维甚至高维空间中的一个点集从一个坐标系变化到另一个坐标系的有效方法。

基础变换

基础的变换包括平移,旋转,缩放,切变,反射,投影等,下面一个个来看。

平移变换

上面已经提到了,平移矩阵用T来表示:

tx,ty和tz分别表示向x,y,z方向移动的距离,如图

注意这个仿射矩阵(Offine Tranform Matrics)对于空间向量是没有作用的。

其逆矩    ,表示向相反的方向移动。

 旋转矩阵 Rotating

旋转变幻是指绕着一个轴旋转一定的角度,绕x,y,z旋转的旋转矩阵可以记为:

逆阵   , 表示绕同一个轴按相反的方向旋转相同的角度。

旋转矩阵的行列式都为1,因为它是正交矩阵。

关于图形(或物体)绕自身的某点旋转,其真实的过程是先将物体移动到旋转点与坐标原点相重合的位置,再将图形绕原点旋转,然后再进行平移变换,平移到原先的位置。

整个矩阵计算过程为   

缩放变换 Scaling

缩放就是放大和缩小,其矩阵表示为

如果 Sx = Sy = Sz,则称为等比变换(uniform),否则就不是(nonuniform)。

        其逆阵   ,表示按相反的方式进行缩放。

        Sx,Sy,Sz中有一个为负数,则改矩阵就是反射矩阵,如果刚好有两个因子为 -1, 则图形旋转  。反射矩阵通常需要特殊对待,比如,对于一个三角形,经过反射变换,顶点的顺序就可能会改变,这就会影响到面的法线,光照和背面消隐等算法就会受影响。可以通过计算左上角 3*3 矩阵的行列式的值来进行判断,若行列式的值为负,则是反射矩阵。

切变变换 Shearing

切变变换可以用于游戏中,制作出爆炸的时候画面抖动的效果,一共有六种:

第一个下标表示要改变的坐标轴,第二个下标表示沿着那个坐标轴变换。相关的矩阵也可以由此得出:第一个下标决定行,第二个决定列,则有:

效果如下:

其逆阵:

级联变换 Concatenation of Transforms

        由于矩阵乘法是没有交换率的,所以矩阵相乘的顺序非常重要,比如 S(2, 0.5, 1)和 , 根据它们执行的顺序不同,得到的结果也会不一样。

       将多个矩阵整合到一起的另一个好处是提高了效率,一般的顺序时 TRS。

欧拉变换 Euler TransForm

       欧拉变换可以将物体旋转到任意的方向,一个欧拉变换可以分为三个分量 h(ead), p(ich), r(oll),记为E(h,p,r)。

       其实就是三个旋转矩阵的级联矩阵:,由于都为对称阵,其逆阵   =  

      使用欧拉变换的时候会出现一个很蛋疼的问题-gimbal lock,可以看看这个视频- youtube video explaining gimbal lock

      还会出现的一个问题就是两个欧拉角之间的插值问题。

      为了避免万圣节锁,一个方法是设定好旋转轴的旋转顺序。

      另一中方法是使用四元组。

      

模型矩阵,视口矩阵和投影矩阵  The Model, View and Projection Matrices

        模型是由一系列的顶点构成的,顶点的坐标是相对于模型的中心来定义的,如果某个顶点的坐标值是(0,0,0),就意味着这个顶点在模型的正中间。

现在假设世界坐标在,模型的左边,则模型左边对应于世界坐标需要乘以一个平移矩阵,这个矩阵就是model matrix(模型矩阵)。

人们在操作这个模型的时候,需要对其进行一些变换,就需要将其每个定点移动到原点。

通过下图中黑色的箭头,就是将模型移动到原点。

这个变换矩阵就是model matrix(模型矩阵)

这个过程可以描述为:

         接下来是View Matrix(视口矩阵)。

        当你站在一座山的前面,想从各个角度来观察这座山的时候,你可以选择跑到不同的位置去看,也可以选择...移动整座山。这在现实生活中看似不行,但在图形学中,这一切都是可行的。

        现在在整个世界中只有一个model,当需要观察这个物体的时候,需要一个摄像机来进行观察,假设摄像机初始化在原点,经过一个平移矩阵移动,

glm::mat4 ViewMatrix = glm::translate(Tx, Ty ,Tz); 


这个矩阵就是View Matrix(视口矩阵),对应的就是世界坐标远点到摄像机的变换矩阵,过程可以由下图描述。

这里提一下glm中的一个神奇的lookat函数~超强的生成 View Matrics

glm::mat4 CameraMatrix = glm::LookAt(  
    cameraPosition, // the position of your camera, in world space  
    cameraTarget,   // where you want to look at, in world space  
    upVector        // probably glm::vec3(0,1,0), but (0,-1,0) would make you looking upside-down, which can be great too  
);  


整个阶段的描述如下:

接下来是Projection matrices(投影矩阵)

        经过前面的Model Matriix 和 View Matrix的变换,现在处在的就是摄像机空间,也就意味着(0,0)上的点就会出现在屏幕的最中央,但是并不是两个坐标就可以决定顶点是否显示,我们不能忽略 Z 坐标,也就是顶点距离摄像机的位置。

       在透视投影中,根据顶点的坐标值,当Vx,Vy的值相同的时候,Vz的值越大,顶点就越在中间,可以参考下图。

一个4*4的矩阵可以用来描述投影:

glm::mat4 projectionMatrix = glm::perspective(  
    FoV,         // The horizontal Field of View, in degrees : the amount of "zoom". Think "camera lens". Usually between 90° (extra wide) and 30° (quite zoomed in)  
    4.0f / 3.0f, // Aspect Ratio. Depends on the size of your window. Notice that 4/3 == 800/600 == 1280/960, sounds familiar ?  
    0.1f,        // Near clipping plane. Keep as big as possible, or you'll get precision issues.  
    100.0f       // Far clipping plane. Keep as little as possible.  
); 


通过投影矩阵变换,模型从照相机坐标变为了齐次坐标,过程描述如下:

GLSL实战MVP

        我们知道,OpenGL中自带了一些接口函数,可以很方便的定义视口,投影矩阵等,但如果使用GLSL的话,所有顶点的位置都是由 *.vert  中的代码来确定,下面我们就来实践一下刚才学习的Model,View,Projection。

      首先是两个简单的Shader

basic.vert

#version 400  
layout(location = 0) in vec3 vertexPosition_modelspace;  
  
// Values that stay constant for the whole mesh.  
uniform mat4 MVP;  
  
void main(){  
  
    // Output position of the vertex, in clip space : MVP * position  
    gl_Position =   MVP * vec4(vertexPosition_modelspace,1);  
} 


就是将模型坐标与MVP矩阵相乘。

basic.frag

#version 400  
  
// Ouput data  
out vec3 color;  
  
void main()  
{  
    // Output color = red   
    color = vec3(1,0,0);  
}  


接着时初始化shader,vao

void CGL::compileShader()  
{  
    static const GLfloat g_vertex_buffer_data[] = {  
        -1.0f, -1.0f, 0.0f,  
         1.0f, -1.0f, 0.0f,  
         0.0f,  1.0f, 0.0f,  
    };  
    static const GLushort g_element_buffer_data[] = { 0, 1, 2 };  
  
    GLuint vertexbuffer;  
    glGenBuffers(1, &vertexbuffer);  
    glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);  
    glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);  
  
    glEnableVertexAttribArray(0);  
    glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);  
    glVertexAttribPointer(  
            0,                  // attribute. No particular reason for 0, but must match the layout in the shader.  
            3,                  // size  
            GL_FLOAT,           // type  
            GL_FALSE,           // normalized?  
            0,                  // stride  
            (void*)0            // array buffer offset  
    );  
    if( ! prog.compileShaderFromFile("shader/basic.vert",GLSLShader::VERTEX) )  
    {  
        printf("Vertex shader failed to compile!\n%s",  
               prog.log().c_str());  
        exit(1);  
    }  
    if( ! prog.compileShaderFromFile("shader/basic.frag",GLSLShader::FRAGMENT))  
    {  
        printf("Fragment shader failed to compile!\n%s",  
               prog.log().c_str());  
        exit(1);  
    }  
  
    if( ! prog.link() )  
    {  
        printf("Shader program failed to link!\n%s",  
               prog.log().c_str());  
        exit(1);  
    }  
    if( ! prog.validate() )  
    {  
        printf("Program failed to validate!\n%s",  
               prog.log().c_str());  
        exit(1);  
    }  
    prog.use();  
}  


然后是初始化Uniform变量:

void CGL::setUniform()  
{  
  
    // Projection matrix : 45° Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units  
    glm::mat4 Projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);  
    // Camera matrix  
    glm::mat4 View       = glm::lookAt(  
                                glm::vec3(3,3,3), // Camera is at (4,3,3), in World Space  
                                glm::vec3(0,0,0), // and looks at the origin  
                                glm::vec3(0,1,0)  // Head is up (set to 0,-1,0 to look upside-down)  
                           );  
    // Model matrix : an identity matrix (model will be at the origin)  
    glm::mat4 Model      = glm::mat4(1.0f);  
    // Our ModelViewProjection : multiplication of our 3 matrices  
    glm::mat4 MVP        = Projection * View * Model; // Remember, matrix multiplication is the other way around  
    prog.setUniform("MVP",MVP);  
    prog.setUniform("modelMatrics",Model);  
} 

渲染一下:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

unity shader中的矩阵变换知识 的相关文章

  • 使用 QGLShaderProgram 将自定义类型(结构)uniform 从 Qt 传递到 GLSL

    我定义了一个包含两个向量的光参数结构 该结构体在 C 和 GLSL 中都以类似的方式定义 注意 QVector3D封装 3floats not doubles C 主机程序 struct LightParameters QVector3D
  • OpenGL 着色器。传递浮点数数组

    在我的场景中 我有许多物体想要同时旋转但角度不同 我有一个着色器 可以计算每个对象的位置并绘制整个场景 将顶点数组传递到带有顶点数组的着色器中 uniform float uRotation mat4 mz mat4 1 0 mz 0 0
  • 透明着色器允许下面的对象显示在上面

    在我的场景中 笑脸 带有 png 图像的四边形 放置在 Y 0 处 点 带有平铺 3X3 的四边形 放置在 Y 0 25 处 我需要用于笑脸的着色器是透明漫反射 因为我使用的是圆形 png 图像 但我在下面使用的点显示在笑脸上方 使用任何其
  • 修复 Nvidia 和 AMD 的 GLSL 着色器

    我在让 GLSL 着色器在 AMD 和 Nvidia 硬件上工作时遇到问题 我并不是在寻求修复特定着色器的帮助 而是寻求如何避免出现这些问题 是否可以检查着色器是否可以在 AMD Nvidia 驱动程序上编译 而无需在具有相应硬件的计算机上
  • 如何使用 ShaderModifier 更改 SCNGeometry 中特定三角形的颜色

    首先 在继续之前 我已经阅读了 SceneKit 使用纹理坐标在纹理上绘制 https stackoverflow com questions 26129111 ios8 scenekit painting on texture with
  • GLSL:如何执行类似 switch 的语句

    我想根据传递到着色器的数据动态调用缓动 所以用伪代码来说 var easing easings easingId var value easing point 我想知道在 GLSL 中完成此任务的最佳方法 我可以以某种方式使用 switch
  • 使用 ShaderMaterial 复制 MeshLambertMaterial 会忽略纹理

    我注意到 THREE js 在内部使用着色器来创建核心材质 例如 MeshLambertMaterial 因此我决定将 Three js 代码中的兰伯特着色器复制到新的着色器中并在其上进行构建 这是我得到的代码 忠实地从 Three js
  • 矩阵基本操作2

    题目描述 问题描述 将方阵 n 行n列 n lt 100 置成下三角矩阵 主对角线右上角数字全部清零 输入格式 第一行输入n 接下来的n行每行n列 表示矩阵的数值 用空格隔开 输出格式 n行n列下三角矩阵 每个数字3个占位符 左对齐 输入样
  • 尝试制定一种算法,将图像附加到该主题

    我实际上正在尝试制作一种看起来像三次贝塞尔曲线的算法 但我想知道是否有更简单的解决方案 这就是我想要的 该图的长度为 1 高度为 1 红线是我的实际输入 黄点是给定的参数 我们可以称之为 扭曲 介于 0 和 1 之间 绿线就是我想要的结果
  • Unity3D - 用于精灵裁剪的着色器

    我正在尝试创建一个可用于在游戏中剪辑 2D 精灵的着色器 我在中找到了该着色器另一个问题 https stackoverflow com questions 16397023 unity3d a shader that will clip
  • 处理中点/笔划的景深着色器

    最近我一直在使用下面的景深着色器 最初来自ofx后处理 https github com neilmendoza ofxPostProcessing blob master src DofPass cppOpenFrameworks 库 用
  • 线性光模式的 Alpha 混合层

    我正在重新创建一些 Photoshop 混合 并尝试使用线性光模式 在 Photoshop 中 您将有一个不透明度为 100 的背景图层 然后是一个不透明度为 50 的顶层 其混合模式设置为 线性光 我确实找到了有关如何进行线性光混合的信息
  • Three.js 使用 WebRTC 并应用 Shader

    我不知道如何将着色器应用于具有视频纹理的 Three js 对象 我一直在使用 webRTC 和 Three js 并使用标准材质成功将视频纹理映射到网格上 var material new THREE MeshBasicMaterial
  • OpenGL 定向光着色器

    我想使用 OpenGL 和 GLSL 将定向光添加到我的场景中 问题在于 理论上正确的方法会产生错误的结果 在顶点着色器中我执行以下操作 光线的方向以世界坐标给出 并使用 viewMatrix 转换为相机坐标 使用法线矩阵将顶点的法线转换为
  • 在Unity中如何使两个精灵的重叠区域透明?

    在Unity中如何使两个精灵的重叠区域透明 你能写一个关于它的着色器吗 经过一些研究 我了解到我应该使用模板缓冲区 但我不知道如何使用 这对我来说至关重要 我必须在 6 天内完成这个学校项目 请帮忙 示例图片 就这样 请记住这是我第一次使用
  • 使用着色器创建模糊过滤器 - 从片段着色器访问相邻像素?

    我想使用 OpenGL ES 2 0 中的片段着色器创建模糊效果 我感兴趣的算法只是一个平均模糊 将所有相邻像素添加到我自己中并除以 9 进行标准化 但是我有两个问题 1 这是否需要我首先渲染到帧缓冲区 然后切换渲染目标 或者有更简单的方法
  • glUseProgram(0) 的作用是什么?

    OpenGL 文档为glUseProgram https www khronos org registry OpenGL Refpages gl4 html glUseProgram xhtml声称用参数调用它zero将导致着色器执行的结果
  • DirectX 世界视图矩阵乘法 - GPU 或 CPU 的地方

    我是 directx 的新手 但令我惊讶的是 我看到的大多数示例中 世界矩阵和视图矩阵都是作为顶点着色器的一部分相乘 而不是与 CPU 相乘并将结果传递给着色器 对于刚性对象 这意味着您为对象的每个顶点将相同的两个矩阵相乘一次 我知道 GP
  • 使用 OpenGL 着色器进行数学计算 (C++)

    我有一个矩阵 例如 100x100 尺寸 我需要对每个元素进行计算 matrix i j tt 8 5例如 我有一个巨大的矩阵 我想使用 OpenGL 着色器来实现该算法 我想使用着色器 例如 uniform float val unifo
  • 更改 Qt OpenGL 窗口示例以使用 OpenGL 3.3

    我正在尝试更改 Qt OpenGL 示例以使用更现代的 opengl 版本 330 似乎合适 所以我做了 在 main cpp 上设置版本和配置文件 设置着色器版本 更改着色器以使用统一 它现在构建没有任何错误 但我只看到一个空白窗口 我错

随机推荐

  • Packet for query is too large

    mysql 执行insert语句时 提示Packet for query is too large异常 问题点 mysql支持的传输数据包大小超限了 解决办法 比较一劳永逸的方式是直接修改mysql安装目录下的my ini配置文件 增加一行
  • 利用python制作一款截图识别软件

    先给大家推荐一款截图软件 非常方便 可以把截出的图片放置到窗口上 并且可以随意移动 这个是微软开发的一款工具 Snipaste 支持各类电脑系统 先简单介绍一下它的用法 F1截图 ctrl c把图片复制到剪贴板 方便下次使用 ctrl v
  • python协程—asyncio模块

    为什么使用协程 当多线程或者多进程足够多时 实际上并不能解决性能的瓶颈问题 也就是多线程和多进程对小规模的请求可以提高效率 过多的请求实际上会降低服务资源响应效率 因此协程是更好的解决文案 什么是协程 当一个程序遇到阻塞时 如果将这个程序挂
  • (翻译)Linux中的IS_ERR()宏

    本文翻译自 https newbedev com is err macro in linux text 20IS ERR 20 28 29 20macro 20in 20Linux 20Tests 20if can 20find 20MAC
  • VUE3.0生命周期函数

    什么是生命周期 1 vue中每个组件都是独立的 每个组件都有一个属于它的生命周期 2 从一个组件创建 数据初始化 挂载 更新 销毁 这就是一个组件所谓的生命周期 强调的是一个时间段 在vue3中 新增了一个setup生命周期函数 setup
  • 四川岳池2021年高考成绩查询,2021年岳池中学升学率高不高?

    引语 了解一个学校 就要全方位的进行了解 清楚学校的师资 录取分数线 升学率等等 四川初升高升学网网编为同学们梳理了2021年岳池中学升学率高不高的相关信息 更多最新资讯可以关注四川初升高升学网公众号 岳池中学2021升学率 立即点击查看
  • MySQL 表分区 报错:Table has no partition for value XXX

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 对已存在的未分区的表进行分区 alter table test PARTITION BY RANGE id PARTITION p1 VALUES LESS THAN 10
  • 扫码点餐软件,打造智慧餐厅新体验!【点餐小程序】

    随着科技的不断发展 扫码点餐软件成为了越来越多餐厅的选择 它们可以帮助顾客快速方便地点餐 同时也带来了更高效的服务体验和更好的管理效果 在这篇文章中 我们将探讨如何通过扫码点餐软件打造智慧餐厅新体验 并且以一个具体的案例来解析其中的技巧 步
  • 10-6 计算时间差

    时间差可以用来衡量程序运行效率 计算其有三种方法 一是使用 difftime 函数 二是使用自定义的 TimeInMillisecond 函数 三是使用 clock 函数 1 使用 difftime 函数计算秒级时间差 三种方法本质相同 都
  • iTerm2使用笔记

    鼠标 设置在less vim中等使用滚轮 在高级设置里面 设置系统编辑方式 让写命令和在其他编辑器中一致 比如alt delete 删除一个word vim vimrc中设置系统clipboard set clipboard unnamed
  • 机器人学习--卡尔曼滤波及各种滤波解析

    什么是滤波 举个最直观的简单例子 臭水沟里舀一大勺水 需要过滤成干净水怎么办 用滤网 网孔可根据需要选择大小孔 过滤 在电路方面波形的高低通滤波原理类似 图像上的噪声点各种中值滤波等类似的滤波方案 Filter 在状态测量领域是根据 预测
  • webpack 和 ts 简单配置及使用

    如何使用webpack 与 ts结合使用 新建项目 执行项目初始化 npm init y 会生成 name tsdemo01 version 1 0 0 description main index js scripts test echo
  • Notion 的插件介绍和使用

    十分推荐 Save to Notion Notion Web Clipper notion 官方的剪藏插件 插件安装 Chrome 商店 https chrome google com webstore detail notion web
  • 看完这篇 教你玩转渗透测试靶机vulnhub——DC8

    Vulnhub靶机DC8渗透测试详解 Vulnhub靶机介绍 Vulnhub靶机下载 Vulnhub靶机安装 Vulnhub靶机漏洞详解 信息收集 暴力破解 提权 获取flag Vulnhub靶机渗透总结 Vulnhub靶机介绍 vulnh
  • keil仿真调试报错

    问题 程序能正常下载 但是keil仿真调试报错 进入仿真程序就在全速运行了 此时什么都干不了 查看command窗口日志信息会发现有报错 jlink error cpu is not halted 然后点击reset按钮会退出全速仿真 所有
  • 一个自己用的opencv历程

    include
  • 设两个栈(stack1,stack2)共享一个一维数组空间s[m],怎么最大限度地利用数组空间

    可以利用扩充栈操作 在传统的双端栈中 两个栈之间存在一种制约关系 两个栈中的元素总数最大可以达到M 如果一个栈中的元素较多 那么另一个栈中的元素就较少 两个栈中的元素总和超不过M 它主要利用了栈的 栈底位置不变 而栈顶位置动态变化 的特性
  • jquery 读取 解析 四级的xml文件 案例

    答案 jQuery 可以很容易地使用 AJAX 技术来解析 XML 文件 下面是一个案例 ajax type GET url yourXMLFile xml dataType xml success function xml xml fin
  • python day55

    今日内容 如何写一个测试脚本 创建一个test py文件 from django test import TestCase Create your tests here import os if name main os environ s
  • unity shader中的矩阵变换知识

    提要 在图形的计算中 比如旋转 缩放 平移 投影等操作 矩阵都扮演着极其重要的角色 它是操作图元的基本工具 虽然很多的图形API已经封装好了这些矩阵操作 但是理解这些矩阵操作的原理会非常非常有帮助 比如说我们可以通过一些矩阵的快捷计算来加速