raylib 3d绘图基础教程(3):几何变换
系列文章目录:[*]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, &#34;Sample&#34;);
// 初始化摄像机
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.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, &#34;Sample&#34;);
// 初始化摄像机
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.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, &#34;Sample&#34;);
// 初始化摄像机
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.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, &#34;Sample&#34;);
// 初始化摄像机
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.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;
}
页:
[1]