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

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可以行走

相关文章:

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

一、关于3D 我们看见&#xff0c;这两个三角形是相似的&#xff0c;因此计算很简单 若相对物体的方向是斜的&#xff0c;计算三角函数即可 不会的看代码 二、EasyX简介 initgraph(长,宽) 打开绘图 或initgraph(长,宽…...

Centos7部署k8s(单master节点安装)

单master节点部署k8s集群(Centos) 一、安装前准备 1、修改主机名 按照资源准备修改即可 # master01 hostnamectl set-hostname master01 ; bash # node1 hostnamectl set-hostname node1 ; bash # node2 hostnamectl set-hostname node2 ; bash2、修改hosts文件 以下命令所…...

【C】链式二叉树算法题1 -- 单值二叉树

leetcode链接https://leetcode.cn/problems/univalued-binary-tree/description/ 1 题目描述 如果二叉树每个节点都具有相同的值&#xff0c;那么该二叉树就是单值二叉树。只有给定的树是单值二叉树时&#xff0c;才返回 true&#xff1b;否则返回 false。 示例 1&#xff1…...

系统架构设计师—计算机基础篇—计算机网络

文章目录 网络互联模型网络协议与标准应用层协议FTP协议TFTP协议 HTTP协议HTTPS协议 DHCP动态主机配置协议DNS协议迭代查询递归查询 传输层协议网络层协议IPV4协议IPV6协议IPV6数据报的目的地址IPV4到IPV6的过渡技术 网络设计分层设计接入层汇聚层核心层 网络布线综合布线系统工…...

VScode在windows10上使用clang-format

用途&#xff1a;自动调整代码格式&#xff0c;如缩进等。 clang-format官方文档&#xff1a;ClangFormat — Clang 21.0.0git documentation 前提&#xff1a;有一个.clang-format文件 下载LLVM&#xff1a;https://github.com/llvm/llvm-project/releases&#xff0c;将可…...

word转换为pdf后图片失真解决办法、高质量PDF转换方法

1、安装Adobe Acrobat Pro DC 自行安装 2、配置Acrobat PDFMaker &#xff08;1&#xff09;点击word选项卡上的Acrobat插件&#xff0c;&#xff08;2&#xff09;点击“首选项”按钮&#xff0c;&#xff08;3&#xff09;点击“高级配置”按钮&#xff08;4&#xff09;点…...

CSS3 圆角:实现与优化指南

CSS3 圆角&#xff1a;实现与优化指南 随着网页设计的发展&#xff0c;CSS3 圆角已经成为了现代网页设计中不可或缺的元素之一。本文将详细讲解 CSS3 圆角的基本用法、实现方式以及优化技巧&#xff0c;帮助您在网页设计中更好地运用这一功能。 一、CSS3 圆角基本用法 1.1 基…...

蓝桥杯 灯笼大乱斗【算法赛】

问题描述 元宵佳节&#xff0c;一场别开生面的灯笼大赛热闹非凡。NN 位技艺精湛的灯笼师依次落座&#xff0c;每位师傅都有相应的资历值&#xff0c;其中第 ii 位师傅的资历值为 AiAi​。从左到右&#xff0c;师傅们的资历值逐级递增&#xff08;即 A1<A2<⋯<ANA1​&l…...

【零基础C语言】第四节 数组

【零基础C语言系列】 【零基础C语言】第一节 C语言概述【数制进制码制】-CSDN博客 【零基础C语言】第二节 数据类型、运算符、表达式-CSDN博客 【零基础C语言】第三节 控制结构-CSDN博客 一、一维数组...

【多模态大模型学习】位置编码的学习记录

【多模态大模型学习】位置编码的学习记录 0.前言1. sinusoidal编码1.0 数学知识——复数1.0.1 复数乘法、共轭复数1.0.2 复数的指数表示 1.1 sinusoidal编码来历1.2 代码实现 2. Rotary Positional Embedding (RoPE) ——旋转位置编码2.1 RoPE来历2.2 代码实现2.2.1 GPT-J风格的…...

vector 面试点总结

