在数字时代,3D视觉效果已经成为游戏、电影、模拟和许多其他领域不可或缺的一部分。C++因其性能优势而成为实现这些效果的首选语言之一👍。本文将带你了解如何在C++中创建3D视觉效果,并且保证整个过程的简单、方便和高效😎。
1.选择编译器(IDE)🤗
考虑到大部分初学者的实战经验和设备性能较为低下,我们不得不抛弃功能强大但复杂的Visual Studio等IDE🤔,这里选择功能较为强大并且用法简单、占用较低的小熊猫C++(RedPanda-Cpp)👀💡。首先进入小熊猫C++官网下载最新版的小熊猫C++(网盘中的.exe文件为安装包):
安装完毕后继续下载要用的库文件,这里我们使用raylib:
按需求选择对应系统位数的库压缩包,并按需求下载驱动。raylib缺省使用OpenGL 3.3。如果电脑上缺少OpenGL驱动,或者不支持OpenGL 3.3,则运行小熊猫C++自带的raylib时会报错😫:
GLFW error: 65543 Description : WGL:OpenGL profile requested but WGL_ARB_create_context_profile is unavailable.
如果显卡不支持OpenGL 2.1,需要下载软渲染的mesa驱动,用解压出来的opengl32.dll替换C:\windows\system32中的同名文件。注意操作系统是32位还是64位,使用对应的版本。
解压库文件后,将解压后的文件夹放入小熊猫C++所在的文件夹中,替换原来的文件夹。raylib是一个轻量级、跨平台的游戏开发库,它支持2D和3D图形渲染、音频处理和数学运算。这个库使用纯C语言编写,但提供了对C++的支持,并且有超过50种不同语言的绑定,而且在小熊猫C++中,加入这个库非常方便😍,所以选择这个库进行教学😆。
2.测试代码🤯
上述步骤走完之后,我们可以通过这一串代码测试这个库能不能正常使用,下面这个程序绘制了一个旋转的立方体(其实是摄像机在围绕立方体旋转😅):
#include <raylib.h>
#include <math.h>int main(void)
{// 初始化const int screenWidth = 640;const int screenHeight = 480;//启用反锯齿SetConfigFlags(FLAG_MSAA_4X_HINT);//初始化窗口InitWindow(screenWidth, screenHeight, "测试");// 初始化摄像机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 = 70.0f; //相机视野宽度camera.projection = CAMERA_PERSPECTIVE; //采用透视投影//设置动画帧率(刷新率,fps)为30帧/秒SetTargetFPS(30);//--------------------------------------------------------------------------------------int angle=0; //多边形旋转角度// 主游戏循环while (!WindowShouldClose()) //关闭窗口或者按ESC键时返回true{double time = GetTime(); // 每次循环更新一帧// 摄像机围绕y轴转动double cameraTime = time*0.3;camera.position.x = (float)cos(cameraTime)*20.0f;camera.position.z = (float)sin(cameraTime)*20.0f;BeginDrawing();ClearBackground(BLACK);//以摄像机视角绘制3d内容BeginMode3D(camera);DrawCube(Vector3{0,0,0},10,10,10,BLACK);DrawCubeWires(Vector3{0,0,0},10,10,10,WHITE);EndMode3D();EndDrawing();}//关闭窗口CloseWindow();return 0;
}
如果能成功运行,你应该能看到下面的界面,能的话就可以开始下一步了,如果有问题先提在评论区😕。
3.尝试做出基础3D效果😀
1.示例解析🧐
首先,我们先对上面测试代码进行讲解:
-
包含库文件📁:
#include <raylib.h> #include <math.h>
这两行代码包含了程序所需的两个库:
raylib.h
提供了创建窗口、处理输入、渲染2D和3D图形等功能,而math.h
提供了数学计算功能,比如三角函数等📐📏。 -
主函数定义:
int main()
这是C语言程序的入口点,程序从这里开始执行。
-
定义屏幕参数💻:
const int screenWidth = 640; const int screenHeight = 480;
这里定义了窗口的宽度和高度,分别设置为640像素和480像素。
-
设置配置标志:
SetConfigFlags(FLAG_MSAA_4X_HINT);
SetConfigFlags
函数用于设置全局配置标志。FLAG_MSAA_4X_HINT
是一个标志,它建议raylib
使用4倍多重采样抗锯齿(MSAA),以减少渲染图像中的锯齿效应。 -
初始化窗口🖱:
InitWindow(screenWidth, screenHeight, "测试");
InitWindow
函数用于创建一个窗口,其宽度和高度由前面定义的screenWidth
和screenHeight
变量指定,窗口标题设置为“测试”。 -
初始化3D摄像机📷:
Camera3D camera = { 0 }; camera.position = (Vector3){ 40.0f, 20.0f, 0.0f }; camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; camera.fovy = 70.0f; camera.projection = CAMERA_PERSPECTIVE;
这里创建了一个3D摄像机对象,并设置了其属性:
-
position
:摄像机在世界空间中的位置,这里设置为(40, 20, 0)
。 -
target
:摄像机的目标点,即摄像机朝向的位置,这里设置为(0, 0, 0)
,表示摄像机朝向世界原点🎥⏳。 -
up
:摄像机的上方向,这里设置为(0, 1, 0)
,表示摄像机的上方向与世界Y轴对齐。 -
fovy
:摄像机的垂直视野角度,这里设置为70.0f
,表示视野宽度。 -
projection
:摄像机的投影类型,这里设置为CAMERA_PERSPECTIVE
,表示使用透视投影。
-
-
设置帧率:
SetTargetFPS(30);
SetTargetFPS
函数用于设置程序的目标帧率,这里设置为30帧/秒。 -
定义旋转角度变量:
int angle = 0;
定义了一个变量
angle
用于存储多边形的旋转角度,虽然在这段代码中并没有使用这个变量😜。 -
主游戏循环:
while (!WindowShouldClose())
这是一个无限循环,只要窗口没有关闭(用户没有点击关闭按钮或者按下ESC键),循环就会继续执行。
-
更新摄像机位置🎥:
double time = GetTime(); double cameraTime = time * 0.3; camera.position.x = (float)cos(cameraTime) * 20.0f; camera.position.z = (float)sin(cameraTime) * 20.0f;
-
GetTime
函数返回自程序启动以来经过的时间(秒)⏱。 -
cameraTime
是根据time
计算的一个值,乘以0.3是为了减慢摄像机的旋转速度🥶。 -
cos(cameraTime)
和sin(cameraTime)
分别计算cameraTime
的余弦和正弦值,这些值用于更新摄像机的X和Z坐标,使其围绕Y轴旋转😵。
-
-
渲染过程📽:
BeginDrawing(); ClearBackground(BLACK); BeginMode3D(camera); DrawCube(Vector3{0,0,0},10,10,10,BLACK); DrawCubeWires(Vector3{0,0,0},10,10,10,WHITE); EndMode3D(); EndDrawing();
-
BeginDrawing
和EndDrawing
标记了渲染过程的开始和结束。 -
ClearBackground(BLACK)
将背景设置为黑色👨🏿。 -
BeginMode3D(camera)
开始3D渲染模式,并使用之前定义的摄像机参数。 -
DrawCube(Vector3{0,0,0},10,10,10,BLACK)
在世界原点(0,0,0)
处绘制一个边长为10单位的黑色立方体。 -
DrawCubeWires(Vector3{0,0,0},10,10,10,WHITE)
在同一位置绘制一个边长为10单位的白色👨🏻线框立方体,用于显示立方体的轮廓。 -
EndMode3D
结束3D渲染模式。
-
-
关闭窗口:
CloseWindow();
程序结束时调用
CloseWindow
函数关闭窗口🤓。 -
返回值:
return 0;
程序正常结束时返回0,表示程序成功执行完毕。
这段代码展示了如何使用 raylib
创建一个3D场景,设置摄像机,并在其中绘制一个旋转的立方体。通过改变摄像机的位置,实现了一个简单的旋转效果。
2.继续进阶😡
掌握好上面的代码,就可以开始进阶了。现在我们尝试做一个可以控制颜色、角度、距离的“正方体”吧!😈
步骤 1: 设置3D环境
在这一步中,我们设置3D渲染所需的基本环境,包括初始化窗口和配置3D相机📸。
// 初始化窗口
InitWindow(800, 450, "3D Cube");// 设置3D相机的位置、目标点和上向量
Camera camera = { 0 };
camera.position = (Vector3){ 5.0f, 5.0f, 5.0f }; // 相机位置设置在(5, 5, 5)
camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // 相机目标点设置在原点(0, 0, 0)
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // 相机的上向量设置为(0, 1, 0),即Y轴正方向
camera.fovy = 45.0f; // 相机的垂直视野角度设置为45度
camera.projection = CAMERA_PERSPECTIVE; // 相机投影类型设置为透视投影
步骤 2: 配置3D对象和颜色
在这一步中,我们设置3D对象(在这个案例中是一个立方体)的初始颜色。
// 设置正方体的初始颜色
Color cubeColor = RED; // 初始颜色设置为红色
步骤 3: 主循环和用户输入处理💻
这一步包含了程序的主循环,以及处理用户输入(如键盘操作)的逻辑。
// 主循环
while (!WindowShouldClose()) {// 检测按键输入以移动相机Vector3 cameraMove = { 0.0f, 0.0f, 0.0f };if (IsKeyDown(KEY_UP)) cameraMove.z -= 0.05f; // 上键被按下时,相机沿Z轴负方向移动if (IsKeyDown(KEY_DOWN)) cameraMove.z += 0.05f; // 下键被按下时,相机沿Z轴正方向移动if (IsKeyDown(KEY_LEFT)) cameraMove.x -= 0.05f; // 左键被按下时,相机沿X轴负方向移动if (IsKeyDown(KEY_RIGHT)) cameraMove.x += 0.05f; // 右键被按下时,相机沿X轴正方向移动if (IsKeyDown(KEY_PAGE_UP)) cameraMove.y += 0.05f; // PageUp键被按下时,相机沿Y轴正方向移动if (IsKeyDown(KEY_PAGE_DOWN)) cameraMove.y -= 0.05f; // PageDown键被按下时,相机沿Y轴负方向移动// 更新相机位置camera.position.x += cameraMove.x;camera.position.y += cameraMove.y;camera.position.z += cameraMove.z;// 检测数字键输入以更改正方体颜色if (IsKeyPressed(KEY_ONE)) cubeColor = RED;if (IsKeyPressed(KEY_TWO)) cubeColor = GOLD;// ... 其他数字键的检测和颜色设置
}
步骤 4: 渲染3D场景🎉
在这一步中,我们处理所有的渲染操作,包括清除屏幕、开启3D模式、绘制3D对象和结束3D模式。
// 清除屏幕
BeginDrawing();
ClearBackground(RAYWHITE); // 将背景设置为RAYWHITE颜色,这是一种亮灰色// 开始3D模式
BeginMode3D(camera);// 绘制3D立方体
DrawCube((Vector3){ 0.0f, 0.0f, 0.0f }, 2.0f, 2.0f, 2.0f, cubeColor); // 绘制一个边长为2的立方体
DrawCubeWires((Vector3){ 0.0f, 0.0f, 0.0f }, 2.0f, 2.0f, 2.0f, GRAY); // 绘制一个边长为2的线框立方体,颜色为灰色// 结束3D模式
EndMode3D();// 绘制文本和帧率
DrawText("Use arrow keys to move the camera.", 10, 40, 20, DARKGRAY); // 在屏幕上绘制文本提示
DrawText("Press 1-9 keys to change the cube color.", 10, 70, 20, DARKGRAY);
DrawFPS(10, 10); // 在屏幕上绘制帧率// 结束绘制
EndDrawing();
步骤 5: 程序结束和资源释放
在程序结束时,我们需要释放所有分配的资源,并关闭窗口。
// 关闭窗口和释放资源
CloseWindow();
整体代码
#include "raylib.h"int main() {// 初始化窗口InitWindow(800, 450, "3D Cube");// 设置3D相机的位置、目标点和上向量Camera camera = { 0 };camera.position = (Vector3){ 5.0f, 5.0f, 5.0f };camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };camera.fovy = 45.0f;camera.projection = CAMERA_PERSPECTIVE;// 设置正方体的初始颜色Color cubeColor = RED;// 主循环while (!WindowShouldClose()) {// 检测按键输入以移动相机Vector3 cameraMove = { 0.0f, 0.0f, 0.0f };if (IsKeyDown(KEY_UP)) cameraMove.z -= 0.05f;if (IsKeyDown(KEY_DOWN)) cameraMove.z += 0.05f;if (IsKeyDown(KEY_LEFT)) cameraMove.x -= 0.05f;if (IsKeyDown(KEY_RIGHT)) cameraMove.x += 0.05f;if (IsKeyDown(KEY_PAGE_UP)) cameraMove.y += 0.05f;if (IsKeyDown(KEY_PAGE_DOWN)) cameraMove.y -= 0.05f;// 更新相机位置camera.position.x += cameraMove.x;camera.position.y += cameraMove.y;camera.position.z += cameraMove.z;// 检测数字键输入以更改正方体颜色if (IsKeyPressed(KEY_ONE)) cubeColor = RED;if (IsKeyPressed(KEY_TWO)) cubeColor = GOLD;if (IsKeyPressed(KEY_THREE)) cubeColor = GREEN;if (IsKeyPressed(KEY_FOUR)) cubeColor = BLUE;if (IsKeyPressed(KEY_FIVE)) cubeColor = DARKBLUE;if (IsKeyPressed(KEY_SIX)) cubeColor = VIOLET;if (IsKeyPressed(KEY_SEVEN)) cubeColor = BEIGE;if (IsKeyPressed(KEY_EIGHT)) cubeColor = BROWN;if (IsKeyPressed(KEY_NINE)) cubeColor = LIGHTGRAY;// 清除屏幕BeginDrawing();ClearBackground(RAYWHITE);// 开始3D模式BeginMode3D(camera);// 绘制3D立方体DrawCube((Vector3){ 0.0f, 0.0f, 0.0f }, 2.0f, 2.0f, 2.0f, cubeColor);DrawCubeWires((Vector3){ 0.0f, 0.0f, 0.0f }, 2.0f, 2.0f, 2.0f, GRAY);EndMode3D();// 绘制文本DrawText("Use arrow keys to move the camera.", 10, 40, 20, DARKGRAY);DrawText("Press 1-9 keys to change the cube color.", 10, 70, 20, DARKGRAY);// 绘制帧率DrawFPS(10, 10);EndDrawing();}// 关闭窗口和释放资源CloseWindow();return 0;
}
最后,运行~ 😎🚬
掌声,起!!!!😏