Homogeneous Coordinates

Homogeneous Coordinates

Problem: Two parallel lines can intercept.

 
Railroad gets narrower and meets at horizon.

In Euclidean space (geometry), two parallel lines on the same plane cannot intercept, or cannot meet each other forever. It is a common sense that everyone is familiar with. 

However, it is not true any more in projective space, for example, the train railroad on the side picture becomes narrower while it moves far away from eyes. Finally, the two parallel rails meet at the horizon, which is a point at infinity. 

Euclidean space (or Cartesian space) describe our 2D/3D geometry so well, but they are not sufficient to handle the projective space (Actually, Euclidean geometry is a subset of projective geometry). The Cartesian coordinates of a 2D point can be expressed as (x, y)

What if this point goes far away to infinity? The point at infinity would be (∞,∞), and it becomes meaningless in Euclidean space. The parallel lines should meet at infinity in projective space, but cannot do in Euclidean space. Mathematicians have discoverd a way to solve this issue.

Solution: Homogeneous Coordinates

Homogeneous coordinates, introduced by August Ferdinand Möbius, make calculations of graphics and geometry possible in projective space. Homogeneous coordinates are a way of representing N-dimensional coordinates with N+1 numbers. 

To make 2D Homogeneous coordinates, we simply add an additional variable, w, into existing coordinates. Therefore, a point in Cartesian coordinates, (X, Y) becomes (x, y, w) in Homogeneous coordinates. And X and Y in Cartesian are re-expressed with x, y and w in Homogeneous as; 
X = x/w 
Y = y/w
 

For instance, a point in Cartesian (1, 2) becomes (1, 2, 1) in Homogeneous. If a point, (1, 2), moves toward infinity, it becomes (∞,∞) in Cartesian coordinates. And it becomes (1, 2, 0) in Homogeneous coordinates, because of (1/0, 2/0) = (∞,∞). Notice that we can express the point at infinity without using "∞".

Why is it called "homogeneous"?

As mentioned before, in order to convert from Homogeneous coordinates (x, y, w) to Cartesian coordinates, we simply divide x and y by w
 

Converting Homogeneous to Cartesian, we can find an important fact. Let's see the following example; 
 
As you can see, the points (1, 2, 3), (2, 4, 6) and (4, 8, 12) correspond to the same Euclidean point (1/3, 2/3). And any scalar product, (1a, 2a, 3a) is the same point as (1/3, 2/3) in Euclidean space. Therefore, these points are "homogeneous" because they represent the same point in Euclidean space (or Cartesian space). In other words, Homogeneous coordinates are scale invariant.

Proof: Two parallel lines can intercept.

Consider the following linear system in Euclidean space; 
 
And we know that there is no solution for above equations because of C ≠ D
If C = D, then two lines are identical (overlapped).

Let's rewrite the equations for projective space by replacing x and y to x/w, y/w respectively. 
 
Now, we have a solution, (x, y, 0) since (C - D)w = 0, ∴ w = 0. Therefore, two parallel lines meet at(x, y, 0), which is the point at infinity. 

Homogeneous coordinates are very useful and fundamental concept in computer graphics, such as projecting a 3D scene onto a 2D plane.

