当前位置: 首页 > news >正文

C++实现3D(EasyX)详细教程

一、关于3D

我们看见,这两个三角形是相似的,因此计算很简单

若相对物体的方向是斜的,计算三角函数即可

不会的看代码

 二、EasyX简介

initgraph(长,宽)                                                        打开绘图 或initgraph(长,宽,窗口设置-见下文)

closegraph()                                                            关闭绘图

cleardevice()                                                           清屏

setlinestyle()                                                           设置线条样式

setfillstyle()                                                             设置填充样式

setcolor()                                                                设置前景色 包括setlinecolor()&settextcolor()

setbkcolor()                                                            设置背景色

setfillcolor()                                                            设置填充色

setbkmode()                                                           设置背景(不)透明

RGBtoGRAY() & RGBtoHSV() &RGBtoHSL()      转换 也可以将前后调换

EGERGB() & EGEGET_R() &  EGEGET_G() & EGEGET_B() 将R/G/B与color数据转换

putpixel(x坐标,y坐标,颜色)                                    画点

line(起点x,起点y,终点x,终点y)                                画线

rectangle(起点x,起点y,终点x,终点y)                      画空心矩形 包括fill前缀(实心)

roundrect(起点x,起点y,终点x,终点y)                     画空心矩形 包括fill前缀(实心)

circle(中心x,中心y,半径)                                         画圆 包括fill前缀(实心) & f后缀(快速)

ellipse(中心x,中心y,半径1,半径2)                           画椭圆 包括fill前缀(实心) & f后缀(快速)

polygon()                                                                画多边形 包括fill前缀(实心) 

outtextxy(x坐标,y坐标,文本)                                  输出文本

mousemsg()                                                           有没有鼠标信息

getmouse()                                                             返回鼠标信息(没有则等待)

keymsg()                                                                 有没有键盘信息

getkey()                                                                   返回键盘信息(没有则等待)

newimage()                                                             分配图片地址

getimage(指针,文件名)[仅EGE]、loadimage()[仅Easyx]  获取图像(从文件)

getimage(起点x,起点y,终点x,终点y)                       获取图像(从屏幕)

putimage(指针,起点x,起点y)                                   输出图像(到屏幕)