ps&#xff1a;部分内容使用“AI”查询 一、入门 1、什么是vector 动态数组容器&#xff0c;支持自动扩容、随机访问和连续内存存储。 2、怎么创建-初始化vector std::vector<int> v; // 创建空vectorstd::vector<int> v {1, 2, 3}; // 直接初始化std::vec…...

正式页面开发-登录注册页面

整体路由设计&#xff1a; 登录和注册的切换是切换组件或者是切换内容&#xff08;v-if和 v-else)&#xff0c;因为点击两个之间路径是没有变化的。也就是登录和注册共用同一个路由。登录是独立的一级路由。登录之后进到首页&#xff0c;有三个大模块&#xff1a;文章分类&…...

Spring项目-抽奖系统(实操项目-用户管理接口)(END)

^__^ (oo)\______ (__)\ )\/\ ||----w | || || 一&#xff1a;前言&#xff1a; 活动创建及展示博客链接&#xff1a;Spring项目-抽奖系统(实操项目-用户管理接口)(THREE)-CSDN博客 上一次完成了活动的创建和活动的展示&#xff0c;接下来就是重头戏—…...

Kafka面试题及原理

1. 消息可靠性&#xff08;不丢失&#xff09; 使用Kafka在消息的收发过程都会出现消息丢失&#xff0c;Kafka分别给出了解决方案 生产者发送消息到Brocker丢失消息在Brocker中存储丢失消费者从Brocker 幂等方案&#xff1a;【分布式锁、数据库锁&#xff08;悲观锁、乐观锁…...

Jenkinsfile流水线构建教程

前言 Jenkins 是目前使用非常广泛的自动化流程的执行工具, 我们目前的一些自动化编译, 自动化测试都允许在 Jenkins 上面. 在 Jenkins 的术语里面, 一些自动化工作联合起来称之为流水线, 比如拉取代码, 编译, 运行自动化测试等. 本文的主要目的是引导你快速熟悉 Jenkinsfile …...

CSS—text文本、font字体、列表list、表格table、表单input、下拉菜单select

目录 1.文本 2.字体 3.列表list a.无序列表 b.有序列表 c.定义列表 4.表格table a.内容 b.合并单元格 3.表单input a.input标签 b.单选框 c.上传文件 4.下拉菜单 1.文本 属性描述color设置文本颜色。direction指定文本的方向 / 书写方向。letter-spacing设置字符…...

API接口:企业名称、注册号、统一社会信用代码、企业类型、成立日期和法定代表人等数据 API 接口使用指南

API接口&#xff1a;企业名称、注册号、统一社会信用代码、企业类型、成立日期和法定代表人等数据 API 接口使用指南 本文详细介绍一种基于 Web 搜索方式实现的企业信息查询接口&#xff0c;适用于数据补全、企业资质验证、信息查询等场景。文章内容涵盖接口功能、请求参数、返…...

在.net中,async/await的理解

一、什么是同步&#xff1f;什么是异步&#xff1f; 在.net中&#xff0c;async 和 await 是两个关键字&#xff0c;async 关键字用于声明一个方法是异步方法&#xff0c;该方法可以包含一个或多个 await 表达式。await 关键字是用于在异步方法中等待一个任务&#xff08;Task…...

水果识别系统 | BP神经网络水果识别系统,含GUI界面(Matlab)

使用说明 代码下载&#xff1a;BP神经网络水果识别系统&#xff0c;含GUI界面&#xff08;Matlab&#xff09; BP神经网络水果识别系统 一、引言 1.1、研究背景及意义 在当今科技迅速发展的背景下&#xff0c;人工智能技术尤其是在图像识别领域的应用日益广泛。水果识别作为…...

40岁开始学Java:Java中单例模式(Singleton Pattern),适用场景有哪些?

在Java中&#xff0c;单例模式&#xff08;Singleton Pattern&#xff09;用于确保一个类只有一个实例&#xff0c;并提供全局访问点。以下是详细的实现方式、适用场景及注意事项&#xff1a; 一、单例模式的实现方式 1. 饿汉式&#xff08;Eager Initialization&#xff09; …...

李宏毅机器学习课程学习笔记04 | 浅谈机器学习-宝可梦、数码宝贝分类器