// CubeViewerView.h : interface of the CCubeViewerView class // #pragma once #include <math.h> const int A_rows = 4; const int A_cols = 4; // Define M_PI if not already defined #ifndef M_PI #define M_PI 3.14159265358979323846 #endif void multiplyMatrices(double A[A_rows][A_cols], double B[A_cols][8], double C[A_rows][8]) { // 初始化结果矩阵C为全0 for (int i = 0; i < A_rows; ++i) { for (int j = 0; j < 8; ++j) { // B的列数固定为8 C[i][j] = 0.0; } } // 矩阵相乘运算 for (int i = 0; i < A_rows; ++i) { // 遍历A的行 for (int j = 0; j < 8; ++j) { // 遍历B的列 for (int k = 0; k < A_cols; ++k) { // 遍历A的列/B的行 C[i][j] += A[i][k] * B[k][j]; } } } } class CCubeViewerView : public CView { protected: // create from serialization only CCubeViewerView() noexcept; DECLARE_DYNCREATE(CCubeViewerView) // Attributes public: CCubeViewerDoc* GetDocument() const; // Operations public: // Overrides public: virtual void OnDraw(CDC* pDC); // overridden to draw this view virtual BOOL PreCreateWindow(CREATESTRUCT& cs); protected: virtual BOOL OnPreparePrinting(CPrintInfo* pInfo); virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo); virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo); // Implementation public: virtual ~CCubeViewerView(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif protected: // Generated message map functions protected: afx_msg void OnFilePrintPreview(); afx_msg void OnRButtonUp(UINT nFlags, CPoint point); afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); afx_msg void OnSize(UINT nType, int cx, int cy); DECLARE_MESSAGE_MAP() private: void RenderScene(CDC* pDC); // Render the scene void ApplyObliqueProjection(double angle, double scale); double m_cubeVertices[8][4]; // Homogeneous coordinates of cube vertices double m_transformedVertices[8][4]; // Transformed vertices }; #ifndef _DEBUG // debug version in CubeViewerView.cpp inline CCubeViewerDoc* CCubeViewerView::GetDocument() const { return reinterpret_cast<CCubeViewerDoc*>(m_pDocument); } #endif // CubeViewerView.cpp : implementation of the CCubeViewerView class // #include "pch.h" #include "framework.h" // SHARED_HANDLERS can be defined in an ATL project implementing preview, thumbnail // handlers and property pages for shell items. This enables sharing of document code with that project. #ifndef SHARED_HANDLERS #include "CubeViewer.h" #endif #include "CubeViewerDoc.h" #include "CubeViewerView.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // CCubeViewerView IMPLEMENT_DYNCREATE(CCubeViewerView, CView) BEGIN_MESSAGE_MAP(CCubeViewerView, CView) ON_WM_CONTEXTMENU() ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CView::OnFilePrintPreview) ON_WM_RBUTTONUP() ON_WM_CREATE() ON_WM_SIZE() END_MESSAGE_MAP() // CCubeViewerView construction/destruction CCubeViewerView::CCubeViewerView() noexcept { // Define the vertices of the cube in homogeneous coordinates m_cubeVertices[0][0] = -1.0; m_cubeVertices[0][1] = -1.0; m_cubeVertices[0][2] = -1.0; m_cubeVertices[0][3] = 1.0; m_cubeVertices[1][0] = 1.0; m_cubeVertices[1][1] = -1.0; m_cubeVertices[1][2] = -1.0; m_cubeVertices[1][3] = 1.0; m_cubeVertices[2][0] = 1.0; m_cubeVertices[2][1] = 1.0; m_cubeVertices[2][2] = -1.0; m_cubeVertices[2][3] = 1.0; m_cubeVertices[3][0] = -1.0; m_cubeVertices[3][1] = 1.0; m_cubeVertices[3][2] = -1.0; m_cubeVertices[3][3] = 1.0; m_cubeVertices[4][0] = -1.0; m_cubeVertices[4][1] = -1.0; m_cubeVertices[4][2] = 1.0; m_cubeVertices[4][3] = 1.0; m_cubeVertices[5][0] = 1.0; m_cubeVertices[5][1] = -1.0; m_cubeVertices[5][2] = 1.0; m_cubeVertices[5][3] = 1.0; m_cubeVertices[6][0] = 1.0; m_cubeVertices[6][1] = 1.0; m_cubeVertices[6][2] = 1.0; m_cubeVertices[6][3] = 1.0; m_cubeVertices[7][0] = -1.0; m_cubeVertices[7][1] = 1.0; m_cubeVertices[7][2] = 1.0; m_cubeVertices[7][3] = 1.0; } CCubeViewerView::~CCubeViewerView() { } BOOL CCubeViewerView::PreCreateWindow(CREATESTRUCT& cs) { return CView::PreCreateWindow(cs); } // CCubeViewerView drawing void CCubeViewerView::OnDraw(CDC* pDC) { RenderScene(pDC); } // CCubeViewerView printing BOOL CCubeViewerView::OnPreparePrinting(CPrintInfo* pInfo) { // default preparation return DoPreparePrinting(pInfo); } void CCubeViewerView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: add extra initialization before printing } void CCubeViewerView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: add cleanup after printing } void CCubeViewerView::OnRButtonUp(UINT /* nFlags */, CPoint point) { ClientToScreen(&point); OnContextMenu(this, point); } void CCubeViewerView::RenderScene(CDC* pDC) { ApplyObliqueProjection(45.0, 0.5); // Apply oblique projection CPen pen(PS_SOLID, 2, RGB(255, 255, 255)); CPen* pOldPen = pDC->SelectObject(&pen); // Draw the transformed cube CPoint points[] = { CPoint(static_cast<int>(m_transformedVertices[0][0]), static_cast<int>(m_transformedVertices[0][1])), CPoint(static_cast<int>(m_transformedVertices[1][0]), static_cast<int>(m_transformedVertices[1][1])), CPoint(static_cast<int>(m_transformedVertices[2][0]), static_cast<int>(m_transformedVertices[2][1])), CPoint(static_cast<int>(m_transformedVertices[3][0]), static_cast<int>(m_transformedVertices[3][1])), CPoint(static_cast<int>(m_transformedVertices[4][0]), static_cast<int>(m_transformedVertices[4][1])), CPoint(static_cast<int>(m_transformedVertices[5][0]), static_cast<int>(m_transformedVertices[5][1])), CPoint(static_cast<int>(m_transformedVertices[6][0]), static_cast<int>(m_transformedVertices[6][1])), CPoint(static_cast<int>(m_transformedVertices[7][0]), static_cast<int>(m_transformedVertices[7][1])) }; pDC->MoveTo(points[0]); pDC->LineTo(points[1]); pDC->LineTo(points[2]); pDC->LineTo(points[3]); pDC->LineTo(points[0]); pDC->MoveTo(points[4]); pDC->LineTo(points[5]); pDC->LineTo(points[6]); pDC->LineTo(points[7]); pDC->LineTo(points[4]); pDC->MoveTo(points[0]); pDC->LineTo(points[4]); pDC->MoveTo(points[1]); pDC->LineTo(points[5]); pDC->MoveTo(points[2]); pDC->LineTo(points[6]); pDC->MoveTo(points[3]); pDC->LineTo(points[7]); pDC->SelectObject(pOldPen); } void CCubeViewerView::ApplyObliqueProjection(double angle, double scale) { double radians = angle * M_PI / 180.0; double P[4][4] = { {1, 0, 0, 0}, {0, 1, 0, 0}, {scale*cos(radians), scale*sin(radians), 1, 0}, {0, 0, 0, 1} }; double tempVertices[8][4]; // Execute matrix multiplication for (int i = 0; i < 8; ++i) { multiplyMatrices(P, m_cubeVertices[i], tempVertices[i]); } // Normalize homogeneous coordinates for (int i = 0; i < 8; ++i) { double w = tempVertices[i][3]; for (int j = 0; j < 4; ++j) { m_transformedVertices[i][j] = tempVertices[i][j] / w; } } } int CCubeViewerView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CView::OnCreate(lpCreateStruct) == -1) return -1; return 0; } void CCubeViewerView::OnSize(UINT nType, int cx, int cy) { CView::OnSize(nType, cx, cy); Invalidate(); // Redraw the view when resized } 严重性 代码 说明 项目 文件 行 禁止显示状态 错误 C2664 “void multiplyMatrices(double [][4],double [][8],double [][8])”: 无法将参数 2 从“double [4]”转换为“double [][8]” CubeViewer c:\users\hookwilling\source\repos\cubeviewer\cubeviewer\cubeviewerview.cpp 141 严重性 代码 说明 项目 文件 行 禁止显示状态 错误(活动) E0167 "double *" 类型的实参与 "double (*)[8]" 类型的形参不兼容 CubeViewer C:\Users\hookwilling\source\repos\CubeViewer\CubeViewer\CubeViewerView.cpp 148 严重性 代码 说明 项目 文件 行 禁止显示状态 错误(活动) E0167 "double *" 类型的实参与 "double (*)[8]" 类型的形参不兼容 CubeViewer C:\Users\hookwilling\source\repos\CubeViewer\CubeViewer\CubeViewerView.cpp 148
05-30
提示报错NXOpen::Point3d没有成员DistanceTo,。参考下下面的帮助文档,以下是参考文档:#ifndef NXOpen_UGMATH_HXX_INCLUDED #define NXOpen_UGMATH_HXX_INCLUDED //-------------------------------------------------------------------------- // Header for C++ interface to JA API //-------------------------------------------------------------------------- // // Source File: // ugmath.ja // // Generated by: // apiwrap // // WARNING: // This file is automatically generated - do not edit by hand // #ifdef _MSC_VER #pragma once #endif #include <NXOpen/NXDeprecation.hxx> #include <vector> #include <NXOpen/NXString.hxx> #include <NXOpen/Callback.hxx> #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4996) #endif #ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif namespace NXOpen { /** Represents three-dimensional cartesian coordinates for a point <br> Created in NX3.0.0. <br> */ struct Point3d { public: /** x */ double X; public: /** y */ double Y; public: /** z */ double Z; public: Point3d() : X(), Y(), Z() { } /** Constructor for the Point3d struct. */ public: Point3d(double xInitial /** x */ , double yInitial /** y */ , double zInitial /** z */ ) : X(xInitial), Y(yInitial), Z(zInitial) { } }; /** Represents a three-dimensional vector <br> Created in NX3.0.0. <br> */ struct Vector3d { public: /** x */ double X; public: /** y */ double Y; public: /** z */ double Z; public: Vector3d() : X(), Y(), Z() { } /** Constructor for the Vector3d struct. */ public: Vector3d(double xInitial /** x */ , double yInitial /** y */ , double zInitial /** z */ ) : X(xInitial), Y(yInitial), Z(zInitial) { } }; /** Represents a 3 x 3 matrix <br> Created in NX3.0.0. <br> */ struct Matrix3x3 { public: /** xx */ double Xx; public: /** xy */ double Xy; public: /** xz */ double Xz; public: /** yx */ double Yx; public: /** yy */ double Yy; public: /** yz */ double Yz; public: /** zx */ double Zx; public: /** zy */ double Zy; public: /** zz */ double Zz; public: Matrix3x3() : Xx(), Xy(), Xz(), Yx(), Yy(), Yz(), Zx(), Zy(), Zz() { } /** Constructor for the Matrix3x3 struct. */ public: Matrix3x3(double xxInitial /** xx */ , double xyInitial /** xy */ , double xzInitial /** xz */ , double yxInitial /** yx */ , double yyInitial /** yy */ , double yzInitial /** yz */ , double zxInitial /** zx */ , double zyInitial /** zy */ , double zzInitial /** zz */ ) : Xx(xxInitial), Xy(xyInitial), Xz(xzInitial), Yx(yxInitial), Yy(yyInitial), Yz(yzInitial), Zx(zxInitial), Zy(zyInitial), Zz(zzInitial) { } }; /** Represents two-dimensional cartesian coordinates for a point <br> Created in NX6.0.0. <br> */ struct Point2d { public: /** x */ double X; public: /** y */ double Y; public: Point2d() : X(), Y() { } /** Constructor for the Point2d struct. */ public: Point2d(double xInitial /** x */ , double yInitial /** y */ ) : X(xInitial), Y(yInitial) { } }; /** Represents four-dimensional homogeneous coordinates for a point. To convert to three-dimensional cartesian coordinates, divide x, y and z by w. <br> Created in NX8.5.0. <br> */ struct Point4d { public: /** x */ double X; public: /** y */ double Y; public: /** z */ double Z; public: /** weight */double W; public: Point4d() : X(), Y(), Z(), W() { } /** Constructor for the Point4d struct. */ public: Point4d(double xInitial /** x */ , double yInitial /** y */ , double zInitial /** z */ , double wInitial /** weight */) : X(xInitial), Y(yInitial), Z(zInitial), W(wInitial) { } }; } #ifdef _MSC_VER #pragma warning(pop) #endif #ifdef __GNUC__ #ifndef NX_NO_GCC_DEPRECATION_WARNINGS #pragma GCC diagnostic warning "-Wdeprecated-declarations" #endif #endif #endif
最新发布
08-09
关于图形 的二次图形消隐和相关文档 长方体的自隐藏线消隐上机指导 1. 实验目的与要求:掌握长方体的表面模型的建立;掌握三维图形的显示流程;掌握长方体自消隐的算法。 2. 实验步骤: (1)长方体表面模型的定义 三维齐次坐标结构的定义,面结构的定义,面结构中添加可见性属性;顶点表的定义,面表、与顶点表的关系。 (2)几何变换的实现 分别对顶点进行绕X轴旋转和绕Y轴旋转,旋转角度为参数,以实现轴测投影。 (3)消隐 计算每个面的外法向量,与视向量进行点积,给该面的可见性属性赋值。 (4)投影变换的实现 平行投影中正投影的投影变换公式及矩阵,要求以XOY平面为投影平面,Z轴正方向为视线方向。 (3)窗口-视区变换的实现 窗口大小的选取——一般将所有图形都取在窗口内;注意投影变换时投影平面的选取,投影平面上的坐标与视区坐标x,y的对应。 (4)图形显示 显示面表中的每一个面,对于不可见面用虚线绘制该面各边,对于可见面用实线绘制各边。 3. 具体任务 在已给出程序Draw3D2中,在视图类中分别添加绕X轴旋转和绕Y轴旋转的函数void RotateX(int angle)和void RotateY(int angle);在视图类中添加计算外法向量的函数HOMOCOORD GetN(HOMOCOORD p1, HOMOCOORD p2, HOMOCOORD p3),其返回值为外法向量。注意面结构中添加的可见性属性,注意显示图形时对于不可见和可见面的处理。 4. 说明 绕X轴的旋转变化的公式实现: 考虑到旋转变化不影响w分量,可得 其他变换类似。对顶点表每个顶点进行更新。 计算外法向量函数的: P1,p2,p3为面上逆时针依次相连的三个顶点,由此外法向量N=(p2-p1)×(p3-p2); 若令x1=p2.x-p1.x, y1=p2.y-p1.y, z1=p2.z-p1.z; x2=p3.x-p2.x, y2=p3.x-p2.x, z2=p3.x-p2.x; 则外法向量可以由下列行列式求出 即
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值