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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/28964.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt 进度条与多线程应用、基于 Qt 的文件复制工具开发

练习1&#xff1a;Qt 进度条与多线程应用 题目描述 开发一个基于 Qt 的应用程序&#xff0c;该应用程序包含一个水平进度条&#xff08;QSlider&#xff09;&#xff0c;并且需要通过多线程来更新进度条的值。请根据以下要求完成代码&#xff1a; 界面设计&#xff1a; 使用 QS…

【算法day2】无重复字符的最长子串 两数之和

无重复字符的最长子串 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长 子串 的长度。 https://leetcode.cn/problems/longest-substring-without-repeating-characters/ class Solution { public:int lengthOfLongestSubstring(string s) {int sub_length …

XHR请求解密:抓取动态生成数据的方法

在如今动态页面大行其道的时代&#xff0c;传统的静态页面爬虫已无法满足数据采集需求。尤其是在目标网站通过XHR&#xff08;XMLHttpRequest&#xff09;动态加载数据的情况下&#xff0c;如何精准解密XHR请求、捕获动态生成的数据成为关键技术难题。本文将深入剖析XHR请求解密…

【漫话机器学习系列】121.偏导数(Partial Derivative)

偏导数&#xff08;Partial Derivative&#xff09;详解 1. 引言 在数学分析、机器学习、物理学和工程学中&#xff0c;我们经常会遇到多个变量的函数。这些函数的输出不仅取决于一个变量&#xff0c;而是由多个变量共同决定的。那么&#xff0c;当其中某一个变量发生变化时&…

[C语言日寄] 字符串操作函数的使用及其拓展

【作者主页】siy2333 【专栏介绍】⌈c语言日寄⌋&#xff1a;这是一个专注于C语言刷题的专栏&#xff0c;精选题目&#xff0c;搭配详细题解、拓展算法。从基础语法到复杂算法&#xff0c;题目涉及的知识点全面覆盖&#xff0c;助力你系统提升。无论你是初学者&#xff0c;还是…