saveimage()                                                            输出图像(到文件 

GetImageBuffer()                                                   获取缓冲区指针(快速绘图、读图) 

......

三、C++实现

#include <graphics.h>
#include <conio.h>
#include <math.h>
#include <time.h>
#include <windows.h>

const int WIDTH = 1200;
const int HEIGHT = 1000;
const float PI = 3.1415926535f;

// 摄像机参数
struct Camera {
    float x = 0, y = 1, z = 0;  // 初始位置
    float yaw = 0, pitch = 0;   // 视角角度
    float speed = 0.1f;         // 移动速度
    float sensitivity = 0.008f; // 鼠标灵敏度
} camera;

// 3D点结构体
struct Point3D { float x, y, z; };

// 立方体面结构体
struct Face {
    int points[4];  // 顶点索引
    COLORREF color;  // 颜色
    float depth;     // 深度用于排序
    Point3D normal;  // 法向量
};

// 立方体顶点
Point3D cubePoints[] = {
    {-1, -1, -1}, {1, -1, -1}, {1, 1, -1}, {-1, 1, -1},
    {-1, -1, 1},  {1, -1, 1},  {1, 1, 1},  {-1, 1, 1}
};

// 立方体面定义
Face cubeFaces[] = {
    {{0,1,2,3}, RED},    // 前
    {{4,5,6,7}, BLUE},   // 后
    {{0,3,7,4}, GREEN},  // 左
    {{1,5,6,2}, YELLOW}, // 右
    {{3,2,6,7}, CYAN},   // 上
    {{0,4,5,1}, MAGENTA} // 下
};

// 计算面的法向量
void calculateNormals() {
    for (auto& face : cubeFaces) {
        Point3D p1 = cubePoints[face.points[0]];
        Point3D p2 = cubePoints[face.points[1]];
        Point3D p3 = cubePoints[face.points[2]];

        // 计算两个向量
        Point3D v1 = {p2.x - p1.x, p2.y - p1.y, p2.z - p1.z};
        Point3D v2 = {p3.x - p1.x, p3.y - p1.y, p3.z - p1.z};

        // 计算叉积(法向量)
        face.normal = {
            v1.y * v2.z - v1.z * v2.y,
            v1.z * v2.x - v1.x * v2.z,
            v1.x * v2.y - v1.y * v2.x
        };

        // 归一化法向量
        float len = sqrt(face.normal.x * face.normal.x + face.normal.y * face.normal.y + face.normal.z * face.normal.z);
        face.normal.x /= len;
        face.normal.y /= len;
        face.normal.z /= len;
    }
}

// 将3D坐标转换为屏幕坐标
POINT project(Point3D p) {
    // 相对摄像机的位置
    float dx = p.x - camera.x;
    float dy = p.y - camera.y;
    float dz = p.z - camera.z;

    // 旋转(绕Y轴和X轴)
    float cosY = cos(camera.yaw), sinY = sin(camera.yaw);
    float cosP = cos(camera.pitch), sinP = sin(camera.pitch);

    // 旋转计算
    float x = dx*cosY - dz*sinY;
    float z = dz*cosY + dx*sinY;
    float y = dy*cosP - z*sinP;
    z = z*cosP + dy*sinP;

    // 透视投影
    if(z <= 0) z = 0.0001f;
    float f = 400 / z;
    return { (int)(x*f + WIDTH/2), (int)(-y*f + HEIGHT/2) };
}

void drawScene() {
    POINT screenPoints[8];
    
    // 投影所有顶点
    for(int i=0; i<8; i++)
        screenPoints[i] = project(cubePoints[i]);

    // 计算每个面的深度并排序
    for(auto& face : cubeFaces) {
        float avgZ = 0;
        for(int i : face.points)
            avgZ += cubePoints[i].z - camera.z;
        face.depth = avgZ/4;
    }

    // 按深度排序(远到近)
    qsort(cubeFaces, 6, sizeof(Face), [](const void* a, const void* b) {
        return (int)(((Face*)b)->depth - ((Face*)a)->depth);
    });

    // 绘制每个面
    for(auto& face : cubeFaces) {
        POINT poly[4];
        for(int i=0; i<4; i++)
            poly[i] = screenPoints[face.points[i]];

        // 直接使用面的颜色填充
        setfillcolor(face.color);
        fillpoly(4, (int*)poly);
    }
}

// 绘制准星
void drawCrosshair() {
    setcolor(BLACK);
    line(WIDTH / 2 - 10, HEIGHT / 2, WIDTH / 2 + 10, HEIGHT / 2);
    line(WIDTH / 2, HEIGHT / 2 - 10, WIDTH / 2, HEIGHT / 2 + 10);
}

// 绘制建筑物
void drawBuilding(float x, float y, float z, float width, float height, float depth, COLORREF color) {
    Point3D buildingPoints[] = {
        {x - width / 2, y - height / 2, z - depth / 2},
        {x + width / 2, y - height / 2, z - depth / 2},
        {x + width / 2, y + height / 2, z - depth / 2},
        {x - width / 2, y + height / 2, z - depth / 2},
        {x - width / 2, y - height / 2, z + depth / 2},
        {x + width / 2, y - height / 2, z + depth / 2},
        {x + width / 2, y + height / 2, z + depth / 2},
        {x - width / 2, y + height / 2, z + depth / 2}
    };

    Face buildingFaces[] = {
        {{0,1,2,3}, color},    // 前
        {{4,5,6,7}, color},   // 后
        {{0,3,7,4}, color},  // 左
        {{1,5,6,2}, color}, // 右
        {{3,2,6,7}, color},   // 上
        {{0,4,5,1}, color} // 下
    };

    POINT screenPoints[8];
    for(int i=0; i<8; i++)
        screenPoints[i] = project(buildingPoints[i]);

    for(auto& face : buildingFaces) {
        POINT poly[4];
        for(int i=0; i<4; i++)
            poly[i] = screenPoints[face.points[i]];

        setfillcolor(face.color);
        fillpoly(4, (int*)poly);
    }
}

// 绘制树
void drawTree(float x, float y, float z) {
    // 树干
    drawBuilding(x, y, z, 0.2f, 1.0f, 0.2f, RGB(139, 69, 19));
    // 树冠
    drawBuilding(x, y + 0.8f, z, 1.0f, 0.5f, 1.0f, RGB(34, 139, 34));
}

// 处理输入
void processInput() {
    // 处理键盘输入
    float moveX = 0, moveZ = 0;
    if (GetAsyncKeyState('W') & 0x8000) moveX = 1;  // 前
    if (GetAsyncKeyState('S') & 0x8000) moveX = -1; // 后
    if (GetAsyncKeyState('A') & 0x8000) moveZ = -1; // 左
    if (GetAsyncKeyState('D') & 0x8000) moveZ = 1;  // 右

    // 计算移动方向(基于当前视角)
    float speed = camera.speed;
    float dx = moveX * cos(camera.yaw) - moveZ * sin(camera.yaw);
    float dz = moveZ * cos(camera.yaw) + moveX * sin(camera.yaw);
    camera.z += dx * speed;
    camera.x += dz * speed;

    // 处理鼠标输入
    MOUSEMSG m;
    while(MouseHit()) {
        m = GetMouseMsg();
        if(m.uMsg == WM_MOUSEMOVE) {
            // 计算相对鼠标移动量
            static int lastX = WIDTH / 2, lastY = HEIGHT / 2;
            int dx = m.x - lastX;
            int dy = m.y - lastY;
            lastX = m.x;
            lastY = m.y;

            // 更新视角
            camera.yaw += dx * camera.sensitivity;
            camera.pitch -= dy * camera.sensitivity;

            // 限制垂直视角
            if(camera.pitch > PI/2.5) camera.pitch = PI/2.5;
            if(camera.pitch < -PI/2.5) camera.pitch = -PI/2.5;

        }
    }
}

int main() {
    initgraph(WIDTH, HEIGHT);
    setbkcolor(WHITE);
    BeginBatchDraw();
    ShowCursor(FALSE); // 隐藏鼠标

    // 计算法向量
    calculateNormals();

    // 将鼠标初始位置设置为窗口中心
    SetCursorPos(WIDTH / 2, HEIGHT / 2);

    while(true) {
        // 检测 ESC 键退出
        if (GetAsyncKeyState(VK_ESCAPE) & 0x8000) {
            break;
        }

        cleardevice(); // 清屏
        processInput();
        //drawScene();
        drawBuilding(0, 0, 5, 2, 3, 2, RGB(128, 128, 128)); // 绘制建筑物
        drawBuilding(0, 0, 5, 2, 3, 2, RGB(128, 128, 128)); // 绘制建筑物
        drawTree(3, 0, 5); // 绘制树
        drawTree(5, 0, 3); // 绘制树
        drawCrosshair();
        FlushBatchDraw(); // 刷新缓冲区
        Sleep(10); // 控制帧率
    }
    
    EndBatchDraw();
    closegraph();
    ShowCursor(TRUE); // 恢复鼠标显示
    return 0;
}

效果:

编译:

g++ -o 3d 3d.cpp -std=c++11 -leasyx

 w a s d可以行走

相关文章:

  • DeepSeek蒸馏TinyLSTM实操指南
  • deepseek使用记录18——文化基因之文化融合
  • 数据结构(初阶)(六)----队列
  • Linux NAT和代理服务器
  • 【开源免费】基于SpringBoot+Vue.JS周边游平台系统(JAVA毕业设计)
  • 0x05 部门功能开发日志技术
  • 图漾PercipioIPTool软件使用
  • python基于后门的神经网络模型水印通用方法
  • AndroidStudio下载旧版本方法
  • miqiu的分布式锁(四):MySQL悲观锁解析
  • 线程控制(创建、终止、等待、分离)
  • 定位需要优化的SQL ,及如何优化SQL
  • 深入xtquant:掌握市场基础信息的获取技巧
  • React 第二十七节 <StrictMode> 的使用方法及注意事项
  • Unity XR-XR Interaction Toolkit开发使用方法(十三)组件介绍(XR Grab Interactable)
  • 开源项目Wren AI 文本到SQL解决方案详解
  • 电池管理系统(BMS)架构详细解析:原理与器件选型指南
  • 力扣785. 判断二分图
  • 在AIStudio飞桨星河社区一键部署DeepSeek-r1:70b模型
  • leetcode第216题组合总和Ⅲ
  • 家国万里·时光故事会|构筑中国船舰钢筋铁骨,她在焊花里展现工匠风范
  • 孟夏韵评《无序的学科》丨误读与重构的文化漂流
  • 首次带人形机器人走科技节红毯,傅利叶顾捷:机器人行业没包袱,很多事都能从零开始
  • 广西壮族自治区政府主席蓝天立任上被查
  • 商务部回应稀土出口管制问题
  • 国新办将就2025年4月份国民经济运行情况举行新闻发布会