raylib绘图库--瞿华
2022.7.1
知乎 1、raylib绘图库简介
知乎 2、raylib 2d动画/游戏教程(1)动画基本原理
知乎 3、raylib 2d动画/游戏教程(2)坐标系与颜色系统
知乎 4、raylib 2d动画/游戏教程(3)图像文件与图层
知乎 5、raylib 2d动画/游戏教程(4)raylib-drawing库
知乎 6、raylib 2d动画/游戏教程(5)键盘与鼠标输入
知乎 7、raylib 2d动画/游戏教程(6)游戏手柄输入
知乎 8、raylib 2d动画/游戏教程(7)音乐和音效
知乎 9、raylib绘制中文内容
知乎 10、使用raygui绘制控件
知乎 11、raylib 3d绘图基础教程(1)坐标系和摄像机
知乎 12、raylib 3d绘图基础教程(2)网格(Mesh)
知乎 13、raylib 3d绘图基础教程(3)几何变换
知乎 14、raylib 3d绘图基础教程(4)3d模型的载入与绘制
raylib 3d绘图基础教程(3)几何变换
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) | 绕Z轴旋转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
#include
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
#include
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
#include
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 translateTransform=MatrixTranslate(-10,0,0);
//绕原来的y轴旋转,即在新坐标系下绕经过{10,0,0}的轴旋转
Matrix rotateTransform=MatrixRotateY(angle);
//两个矩阵相乘,得到复合变换矩阵
Matrix complexTransform =MatrixMultiply(translateTransform,rotateTransform);
绕y轴(图中中央黑线)转圈
完整程序如下:
#include
#include
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 translateTransform=MatrixTranslate(-10,0,0);
//绕原来的y轴旋转,即在新坐标系下绕经过{10,0,0}的轴旋转
Matrix rotateTransform=MatrixRotateY(angle);
//两个矩阵相乘,得到复合变换矩阵
Matrix complexTransform =MatrixMultiply(translateTransform,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;
}