绘制 2D 地图坐标到 OpenGL 3D 投影
Plotting 2D map coordinates to OpenGL 3D projection
我正在尝试将在我的地图编辑器中创建的 2D 地图转换为使用 OpenGL 绘制的 3D 地图。这是我在地图编辑器中生成的地图:
这些顶点是相对于我的笛卡尔原点世界坐标(图片顶部),我正在应用此公式将其转换为 OpenGL 对象坐标:
世界大小:800x600
x = (X / 800) -0.5
y = (Y / 600) -0.5
得到这个结果:
(第一个对象面)
−0.48625, 0.068333333
0.12625, 0.07
0.12875, −0.481666667
−0.4875, −0.486666667
在 OpenGL 中绘制这个顶点缓冲区,我得到了一个非常奇怪的结果。那么如何从这些顶点位置获得 3D 模型呢?喜欢这张照片:
我正在三角形模式下渲染 OpenGL 并使用此示例作为起点:https://github.com/JoeyDeVries/LearnOpenGL/blob/master/src/1.getting_started/7.4.camera_class/camera_class.cpp
使用转换公式 + Earcut 曲面细分 (https://github.com/mapbox/earcut.hpp),我终于在 OpenGL 中正确渲染了这个矩形。两个平面只有 Z 轴不同,现在的问题是如何渲染它的横向,因为 Earcut 只适用于 2D 坐标...
很难确定,但您的对象似乎只渲染了两个面孔,因为您没有将其他面孔添加到索引中。
因为你有顶点,但你还需要告诉边有一个三角形。如果它们是三角形,您最终应该绘制 16 个三角形。
如果不使用索引,则需要为每个三角形复制顶点并最终绘制 48 个顶点。
为了让 earing 算法在 3D 中工作,如果您确定多边形的所有点都在同一个平面中,您可以取 3 个顶点,推导出它的平面,并创建一个变换矩阵以带来所有这些都指向 (x,y,0),这就像二维坐标。
如果我做对了,你得到了一些平面 2D 多边形以及向其添加一些恒定厚度的东西(作为 3D 网格)。这很容易做到。正如您正确假设的那样,您需要先进行三角测量。所以你应该有这个输入:
table 点 pnt[pnts]
对象所有点的列表。
多边形pol[pols]
(对象的周长)
刚刚排序的点索引列表引用了 table 个点
三角剖分结果fac[facs]
表示所有三角形的 3 点索引的有序列表。
现在要从中制作网格,我们需要这样做:
复制所有点并通过一些平移挤出它们。
所有这些新点都将添加到当前 pnt[pnts]
table。不要忘记记住原始 table 尺寸 pnts0
,因为稍后需要它。
copy/reverse三角剖分.
三角多边形的对边将是相同的,只是在反向多边形缠绕中。所以只需将它复制到 fac[facs]
作为反向索引顺序的新三角形...不要忘记将原始点 table 大小添加到所有新面孔。这将使用新点...从您的图像中您已经达到了这一点。
创建缺失的侧面。
为此我们可以利用原始多边形。因为我们刚刚复制了点,所以我们知道 pnt[3*i]
与 pnt[pnts0+3*i]
相反。所以我们只创建连接多边形相对边的三角形面。
这是我现在为此破坏的小 C++ 示例:
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "Unit1.h"
#include "gl_simple.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
const int N=128;
int pnts=6*3; // 3* number of points
float pnt[N]= // x,y per each point
{
-0.5,-0.5,0.0, // 6 ------ 9
-0.4, 0.0,0.0, // + +
-0.5,+0.5,0.0, // 3 12
+0.5,+0.5,0.0, // + +
+0.4, 0.0,0.0, // 0 ----- 15
+0.5,-0.5,0.0,
};
int pol[N]={ 0,3,6,9,12,15 }, pols=6; // original polygon (3*pnt index), number of its vertexes
int fac[N]= // triangulation result (3*pnt index)
{
0,3,15,
3,12,15,
3,6,12,
6,9,12,
}, facs=4*3; // number of triangles*3
//---------------------------------------------------------------------------
void extrude(float dz)
{
int i,i0,pnts0=pnts;
// copy and reverse triangulation
for (i=0;i<facs;i++)
fac[facs+facs-1-i]=fac[i]+pnts; facs+=facs;
// duplicate points
for (i=0;i<pnts;i++) pnt[pnts0+i]=pnt[i]; pnts+=pnts;
// extrude points
for (i= 2;i<pnts0;i+=3) pnt[i]-=dz;
for ( ;i<pnts ;i+=3) pnt[i]+=dz;
// side faces
for (i0=pols-1,i=0;i<pols;i0=i,i++)
{
fac[facs]=pol[i ]+pnts0; facs++;
fac[facs]=pol[i ]; facs++;
fac[facs]=pol[i0]; facs++;
fac[facs]=pol[i0]+pnts0; facs++;
fac[facs]=pol[i ]+pnts0; facs++;
fac[facs]=pol[i0]; facs++;
}
}
//---------------------------------------------------------------------------
void gl_draw()
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glDisable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glEnable(GL_COLOR_MATERIAL);
/*
glPolygonMode(GL_FRONT,GL_FILL);
glPolygonMode(GL_BACK,GL_LINE);
glDisable(GL_CULL_FACE);
*/
// set view
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0,0.0,-5.0);
static float ang=0.0;
glRotatef(ang,0.2,0.7,0.1); ang+=5.0; if (ang>=360.0) ang-=360.0;
// render mesh
float *p0,*p1,*p2,n[3],a[3],b[3],c;
glColor3f(0.7,0.7,0.7);
glBegin(GL_TRIANGLES);
for (int i=0;i+3<=facs;i+=3)
{
// points
p0=pnt+fac[i+0];
p1=pnt+fac[i+1];
p2=pnt+fac[i+2];
// compute normal
a[0]=p1[0]-p0[0]; a[1]=p1[1]-p0[1]; a[2]=p1[2]-p0[2];
b[0]=p2[0]-p1[0]; b[1]=p2[1]-p1[1]; b[2]=p2[2]-p1[2];
n[0]=(a[1]*b[2])-(a[2]*b[1]);
n[1]=(a[2]*b[0])-(a[0]*b[2]);
n[2]=(a[0]*b[1])-(a[1]*b[0]);
c=1.0/sqrt((n[0]*n[0])+(n[1]*n[1])+(n[2]*n[2]));
n[0]*=c; n[1]*=c; n[2]*=c;
// render
glNormal3fv(n);
glVertex3fv(p0);
glVertex3fv(p1);
glVertex3fv(p2);
}
glEnd();
// glFlush();
glFinish();
SwapBuffers(hdc);
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
// Init of program
gl_init(Handle); // init OpenGL
extrude(0.2);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
// Exit of program
gl_exit();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
// repaint
gl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
// resize
gl_resize(ClientWidth,ClientHeight);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::tim_redrawTimer(TObject *Sender)
{
gl_draw();
}
//---------------------------------------------------------------------------
它是基于 VCL 所以忽略所有 VCL 的东西并移植你 want/need 和 [=66= 的事件]GL context 符合您的编程风格。这里唯一重要的东西是:
tables pnt,fac,pol
保存输入,后者也输出。 extrude(dz)
将创建网格(只调用一次!),gl_draw 将 table 渲染为网格(使用旧样式 GL api 为简单起见)。
对于 GL 的东西,我使用了我的 gl_simple.h
,你可以在这个相关的 QA 中找到它:
下面是上面代码的预览:
断断续续是由于我的 GIF 捕捉渲染很流畅。我使用静态分配和运行 正常计算所以代码简单易懂。对于真正的交易来说,你需要实现动态列表和 VAO/VBO ... 如果你想要良好的性能
我正在尝试将在我的地图编辑器中创建的 2D 地图转换为使用 OpenGL 绘制的 3D 地图。这是我在地图编辑器中生成的地图:
这些顶点是相对于我的笛卡尔原点世界坐标(图片顶部),我正在应用此公式将其转换为 OpenGL 对象坐标:
世界大小:800x600
x = (X / 800) -0.5
y = (Y / 600) -0.5
得到这个结果:
(第一个对象面)
−0.48625, 0.068333333
0.12625, 0.07
0.12875, −0.481666667
−0.4875, −0.486666667
在 OpenGL 中绘制这个顶点缓冲区,我得到了一个非常奇怪的结果。那么如何从这些顶点位置获得 3D 模型呢?喜欢这张照片:
我正在三角形模式下渲染 OpenGL 并使用此示例作为起点:https://github.com/JoeyDeVries/LearnOpenGL/blob/master/src/1.getting_started/7.4.camera_class/camera_class.cpp
使用转换公式 + Earcut 曲面细分 (https://github.com/mapbox/earcut.hpp),我终于在 OpenGL 中正确渲染了这个矩形。两个平面只有 Z 轴不同,现在的问题是如何渲染它的横向,因为 Earcut 只适用于 2D 坐标...
很难确定,但您的对象似乎只渲染了两个面孔,因为您没有将其他面孔添加到索引中。
因为你有顶点,但你还需要告诉边有一个三角形。如果它们是三角形,您最终应该绘制 16 个三角形。 如果不使用索引,则需要为每个三角形复制顶点并最终绘制 48 个顶点。
为了让 earing 算法在 3D 中工作,如果您确定多边形的所有点都在同一个平面中,您可以取 3 个顶点,推导出它的平面,并创建一个变换矩阵以带来所有这些都指向 (x,y,0),这就像二维坐标。
如果我做对了,你得到了一些平面 2D 多边形以及向其添加一些恒定厚度的东西(作为 3D 网格)。这很容易做到。正如您正确假设的那样,您需要先进行三角测量。所以你应该有这个输入:
table 点
pnt[pnts]
对象所有点的列表。
多边形
pol[pols]
(对象的周长)刚刚排序的点索引列表引用了 table 个点
三角剖分结果
fac[facs]
表示所有三角形的 3 点索引的有序列表。
现在要从中制作网格,我们需要这样做:
复制所有点并通过一些平移挤出它们。
所有这些新点都将添加到当前
pnt[pnts]
table。不要忘记记住原始 table 尺寸pnts0
,因为稍后需要它。copy/reverse三角剖分.
三角多边形的对边将是相同的,只是在反向多边形缠绕中。所以只需将它复制到
fac[facs]
作为反向索引顺序的新三角形...不要忘记将原始点 table 大小添加到所有新面孔。这将使用新点...从您的图像中您已经达到了这一点。创建缺失的侧面。
为此我们可以利用原始多边形。因为我们刚刚复制了点,所以我们知道
pnt[3*i]
与pnt[pnts0+3*i]
相反。所以我们只创建连接多边形相对边的三角形面。
这是我现在为此破坏的小 C++ 示例:
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "Unit1.h"
#include "gl_simple.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
const int N=128;
int pnts=6*3; // 3* number of points
float pnt[N]= // x,y per each point
{
-0.5,-0.5,0.0, // 6 ------ 9
-0.4, 0.0,0.0, // + +
-0.5,+0.5,0.0, // 3 12
+0.5,+0.5,0.0, // + +
+0.4, 0.0,0.0, // 0 ----- 15
+0.5,-0.5,0.0,
};
int pol[N]={ 0,3,6,9,12,15 }, pols=6; // original polygon (3*pnt index), number of its vertexes
int fac[N]= // triangulation result (3*pnt index)
{
0,3,15,
3,12,15,
3,6,12,
6,9,12,
}, facs=4*3; // number of triangles*3
//---------------------------------------------------------------------------
void extrude(float dz)
{
int i,i0,pnts0=pnts;
// copy and reverse triangulation
for (i=0;i<facs;i++)
fac[facs+facs-1-i]=fac[i]+pnts; facs+=facs;
// duplicate points
for (i=0;i<pnts;i++) pnt[pnts0+i]=pnt[i]; pnts+=pnts;
// extrude points
for (i= 2;i<pnts0;i+=3) pnt[i]-=dz;
for ( ;i<pnts ;i+=3) pnt[i]+=dz;
// side faces
for (i0=pols-1,i=0;i<pols;i0=i,i++)
{
fac[facs]=pol[i ]+pnts0; facs++;
fac[facs]=pol[i ]; facs++;
fac[facs]=pol[i0]; facs++;
fac[facs]=pol[i0]+pnts0; facs++;
fac[facs]=pol[i ]+pnts0; facs++;
fac[facs]=pol[i0]; facs++;
}
}
//---------------------------------------------------------------------------
void gl_draw()
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glDisable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glEnable(GL_COLOR_MATERIAL);
/*
glPolygonMode(GL_FRONT,GL_FILL);
glPolygonMode(GL_BACK,GL_LINE);
glDisable(GL_CULL_FACE);
*/
// set view
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0,0.0,-5.0);
static float ang=0.0;
glRotatef(ang,0.2,0.7,0.1); ang+=5.0; if (ang>=360.0) ang-=360.0;
// render mesh
float *p0,*p1,*p2,n[3],a[3],b[3],c;
glColor3f(0.7,0.7,0.7);
glBegin(GL_TRIANGLES);
for (int i=0;i+3<=facs;i+=3)
{
// points
p0=pnt+fac[i+0];
p1=pnt+fac[i+1];
p2=pnt+fac[i+2];
// compute normal
a[0]=p1[0]-p0[0]; a[1]=p1[1]-p0[1]; a[2]=p1[2]-p0[2];
b[0]=p2[0]-p1[0]; b[1]=p2[1]-p1[1]; b[2]=p2[2]-p1[2];
n[0]=(a[1]*b[2])-(a[2]*b[1]);
n[1]=(a[2]*b[0])-(a[0]*b[2]);
n[2]=(a[0]*b[1])-(a[1]*b[0]);
c=1.0/sqrt((n[0]*n[0])+(n[1]*n[1])+(n[2]*n[2]));
n[0]*=c; n[1]*=c; n[2]*=c;
// render
glNormal3fv(n);
glVertex3fv(p0);
glVertex3fv(p1);
glVertex3fv(p2);
}
glEnd();
// glFlush();
glFinish();
SwapBuffers(hdc);
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
// Init of program
gl_init(Handle); // init OpenGL
extrude(0.2);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
// Exit of program
gl_exit();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
// repaint
gl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
// resize
gl_resize(ClientWidth,ClientHeight);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::tim_redrawTimer(TObject *Sender)
{
gl_draw();
}
//---------------------------------------------------------------------------
它是基于 VCL 所以忽略所有 VCL 的东西并移植你 want/need 和 [=66= 的事件]GL context 符合您的编程风格。这里唯一重要的东西是:
tables pnt,fac,pol
保存输入,后者也输出。 extrude(dz)
将创建网格(只调用一次!),gl_draw 将 table 渲染为网格(使用旧样式 GL api 为简单起见)。
对于 GL 的东西,我使用了我的 gl_simple.h
,你可以在这个相关的 QA 中找到它:
下面是上面代码的预览:
断断续续是由于我的 GIF 捕捉渲染很流畅。我使用静态分配和运行 正常计算所以代码简单易懂。对于真正的交易来说,你需要实现动态列表和 VAO/VBO ... 如果你想要良好的性能