计算机毕业设计Python+Django+Vue3微博数据舆情分析平台 微博用户画像系统 微博舆情可视化(源码+ 文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

ssm_mysql_暖心家装平台

收藏关注不迷路&#xff01;&#xff01; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff08;免费咨询指导选题&#xff09;&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;希望帮助更多…

地下井室可燃气体监测装置:守护地下安全,防患于未“燃”!

在城市的地下&#xff0c;隐藏着无数的燃气管道和井室&#xff0c;它们是城市基础设施建设的重要部分&#xff0c;燃气的使用&#xff0c;给大家的生活提供了极大的便利。在便利生活的背后&#xff0c;也存在潜在的城市安全隐患。 近年来&#xff0c;地下井室可燃气体泄漏事故…

EasyCVR平台赋能农业产业园:AIoT驱动的视频监控与大数据分析解决方案

随着现代农业的快速发展&#xff0c;农业产业园区的规模不断扩大&#xff0c;管理复杂度也随之增加。为了提高农业生产效率、保障农产品质量安全、实现精细化管理和智能化运营&#xff0c;视频信息化建设成为现代农业产业园的重要发展方向。EasyCVR作为一款功能强大的视频监控与…

【三维生成】StarGen:基于视频扩散模型的可扩展的时空自回归场景生成

标题&#xff1a;《StarGen: A Spatiotemporal Autoregression Framework with Video Diffusion Model for Scalable and Controllable Scene Generation》 项目&#xff1a;https://zju3dv.github.io/StarGen 来源&#xff1a;商汤科技、浙大CAD、Tetras.AI 文章目录 摘要一、…

STM32 进阶 定时器

在stm32中定时器大概分为4类 1、系统定时器&#xff1a;属于arm内核&#xff0c;内嵌在NVIC中 2、高级定时器&#xff1a;可以用来刹车和死区 3、通用定时器&#xff1a;可以用来输出pwm方波 4、基本定时器&#xff1a;只能记数 系统定时器注意&#xff1a; 1、系统定时器…

day21-API(算法,lambda,练习)

常见的七种查找算法&#xff1a; ​ 数据结构是数据存储的方式&#xff0c;算法是数据计算的方式。所以在开发中&#xff0c;算法和数据结构息息相关。今天的讲义中会涉及部分数据结构的专业名词&#xff0c;如果各位铁粉有疑惑&#xff0c;可以先看一下哥们后面录制的数据结构…

正则表达式梳理(基于python)

正则表达式&#xff08;regular expression&#xff09;是一种针对字符串匹配查找所定义的规则模式&#xff0c;独立于语言&#xff0c;但不同语言在实现上也会存在一些细微差别&#xff0c;下面基于python对常用的相关内容进行梳理。 文章目录 一、通用常识1.通配符ps.反义 2.…

Java多线程与高并发专题——为什么 Map 桶中超过 8 个才转为红黑树?

引入 JDK 1.8 的 HashMap 和 ConcurrentHashMap 都有这样一个特点&#xff1a;最开始的 Map 是空的&#xff0c;因为里面没有任何元素&#xff0c;往里放元素时会计算 hash 值&#xff0c;计算之后&#xff0c;第 1 个 value 会首先占用一个桶&#xff08;也称为槽点&#xff…

Llama-Factory框架下的Meta-Llama-3-8B-Instruct模型微调

目录 引言 Llama - Factory 训练框架简介&#xff1a; Meta - Llama - 3 - 8B - Instruct 模型概述&#xff1a; Lora 方法原理及优势&#xff1a; 原理 优势 环境准备: 部署环境测试&#xff1a; 数据准备&#xff1a; 模型准备&#xff1a; 模型配置与训练&#xff1…

介绍一个能支持高带宽的EDID编辑软件

软件名称叫980 Manager 4.24.16&#xff0c;安装后的图标如下。   软件可以去此地址下载https://download.csdn.net/download/cjie221/90459603&#xff0c;下载后需解压&#xff0c;运行.msi文件安装。   安装后&#xff0c;打开软件&#xff0c;首先会弹出这个界面&…

2025年Cursor最新安装使用教程

Cursor安装教程 一、Cursor下载二、Cursor安装三、Cursor编辑器快捷键(1) 基础编辑快捷键(2) 导航快捷键(3) 其他常用快捷键 一、Cursor下载 Cursor官方网站&#xff08;https://www.cursor.com/ &#xff09; 根据自己电脑操作系统选择对应安装包 二、Cursor安装 下载完成后…

[内网安全] Windows 本地认证 — NTLM 哈希和 LM 哈希

关注这个专栏的其他相关笔记&#xff1a;[内网安全] 内网渗透 - 学习手册-CSDN博客 0x01&#xff1a;SAM 文件 & Windows 本地认证流程 0x0101&#xff1a;SAM 文件简介 Windows 本地账户的登录密码是存储在系统本地的 SAM 文件中的&#xff0c;在登录 Windows 的时候&am…

pt-archiver删除数据库的数据表/各种报错类型

这篇帖子是前面文的一部分延申 mysqlimport导入一亿数据的csv文件/一行命令删除表-CSDN博客 如需转载&#xff0c;标记出处 目录 pt-archiver命令格式 如果执行后出现下面报错 1&#xff09;Cannot find an ascendable index in table at /usr/bin/pt-archiver line 3233. …

开发环境搭建-06.后端环境搭建-前后端联调-Nginx反向代理和负载均衡概念

一.前后端联调 我们首先来思考一个问题 前端的请求地址是&#xff1a;http://localhost/api/employee/login 后端的接口地址是&#xff1a;http://localhost:8080/admin/employee/login 明明请求地址和接口地址不同&#xff0c;那么前端是如何请求到后端接口所响应回来的数…