找回密码
 立即注册
查看: 323|回复: 0

raylib 3d绘图基础教程(3):几何变换

[复制链接]
发表于 2022-2-15 16:10 | 显示全部楼层 |阅读模式
系列文章目录:

  • raylib绘图库简介
  • raylib 3d绘图基础教程(1)坐标系和摄像机
  • raylib 3d绘图基础教程(2):网格(Mesh)
1 概述

在绘制3维图形场景时,我们经常需要改变三维模型的大小(缩放)、姿态(旋转或者翻转)和位置(位移)等。这些操作都属于几何变换(Geometric Transformation),其实质就是将原三维模型上的每一个点{x,y,z}变换到新的位置{x',y',z'}上,也就是说,对原模型上每一个点的坐标{x,y,z}进行计算,得到它的新位置坐标{x',y',z'}。
因为在计算机图形处理中,3维坐标系常见的几何变换(坐标计算)都可以用四元组{x,y,y,1}乘以一个4x4的矩阵得到{x',y',z',c}来完成,所以各种3d绘图库一般都会用一个4x4矩阵来表征要对三维模型进行的变换,称为变换矩阵(transformation matrix)。raylib也一样,它用Matrix结构用来表征几何变换,其在raylib.h中的定义如下:
// Matrix, 4x4 components, column major, OpenGL style, right handed
typedef struct Matrix {
    float m0, m4, m8, m12;  // Matrix first row (4 components)
    float m1, m5, m9, m13;  // Matrix second row (4 components)
    float m2, m6, m10, m14; // Matrix third row (4 components)
    float m3, m7, m11, m15; // Matrix fourth row (4 components)
} Matrix;
在实际编程时我们很少手工填写变换矩阵中的各个元素,而是使用raymath.h中提供的Matrix系列变换矩阵生成函数来构造变换矩阵。
2 变换函数生成函数

raylib 4.0的raymath.h头文件提供了下列变换函数:
变换矩阵生成函数变换矩阵作用
MatrixRotate(axis,angle)绕指定的坐标轴axis旋转angle度
MatrixRotateX(angle)绕X轴旋转angle度
MatrixRotateY(angle)绕Y轴旋转angle度
MatrixRotateZ(angle)绕X轴旋转angle度
MatrixRotateXYZ(angles)依次绕X、Y、Z轴旋转angles.x,angles.y和angles.z度
MatrixRotateZYX(angles)依次绕Z、Y、X轴旋转angles.x,angles.y和angles.z度
MatrixScale(x,y,z)(缩放)分别在X、Y、Z方向上缩放x,y,z倍
MatrixTranslate(x,y,z)(平移)将坐标系原点移动到原坐标系的(x,y,z)点处
MatrixIdentity()模型保持不变(单位矩阵)
2.1 旋转

在raylib 3d绘图基础教程(2):网格(Mesh)的示例中,我们已经演示了网格模型绕y轴旋转的效果,这里不再赘述。



模型绕y轴旋转示例

2.2 缩放

在本例中,我们使用MatrixScale函数创建缩放变换矩阵,缩放因子从0.1到3来回变化:
//创建缩放变换矩阵
Matrix transform=MatrixScale(scale,scale,scale);


完整程序如下:
#include <raylib.h>
#include <raymath.h>

int main(void)
{
        // 初始化
        const int screenWidth = 640;
        const int screenHeight = 480;
       
        //启用反锯齿
        SetConfigFlags(FLAG_MSAA_4X_HINT);
       
        //初始化窗口
        InitWindow(screenWidth, screenHeight, "Sample");
       
        // 初始化摄像机
        Camera3D camera = { 0 };
        camera.position = (Vector3){ 40.0f, 20.0f, 0.0f }; //相机所在位置{x,y,z}
        camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; //相机朝向位置{x,y,z}
        camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; //相机正上方朝向矢量
        camera.fovy = 40; //相机视野宽度
        camera.projection = CAMERA_PERSPECTIVE; //采用透视投影
       
        //创建几何体
        Mesh mesh = GenMeshCube(5,5,15);
       
        //设置动画帧率(刷新率,fps)为30帧/秒
        SetTargetFPS(30);
        //--------------------------------------------------------------------------------------
        //缩放因子
        double scale = 0.1;
        double step = 0.1;
       
        int colorHue = 0;
        // 主游戏循环
        while (!WindowShouldClose())    //关闭窗口或者按ESC键时返回true
        {
               
                // 每次循环更新一帧
                double time = GetTime();
                scale+=step;
                if (scale>2.9) {
                        step=-0.1;
                } else if (scale<0.2) {
                        step=0.1;
                }
               
                colorHue++;
                colorHue%=360;
               
                //创建贴图
                Image checked = GenImageChecked(2,2,1,1,ColorFromHSV(colorHue,1,1),LIGHTGRAY);
                Texture2D texture = LoadTextureFromImage(checked);
                UnloadImage(checked);
               
                //基于贴图创建材质
                Material material=LoadMaterialDefault();
                material.maps[MATERIAL_MAP_DIFFUSE].texture = texture;
               
                //创建缩放变换矩阵
                Matrix transform=MatrixScale(scale,scale,scale);
               
                BeginDrawing();               
                        ClearBackground(WHITE);
                        //以摄像机视角绘制3d内容
                        BeginMode3D(camera);
                                //绘制网格
                                DrawMesh(mesh,material,transform);                       
                        EndMode3D();
                EndDrawing();
               
                //释放材质和贴图
                UnloadMaterial(material);
                UnloadTexture(texture);
        }
        //释放网格
        UnloadMesh(mesh);
        //关闭窗口
        CloseWindow();
       
        return 0;
}
2.3 平移

在本例中,我们使用MatrixTranslate函数创建平移变换矩阵,让模型沿y轴移动:
//创建平移变换矩阵
Matrix transform= MatrixTranslate(0,y,0);


完整程序如下:
#include <raylib.h>
#include <raymath.h>

int main(void)
{
        // 初始化
        const int screenWidth = 640;
        const int screenHeight = 480;
       
        //启用反锯齿
        SetConfigFlags(FLAG_MSAA_4X_HINT);
       
        //初始化窗口
        InitWindow(screenWidth, screenHeight, "Sample");
       
        // 初始化摄像机
        Camera3D camera = { 0 };
        camera.position = (Vector3){ 40.0f, 20.0f, 0.0f }; //相机所在位置{x,y,z}
        camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; //相机朝向位置{x,y,z}
        camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; //相机正上方朝向矢量
        camera.fovy = 40; //相机视野宽度
        camera.projection = CAMERA_PERSPECTIVE; //采用透视投影
       
        //创建几何体
        Mesh mesh = GenMeshCube(5,5,15);
       
        //设置动画帧率(刷新率,fps)为30帧/秒
        SetTargetFPS(30);
        //--------------------------------------------------------------------------------------
        //平移y坐标
        double y = -10;
        double step = 0.1;
       
        int colorHue = 0;
        // 主游戏循环
        while (!WindowShouldClose())    //关闭窗口或者按ESC键时返回true
        {
               
                // 每次循环更新一帧
                double time = GetTime();
                y+=step;
                if (y>9.9) {
                        step=-0.1;
                } else if (y<-9.9) {
                        step=0.1;
                }
               
                colorHue++;
                colorHue%=360;
               
                //创建贴图
                Image checked = GenImageChecked(2,2,1,1,ColorFromHSV(colorHue,1,1),LIGHTGRAY);
                Texture2D texture = LoadTextureFromImage(checked);
                UnloadImage(checked);
               
                //基于贴图创建材质
                Material material=LoadMaterialDefault();
                material.maps[MATERIAL_MAP_DIFFUSE].texture = texture;
               
                //创建平移变换矩阵
                Matrix transform= MatrixTranslate(0,y,0);
               
                BeginDrawing();               
                        ClearBackground(WHITE);
                        //以摄像机视角绘制3d内容
                        BeginMode3D(camera);
                        //绘制网格
                        DrawMesh(mesh,material,transform);                       
                        EndMode3D();
                EndDrawing();
               
                //释放材质和贴图
                UnloadMaterial(material);
                UnloadTexture(texture);
        }
        //释放网格
        UnloadMesh(mesh);
        //关闭窗口
        CloseWindow();
       
        return 0;
}
3 复合变换

我们可以通过矩阵乘法,将变换矩阵相乘得到新的复合变换矩阵,来实现复合变换。
3.1 边旋转边缩放

在本例中,我们将缩放矩阵和旋转矩阵相乘,得到同时缩放和旋转的效果:
//创建缩放变换矩阵
Matrix scaleTransform=MatrixScale(scale,scale,scale);
//创建旋转变换矩阵
Matrix rotateTransform=MatrixRotateY(angle);
//两个矩阵相乘,得到复合变换矩阵
Matrix complexTransform = MatrixMultiply(scaleTransform,rotateTransform);


完整程序如下:
#include <raylib.h>
#include <raymath.h>

int main(void)
{
        // 初始化
        const int screenWidth = 640;
        const int screenHeight = 480;

        //启用反锯齿
        SetConfigFlags(FLAG_MSAA_4X_HINT);

        //初始化窗口
        InitWindow(screenWidth, screenHeight, "Sample");

        // 初始化摄像机
        Camera3D camera = { 0 };
        camera.position = (Vector3){ 40.0f, 20.0f, 0.0f }; //相机所在位置{x,y,z}
        camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; //相机朝向位置{x,y,z}
        camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; //相机正上方朝向矢量
        camera.fovy = 40; //相机视野宽度
        camera.projection = CAMERA_PERSPECTIVE; //采用透视投影

        //创建几何体
        Mesh mesh = GenMeshCube(5,5,15);

        //设置动画帧率(刷新率,fps)为30帧/秒
        SetTargetFPS(30);
        //--------------------------------------------------------------------------------------
        //缩放因子
        double scale = 0.1;
        double step = 0.01;
       
        double angle;

        int colorHue = 0;
        // 主游戏循环
        while (!WindowShouldClose())    //关闭窗口或者按ESC键时返回true
        {

                // 每次循环更新一帧
                double time = GetTime();
                angle = time*0.3;
                scale+=step;
                if (scale>2.9) {
                        step=-0.01;
                } else if (scale<0.2) {
                        step=0.01;
                }

                colorHue++;
                colorHue%=360;

                //创建贴图
                Image checked = GenImageChecked(2,2,1,1,ColorFromHSV(colorHue,1,1),LIGHTGRAY);
                Texture2D texture = LoadTextureFromImage(checked);
                UnloadImage(checked);

                //基于贴图创建材质
                Material material=LoadMaterialDefault();
                material.maps[MATERIAL_MAP_DIFFUSE].texture = texture;

                //创建缩放变换矩阵
                Matrix scaleTransform=MatrixScale(scale,scale,scale);
                //创建旋转变换矩阵
                Matrix rotateTransform=MatrixRotateY(angle);
                //两个矩阵相乘,得到复合变换矩阵
                Matrix complexTransform = MatrixMultiply(scaleTransform,rotateTransform);

                BeginDrawing();
                        ClearBackground(WHITE);
                        //以摄像机视角绘制3d内容
                        BeginMode3D(camera);
                                //绘制网格
                                DrawMesh(mesh,material,complexTransform);
                        EndMode3D();
                EndDrawing();

                //释放材质和贴图
                UnloadMaterial(material);
                UnloadTexture(texture);
        }
        //释放网格
        UnloadMesh(mesh);
        //关闭窗口
        CloseWindow();

        return 0;
}
3.2 绕y轴转圈

在前面的例子中,模型本身的中心位于原点,因此绕y旋转的效果就是模型绕y轴旋转。如果我们希望模型以10为半径绕y轴转圈,该怎么办呢?
可以这样做:先把通过平移,把原来的y轴移动到新坐标系中距离原点10的地方;然后让模型(中心在新坐标的原点上)绕原来的y轴旋转。
//移动原坐标系的原点到{-10,0,0},即原来的y轴通过新坐标系的{10,0,0}
Matrix translate1Transform=MatrixTranslate(-10,0,0);
//绕原来的y轴旋转,即在新坐标系下绕经过{10,0,0}的轴旋转
Matrix rotateTransform=MatrixRotateY(angle);
       
//两个矩阵相乘,得到复合变换矩阵
Matrix complexTransform =
        MatrixMultiply(translate1Transform,rotateTransform);



绕y轴(图中中央黑线)转圈

完整程序如下:
#include <raylib.h>
#include <raymath.h>

int main(void)
{
        // 初始化
        const int screenWidth = 640;
        const int screenHeight = 480;
       
        //启用反锯齿
        SetConfigFlags(FLAG_MSAA_4X_HINT);
       
        //初始化窗口
        InitWindow(screenWidth, screenHeight, "Sample");
       
        // 初始化摄像机
        Camera3D camera = { 0 };
        camera.position = (Vector3){ 5.0f, 50.0f, 7.0f }; //相机所在位置{x,y,z}
        camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; //相机朝向位置{x,y,z}
        camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; //相机正上方朝向矢量
        camera.fovy = 40; //相机视野宽度
        camera.projection = CAMERA_PERSPECTIVE; //采用透视投影
       
        //创建几何体
        Mesh mesh = GenMeshCube(5,5,15);
       
        //设置动画帧率(刷新率,fps)为30帧/秒
        SetTargetFPS(30);
        //--------------------------------------------------------------------------------------
        double angle;
       
        int colorHue = 0;
        // 主游戏循环
        while (!WindowShouldClose())    //关闭窗口或者按ESC键时返回true
        {
               
                // 每次循环更新一帧
                double time = GetTime();
                angle = time*0.3;
               
                colorHue++;
                colorHue%=360;
               
                //创建贴图
                Image checked = GenImageChecked(2,2,1,1,ColorFromHSV(colorHue,1,1),LIGHTGRAY);
                Texture2D texture = LoadTextureFromImage(checked);
                UnloadImage(checked);
               
                //基于贴图创建材质
                Material material=LoadMaterialDefault();
                material.maps[MATERIAL_MAP_DIFFUSE].texture = texture;
               
                //移动原坐标系的原点到{-10,0,0},即原来的y轴通过新坐标系的{10,0,0}
                Matrix translate1Transform=MatrixTranslate(-10,0,0);
                //绕原来的y轴旋转,即在新坐标系下绕经过{10,0,0}的轴旋转
                Matrix rotateTransform=MatrixRotateY(angle);
               
                //两个矩阵相乘,得到复合变换矩阵
                Matrix complexTransform =
                        MatrixMultiply(translate1Transform,rotateTransform);
               
                BeginDrawing();
                ClearBackground(WHITE);
                //以摄像机视角绘制3d内容
                BeginMode3D(camera);
                DrawGrid(10,5);
                DrawLine3D(Vector3{0,100,0},Vector3{0,-100,0},BLACK);
                //绘制网格
                DrawMesh(mesh,material,complexTransform);
                EndMode3D();
                EndDrawing();
               
                //释放材质和贴图
                UnloadMaterial(material);
                UnloadTexture(texture);
        }
        //释放网格
        UnloadMesh(mesh);
        //关闭窗口
        CloseWindow();
       
        return 0;
}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-5-21 12:23 , Processed in 0.103419 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表