文章目录 案例&#xff1a;宝可梦、数码宝贝分类器第一步&#xff1a;需要定义一个含有未知数的function第二步&#xff1a;loss of a function如何Sample Training Examples > 如何抽样可以得到一个较好的结果如何权衡模型的复杂程度 Tradeoff of Model Complexity todo 这…...

C++11中的右值引用和完美转发

C11中的右值引用和完美转发 右值引用 右值引用是 C11 引入的一种新的引用类型&#xff0c;用 && 表示。它主要用于区分左值和右值&#xff0c;并且可以实现移动语义&#xff0c;避免不必要的深拷贝&#xff0c;提高程序的性能。左值通常是可以取地址的表达式&#xf…...

Redis详解(实战 + 面试)

目录 Redis 是单线程的&#xff01;为什么 Redis-Key(操作redis的key命令) String 扩展字符串操作命令 数字增长命令 字符串范围range命令 设置过期时间命令 批量设置值 string设置对象,但最好使用hash来存储对象 组合命令getset,先get然后在set Hash hash命令: h…...

ISP CIE-XYZ色彩空间

1. 颜色匹配实验 1931年&#xff0c;CIE综合了前人实验数据&#xff0c;统一采用700nm&#xff08;红&#xff09;、546.1nm&#xff08;绿&#xff09;、435.8nm&#xff08;蓝&#xff09;​作为标准三原色波长&#xff0c;绘制了色彩匹配函数&#xff0c;如下图。选定这些波…...

【强化学习笔记1】从强化学习的基本概念到近端策略优化(PPO)

好久没有更新了。最近想学习一下强化学习&#xff0c;本系列是李宏毅老师强化学习的课程笔记。 1. Policy-based Model 1.1 Actor 在policy-based model中&#xff0c;主要的目的就是训练一个actor。 对于一个episode&#xff08;例如&#xff0c;玩一局游戏&#xff09;&…...

Deepseek对ChatGPT的冲击?

从测试工程师的视角来看&#xff0c;DeepSeek对ChatGPT的冲击主要体现在**测试场景的垂直化需求与通用模型局限性之间的博弈**。以下从技术适配性、效率优化、风险控制及未来趋势四个维度展开分析&#xff1a; --- ### **一、技术适配性&#xff1a;垂直领域能力决定工具选择…...

STM32中的ADC

目录 一&#xff1a;什么是ADC 二&#xff1a;ADC的用途 三&#xff1a;STM32F103ZET6的ADC 3.1ADC对应的引脚 3.2ADC时钟 3.3ADC的工作模式 ​编辑3.4ADC校准 3.5ADC转换结构和实际电压的换算 四&#xff1a;ADC配置步骤 五&#xff1a;两个重要的函数 一&#xff1a…...

开启AI短剧新纪元!SkyReels-V1/A1双剑合璧!昆仑万维开源首个面向AI短剧的视频生成模型

论文链接&#xff1a;https://arxiv.org/abs/2502.10841 项目链接&#xff1a;https://skyworkai.github.io/skyreels-a1.github.io/ Demo链接&#xff1a;https://www.skyreels.ai/ 开源地址&#xff1a;https://github.com/SkyworkAI/SkyReels-A1 https://github.com/Skywork…...

【uniapp】在UniApp中实现持久化存储:安卓--生成写入数据为jsontxt

在移动应用开发中&#xff0c;数据存储是一个至关重要的环节。对于使用UniApp开发的Android应用来说&#xff0c;缓存&#xff08;Cache&#xff09;是一种常见的数据存储方式&#xff0c;它能够提高应用的性能和用户体验。然而&#xff0c;缓存数据在用户清除缓存或清除应用数…...

大白话React第十一章React 相关的高级特性以及在实际项目中的应用优化

假设我们已经对 React 前端框架的性能和可扩展性评估有了一定了解&#xff0c;接下来的阶段可以深入学习 React 相关的高级特性以及在实际项目中的应用优化&#xff0c;以下是详细介绍及代码示例&#xff1a; 1. React 高级特性的深入学习 1.1 React 并发模式&#xff08;Con…...