【图形学】投影和消隐简介

投影

正交投影

对于物体上任意一点的三维坐标P(x,y,z),投影后的三维坐标为 P ′ ( x ′ , y ′ , z ′ ) P^\prime(x^\prime,y^\prime,z^\prime) P(x,y,z),那么正交投影的方程为 { x ′ = x y ′ = y z ′ = 0 \begin{cases} x^\prime=x\\y^\prime=y\\z^\prime=0 \end{cases} x=xy=yz=0

斜投影

在这里插入图片描述

如图所示,空间中一点 P 1 ( x , y , z ) P_1(x,y,z) P1(x,y,z)在xOy面上的斜投影坐标 P 2 ( x ′ , y ′ , 0 ) P_2(x^\prime,y^\prime,0) P2(x,y,0)正交投影点 P 3 ( x , y , 0 ) P_3(x,y,0) P3(x,y,0),那么有 { x ′ = x − L cos ⁡ β = x − z cot ⁡ α cos ⁡ β y ′ = y − L sin ⁡ β = y − z cot ⁡ α sin ⁡ β \begin{cases} x^\prime=x-L\cos\beta=x-z\cot\alpha\cos\beta\\ y^\prime=y-L\sin\beta=y-z\cot\alpha\sin\beta \end{cases} {x=xLcosβ=xzcotαcosβy=yLsinβ=yzcotαsinβ

透视投影

透视投影有三个坐标系,世界坐标系,观察坐标系,屏幕坐标系
在这里插入图片描述

在这里插入图片描述

观察坐标系到投影坐标系的转换可以用相似求出
在这里插入图片描述

{ x ′ = n ⋅ x z y ′ = n ⋅ y z \begin{cases} x^\prime=n\cdot \frac{x}{z}\\ y^\prime=n\cdot \frac{y}{z} \end{cases} {x=nzxy=nzy

建立投影类

class CProjection
{
public:CProjection();~CProjection();void SetViewPoint(double R);CPoint3 GetViewPoint();CPoint2 ObliqueProjection(CPoint3 WorldPoint);//正交投影CPoint2 OrthogonalProjection(CPoint3 WorldPoint);//斜二测投影CPoint2 PerspectiveProjection(CPoint3 WorldPoint);//透视投影
private:CPoint3 m_viewPoint;double R, d;
};CProjection::CProjection()
{R = 1200; d = 800;m_viewPoint.m_x = 0;m_viewPoint.m_y = 0;m_viewPoint.m_z = R;
}CProjection::~CProjection()
{
}void CProjection::SetViewPoint(double R)
{this->R = R;
}CPoint3 CProjection::GetViewPoint()
{return m_viewPoint;
}CPoint2 CProjection::ObliqueProjection(CPoint3 WorldPoint)//斜二测投影
{CPoint2 ScreenPoint;ScreenPoint.m_x = WorldPoint.m_x - 0.3536 * WorldPoint.m_z;ScreenPoint.m_y = WorldPoint.m_y - 0.3536 * WorldPoint.m_z;return ScreenPoint;
}CPoint2 CProjection::OrthogonalProjection(CPoint3 WorldPoint)//正交投影
{CPoint2 ScreenPoint;ScreenPoint.m_x = WorldPoint.m_x ;ScreenPoint.m_y = WorldPoint.m_y ;return ScreenPoint;
}CPoint2 CProjection::PerspectiveProjection(CPoint3 WorldPoint)
{CPoint2 ScreenPoint;CPoint3 ViewPoint;ViewPoint.m_x = WorldPoint.m_x;ViewPoint.m_y = WorldPoint.m_y;ViewPoint.m_z = m_viewPoint.m_z - WorldPoint.m_z;ScreenPoint.m_x = d * ViewPoint.m_x / ViewPoint.m_z;ScreenPoint.m_y = d * ViewPoint.m_y / ViewPoint.m_z;return ScreenPoint;
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

消隐,背面剔除算法

给定视点位置或视线方向后,确定场景中哪些物体表面是可见的、哪些物体表面是不可见的,即是消隐。
背面剔除算法主要是针对凸多面体,其表面要么可见,要么不可见。算法要给出测试每个表面是否可见的表达式。
在这里插入图片描述
如图所示,根据表面的外法向量 N ⃗ \vec N N 和式向量 V ⃗ \vec V V 的夹角 θ \theta θ来进行可见性判定。
N ⃗ = P 2 P 4 ⃗ × P 3 P 4 ⃗ \vec N=\vec {P_2P_4} \times \vec {P_3P_4} N =P2P4 ×P3P4
给定视点坐标 O ( x v , y v , z v ) O(x_v,y_v,z_v) O(xv,yv,zv)后,视向量表示为 V ⃗ = ( x v − x 4 , y v − y 4 , z v − z 4 ) \vec V=(x_v-x_4,y_v-y_4,z_v-z_4) V =(xvx4,yvy4,zvz4)
将法向量 N ⃗ \vec N N 归一化为单位向量 n ⃗ \vec n n ,将视向量归一化为单位向量 v ⃗ \vec v v ,则有
n ⃗ ⋅ v ⃗ = cos ⁡ θ \vec n \cdot \vec v=\cos\theta n v =cosθ
可见性的判定如下:

  • cos ⁡ θ > 0 \cos\theta>0 cosθ>0时,表面可见,绘制多边形的边界线
  • cos ⁡ θ = 0 \cos\theta=0 cosθ=0时,表面多边形退化为一条直线
  • cos ⁡ θ < 0 \cos\theta<0 cosθ<0时,表面多边形不可见
    为了实现背面剔除算法,这里设计一个三维向量类CVector3

这里给的例子是一个立方体,那如果是一个贝塞尔曲面拟合的物体(比如球),那么用递归曲面片的方式绘制曲面,对于每一个细分曲面片又可以看成一个平面四边形,求出法向量即可。
在这里插入图片描述

三维向量类CVector3

class CVector3
{
public:CVector3();virtual ~CVector3();CVector3(double x, double y, double z);//绝对向量CVector3(const CPoint3& p);CVector3(const CPoint3& p0, const CPoint3& p1);//相对向量double Magnitude();//计算向量的模CVector3 Normalize();//归一化向量friend CVector3 operator + (const CVector3& v0, const CVector3& v1);//运算符重载friend CVector3 operator - (const CVector3& v0, const CVector3& v1);friend CVector3 operator * (const CVector3& v, double t);friend CVector3 operator * (double t, const CVector3& v);friend CVector3 operator / (const CVector3& v, double t);friend double DotProduct(const CVector3& v0, const CVector3& v1);//计算向量的点积friend CVector3 CrossProduct(const CVector3& v0, const CVector3& v1);//计算向量的叉积
private:double m_x, m_y, m_z;
};
CVector3::CVector3(void)
{m_x = 0.0, m_y = 0.0, m_z = 1.0;//指向z轴正向
}CVector3::~CVector3(void)
{
}CVector3::CVector3(double x, double y, double z)//绝对向量
{m_x = x;m_y = y;m_z = z;
}
CVector3::CVector3(const CPoint3& p)
{m_x = p.m_x;m_y = p.m_y;m_z = p.m_z;
}
CVector3::CVector3(const CPoint3& p0, const CPoint3& p1)//相对向量
{m_x = p1.m_x - p0.m_x;m_y = p1.m_y - p0.m_y;m_z = p1.m_z - p0.m_z;
}
double CVector3::Magnitude(void)//向量的模
{return sqrt(m_x * m_x + m_y * m_y + m_z * m_z);
}
CVector3 CVector3::Normalize(void)//归一化为单位向量
{CVector3 vector;double magnitude = sqrt(m_x * m_x + m_y * m_y + m_z * m_z);if (fabs(magnitude) < 1e-6)magnitude = 1.0;vector.m_x = m_x / magnitude;vector.m_y = m_y / magnitude;vector.m_z = m_z / magnitude;return vector;
}
void CVector3::IntoOut()
{if (m_x * m_y < 0&&m_x*m_z>0) {m_x = -m_x, m_z = -m_z;}else if (m_x * m_z < 0 && m_x * m_y>0) {m_x = -m_x, m_y = -m_y;}else if (m_y * m_z < 0 && m_y * m_z>0) {m_y = -m_y, m_z = -m_z;}
}
CVector3 operator + (const CVector3& v0, const CVector3& v1)//向量的和
{CVector3 vector;vector.m_x = v0.m_x + v1.m_x;vector.m_y = v0.m_y + v1.m_y;vector.m_z = v0.m_z + v1.m_z;return vector;
}CVector3 operator - (const CVector3& v0, const CVector3& v1)//向量的差
{CVector3 vector;vector.m_x = v0.m_x - v1.m_x;vector.m_y = v0.m_y - v1.m_y;vector.m_z = v0.m_z - v1.m_z;return vector;
}CVector3 operator * (const CVector3& v, double t)//向量与常量的积
{CVector3 vector;vector.m_x = v.m_x * t;vector.m_y = v.m_y * t;vector.m_z = v.m_z * t;return vector;
}CVector3 operator * (double t, const CVector3& v)//常量与向量的积
{CVector3 vector;vector.m_x = v.m_x * t;vector.m_y = v.m_y * t;vector.m_z = v.m_z * t;return vector;
}CVector3 operator / (const CVector3& v, double scalar)//向量数除
{if (fabs(scalar) < 1e-6)scalar = 1.0;CVector3 vector;vector.m_x = v.m_x / scalar;vector.m_y = v.m_y / scalar;vector.m_z = v.m_z / scalar;return vector;
}double DotProduct(const CVector3& v0, const CVector3& v1)//向量的点积
{return(v0.m_x * v1.m_x + v0.m_y * v1.m_y + v0.m_z * v1.m_z);
}CVector3 CrossProduct(const CVector3& v0, const CVector3& v1)//向量的叉积
{CVector3 vector;vector.m_x = v0.m_y * v1.m_z - v0.m_z * v1.m_y;vector.m_y = v0.m_z * v1.m_x - v0.m_x * v1.m_z;vector.m_z = v0.m_x * v1.m_y - v0.m_y * v1.m_x;return vector;
}

示例,球的消隐

在这里插入图片描述

需要项目代码请评论区留言或私信

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

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

相关文章

做跨境电商需要使用住宅代理IP吗?

住宅代理IP是近年来跨境电商领域日益受到重视的技术工具&#xff0c;不仅可以保护隐私、优化网络速度&#xff0c;还能助推跨境电商的精细化管理。接下来&#xff0c;我们将深入探讨利用住宅代理IP如何为跨境电商业务带来竞争优势。 一、住宅代理IP与跨境电商 住宅代理IP&…

flex:1最后一行或者只有一行时不想占满整行

flex:1最后一行或者只有一行时不想占满整行 问题解决方法注意 问题 在设置flex1的时候&#xff0c;如果内容只有一行或者多行的最后一行的时候&#xff0c;往往最后一行会占满整行&#xff0c;导致项目比其他行宽&#xff0c;比如&#xff1a; 解决方法 这时候我们可以使用…

2.3_9 吸烟者问题

2.3_9 吸烟者问题 问题描述 问题分析 假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它&#xff0c;但是要卷起并抽掉一支烟&#xff0c;抽烟者需要有三种材料&#xff1a;烟草、纸和胶水。三个抽烟者中&#xff0c;第一个拥有烟草、第二个拥有纸…

BlueLotus 下载安装使用

说明 蓝莲花平台BlueLotus&#xff0c;是清华大学曾经的蓝莲花战队搭建的平台&#xff0c;该平台用于接收xss返回数据。 正常执行反射型xss和存储型xss&#xff1a; 反射型在执行poc时&#xff0c;会直接在页面弹出执行注入的poc代码&#xff1b;存储型则是在将poc代码注入用…

【Docker】Docker Image(镜像)

文章目录 一、Docker镜像是什么&#xff1f;二、镜像生活案例三、为什么需要镜像四、镜像命令详解docker rmidocker savedocker loaddocker historydocker image prune 五、镜像操作案例六、镜像综合实战实战一、离线迁移镜像实战二、镜像存储的压缩与共享 一、Docker镜像是什么…

孙思邈中文医疗大模型

孙思邈, 唐代医药学家、道士, 被后人尊称为"药王". 其十分重视民间的医疗经验, 不断积累走访, 及时记录下来, 写下著作《千金要方》. 唐朝建立后, 孙思邈接受朝廷的邀请, 与政府合作开展医学活动, 完成了世界上第一部国家药典《唐新本草》. 孙思邈中医药大模型(简称:…

【C语言】位与移位操作符详解

目录 1.⼆进制和进制转换 ①十进制&#xff1a;生活中最常用 ②二进制&#xff1a;计算机中使用的&#xff0c;每个数字称为一个比特 ③八进制、十六进制也如上 ④二进制转十进制 ⑤十进制转二进制 ⑥二进制转八进制 ⑦二进制转十六进制 2.原码、反码、补码 3.移位操…

【C++航海王:追寻罗杰的编程之路】类与对象你学会了吗?(下)

目录 1 -> 再谈构造函数1.1 -> 构造函数体赋值1.2 -> 初始化列表1.3 -> explicit关键字 2 -> static成员2.1 -> 概念2.2 -> 特性 3 -> 友元3.1 -> 友元函数3.2 -> 友元类 4 -> 内部类5 -> 匿名对象6 -> 拷贝对象时的一些编译器优化 1 -…

[N-141]基于springboot,vue网上拍卖平台

开发工具&#xff1a;IDEA 服务器&#xff1a;Tomcat9.0&#xff0c; jdk1.8 项目构建&#xff1a;maven 数据库&#xff1a;mysql5.7 系统分前后台&#xff0c;项目采用前后端分离 前端技术&#xff1a;vueelementUI 服务端技术&#xff1a;springbootmybatis-plusredi…

.NET Core 实现 JWT 认证

写在前面 JWT&#xff08;JSON Web Token&#xff09;是一种开放标准, 由三部分组成&#xff0c;分别是Header、Payload和Signature&#xff0c;它以 JSON 对象的方式在各方之间安全地传输信息。通俗的说&#xff0c;就是通过数字签名算法生产一个字符串&#xff0c;然后在网络…

RFID手持终端_智能pda手持终端设备定制方案

手持终端是一款多功能、适用范围广泛的安卓产品&#xff0c;具有高性能、大容量存储、高端扫描头和全网通数据连接能力。它能够快速平稳地运行&#xff0c;并提供稳定的连接表现和快速的响应时&#xff0c;适用于医院、物流运输、零售配送、资产盘点等苛刻的环境。通过快速采集…

ROS笔记二:launch

目录 launch node标签 参数 参数服务器 节点分组 launch launch文件是一种可以可实现多节点启动和参数配置的xml文件,launch文件用于启动和配置ROS节点、参数和其他相关组件。launch文件通常使用XML格式编写&#xff0c;其主要目的是方便地启动ROS节点和设置节点之间的连…