WebGL - 在平面上显示球体
WebGL - display a sphere on a plane
我想像在球体上一样显示图像 - 但在平面上。
此操作的一个示例是 Mercatore 投影,即来自地球的地球地图 "unrolled"。
为了更好地解释我自己,在一个球体上有一个方形纹理——不是在整个球体上,而是在它的一部分上——我想在一个平面上展示在球体上看到这个纹理的结果。
我已经找到了这个:
How do I 'wrap' a plane over a sphere with three.js?
但我想用着色器来做,因为它可能是最有效的,但也可能是最困难的。我很难找到合适的公式。有什么数学框架吗?
您应该指定您真正想要的投影。曲面有很多方法(不仅仅是球体)。你的问题是这种变换的逆,所以首先是直接投影(平面 - >球面)。我用这两个(都用于特定目的):
距球面中心的距离与平面上的距离相匹配
这用于校正曲面上的纹理,例如眼镜上的装饰品等...
球体上到视轴的垂直距离与平面上的距离相匹配
因此,如果您从视轴看,您会在球体和平面上看到相同的图像,只需设置坐标系即可,因此 Z
轴是观察方向,x,y
轴是对应于您的二维平面轴。然后只需计算 z 坐标以匹配球体表面
我想你想要第一个选项
因此计算中间点 (x0,y0)
作为边界框的中心或均匀分布的点平均点。通过 atan2
以弧度计算每个点和坐标(从中点开始)的 ang
!!!
然后计算 dx,dy
并将二维坐标计算为 (x,y)=(x0+dx,y0+dy)
这里是结果示例(我将其用于任何类型的曲率):
[备注]
还有另一种基于光线投射的方法,可能还有更多...
[edit1] C++ 示例
为您破解小型 C++ class:
//---------------------------------------------------------------------------
#include <Math.h>
class sphere_projection
{
public:
float x0,y0,z0,r0; // 3D sphere
float u0,v0; // mid point of 2D image
float m; // scale 2D image
int mode; // which projection type
sphere_projection()
{
x0=0.0; y0=0.0; z0=0.0; r0=1.0;
u0=0.0; v0=0.0; m=1.0;
mode=1;
}
void uv2xyz(float &x,float &y,float &z,float u,float v)
{
if (mode==1)
{
float a,b;
// 2D position scaled around midpoint and converted from arclength to angle
u=(u-u0)*m/r0;
v=(v-v0)*m/r0;
// correct on radius distrotion in both axises
a=u/cos(v);
b=v/cos(u);
// compute the 3D cartesian point on surface
z=z0+(r0*cos(b)*cos(a));
x=x0+(r0*cos(b)*sin(a));
y=y0+(r0*sin(b));
}
if (mode==2)
{
// 2D position scaled around midpoint
x=(u-u0)*m;
y=(v-v0)*m;
// compute the 3D cartesian point on surface
x=x0+x;
y=y0+y;
z=z0+sqrt(r0*r0-x*x-y*y);
}
}
void uv2xy (float &x,float &y, float u,float v)
{
if (mode==1)
{
float a,b,z;
// 2D position scaled around midpoint and converted from arclength to angle
a=(u-u0)*m/r0;
b=(v-v0)*m/r0;
// correct on radius distrotion in both axises and convert back to 2D position
x=u0+(a*r0/(m*cos(b)));
y=v0+(b*r0/(m*cos(a)));
}
if (mode==2)
{
float z;
// 2D position scaled around midpoint + Z axis
x=(u-u0)*m;
y=(v-v0)*m;
z=sqrt(r0*r0-x*x-y*y);
// compute arclengths and convert back to 2D position
x=u0+(r0*atan2(x,z)/m);
y=v0+(r0*atan2(y,z)/m);
}
}
};
//---------------------------------------------------------------------------
这是如何使用它(在 OpenGL 中渲染):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(0.0,+2.5,-20.0);
static float ang=0.0; ang+=2.5;
float x,y,z,u,v,d=0.2;
sphere_projection sp;
sp.x0=0.0;
sp.y0=0.0;
sp.z0=0.0;
sp.r0=1.5;
sp.u0=0.0;
sp.v0=0.0;
sp.m =0.5;
for (sp.mode=1;sp.mode<=2;sp.mode++)
{
// original 2D grid
glMatrixMode(GL_MODELVIEW);
glTranslatef(-5.0,0.0,0.0);
glColor3f(1.0f, 1.0f, 1.0f);
for (u=d-1.0;u<=1.0;u+=d)
for (v=d-1.0;v<=1.0;v+=d)
{
glBegin(GL_LINE_LOOP);
glVertex3f(u-d,v-d,0.0);
glVertex3f(u-d,v ,0.0);
glVertex3f(u ,v ,0.0);
glVertex3f(u ,v-d,0.0);
glEnd();
}
// sphere mapped corrected
glMatrixMode(GL_MODELVIEW);
glTranslatef(+5.0,0.0,0.0);
glPushMatrix();
glRotatef(ang,0.0,1.0,0.0);
glColor3f(1.0f, 0.0f, 0.0f);
for (u=d-1.0;u<=1.0;u+=d)
for (v=d-1.0;v<=1.0;v+=d)
{
glBegin(GL_LINE_LOOP);
sp.uv2xyz(x,y,z,u-d,v-d); glVertex3f(x,y,z);
sp.uv2xyz(x,y,z,u-d,v ); glVertex3f(x,y,z);
sp.uv2xyz(x,y,z,u ,v ); glVertex3f(x,y,z);
sp.uv2xyz(x,y,z,u ,v-d); glVertex3f(x,y,z);
glEnd();
}
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
// sphere mapped corrected
glMatrixMode(GL_MODELVIEW);
glTranslatef(+5.0,0.0,0.0);
glColor3f(0.0f, 0.0f, 1.0f);
for (u=d-1.0;u<=1.0;u+=d)
for (v=d-1.0;v<=1.0;v+=d)
{
glBegin(GL_LINE_LOOP);
sp.uv2xy(x,y,u-d,v-d); glVertex3f(x,y,0.0);
sp.uv2xy(x,y,u-d,v ); glVertex3f(x,y,0.0);
sp.uv2xy(x,y,u ,v ); glVertex3f(x,y,0.0);
sp.uv2xy(x,y,u ,v-d); glVertex3f(x,y,0.0);
glEnd();
}
glTranslatef(-5.0,-5.0,0.0);
}
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glFlush();
SwapBuffers(hdc);
这是结果:
sp.uv2xy
将 2D (u,v) 图像坐标转换为投影校正的 2D (x,y) 坐标 (image)
sp.uv2xyz
将 2D (u,v) 图像坐标转换为投影校正的 3D (x,y,x) 坐标(球面,其中 x,y 轴对应于屏幕 x,y 轴)
sp.mode
{1,2} 选择您要使用的投影类型
sp.u0,v0,m
选择投影图像中点和比例尺
sp.x0,y0,z0,r0
定义您投影的球体
[edit2] 等距球体投影
这个2D不需要校正u,v
坐标直接转换为球面角a=long,b=lat
所以对于u,v
范围<0,+1>
:
a=x*2.0*M_PI; b=(y-0.5)*M_PI;
那么3D坐标就是球面变换:
x=x0+(r0*cos(b)*cos(a));
y=y0+(r0*cos(b)*sin(a));
z=z0+(r0*sin(b));
如果要逆向变换google球坐标系
我想像在球体上一样显示图像 - 但在平面上。 此操作的一个示例是 Mercatore 投影,即来自地球的地球地图 "unrolled"。 为了更好地解释我自己,在一个球体上有一个方形纹理——不是在整个球体上,而是在它的一部分上——我想在一个平面上展示在球体上看到这个纹理的结果。 我已经找到了这个: How do I 'wrap' a plane over a sphere with three.js?
但我想用着色器来做,因为它可能是最有效的,但也可能是最困难的。我很难找到合适的公式。有什么数学框架吗?
您应该指定您真正想要的投影。曲面有很多方法(不仅仅是球体)。你的问题是这种变换的逆,所以首先是直接投影(平面 - >球面)。我用这两个(都用于特定目的):
距球面中心的距离与平面上的距离相匹配
这用于校正曲面上的纹理,例如眼镜上的装饰品等...
球体上到视轴的垂直距离与平面上的距离相匹配
因此,如果您从视轴看,您会在球体和平面上看到相同的图像,只需设置坐标系即可,因此
Z
轴是观察方向,x,y
轴是对应于您的二维平面轴。然后只需计算 z 坐标以匹配球体表面
我想你想要第一个选项
因此计算中间点 (x0,y0)
作为边界框的中心或均匀分布的点平均点。通过 atan2
以弧度计算每个点和坐标(从中点开始)的 ang
!!!
然后计算 dx,dy
并将二维坐标计算为 (x,y)=(x0+dx,y0+dy)
这里是结果示例(我将其用于任何类型的曲率):
[备注]
还有另一种基于光线投射的方法,可能还有更多...
[edit1] C++ 示例
为您破解小型 C++ class:
//---------------------------------------------------------------------------
#include <Math.h>
class sphere_projection
{
public:
float x0,y0,z0,r0; // 3D sphere
float u0,v0; // mid point of 2D image
float m; // scale 2D image
int mode; // which projection type
sphere_projection()
{
x0=0.0; y0=0.0; z0=0.0; r0=1.0;
u0=0.0; v0=0.0; m=1.0;
mode=1;
}
void uv2xyz(float &x,float &y,float &z,float u,float v)
{
if (mode==1)
{
float a,b;
// 2D position scaled around midpoint and converted from arclength to angle
u=(u-u0)*m/r0;
v=(v-v0)*m/r0;
// correct on radius distrotion in both axises
a=u/cos(v);
b=v/cos(u);
// compute the 3D cartesian point on surface
z=z0+(r0*cos(b)*cos(a));
x=x0+(r0*cos(b)*sin(a));
y=y0+(r0*sin(b));
}
if (mode==2)
{
// 2D position scaled around midpoint
x=(u-u0)*m;
y=(v-v0)*m;
// compute the 3D cartesian point on surface
x=x0+x;
y=y0+y;
z=z0+sqrt(r0*r0-x*x-y*y);
}
}
void uv2xy (float &x,float &y, float u,float v)
{
if (mode==1)
{
float a,b,z;
// 2D position scaled around midpoint and converted from arclength to angle
a=(u-u0)*m/r0;
b=(v-v0)*m/r0;
// correct on radius distrotion in both axises and convert back to 2D position
x=u0+(a*r0/(m*cos(b)));
y=v0+(b*r0/(m*cos(a)));
}
if (mode==2)
{
float z;
// 2D position scaled around midpoint + Z axis
x=(u-u0)*m;
y=(v-v0)*m;
z=sqrt(r0*r0-x*x-y*y);
// compute arclengths and convert back to 2D position
x=u0+(r0*atan2(x,z)/m);
y=v0+(r0*atan2(y,z)/m);
}
}
};
//---------------------------------------------------------------------------
这是如何使用它(在 OpenGL 中渲染):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(0.0,+2.5,-20.0);
static float ang=0.0; ang+=2.5;
float x,y,z,u,v,d=0.2;
sphere_projection sp;
sp.x0=0.0;
sp.y0=0.0;
sp.z0=0.0;
sp.r0=1.5;
sp.u0=0.0;
sp.v0=0.0;
sp.m =0.5;
for (sp.mode=1;sp.mode<=2;sp.mode++)
{
// original 2D grid
glMatrixMode(GL_MODELVIEW);
glTranslatef(-5.0,0.0,0.0);
glColor3f(1.0f, 1.0f, 1.0f);
for (u=d-1.0;u<=1.0;u+=d)
for (v=d-1.0;v<=1.0;v+=d)
{
glBegin(GL_LINE_LOOP);
glVertex3f(u-d,v-d,0.0);
glVertex3f(u-d,v ,0.0);
glVertex3f(u ,v ,0.0);
glVertex3f(u ,v-d,0.0);
glEnd();
}
// sphere mapped corrected
glMatrixMode(GL_MODELVIEW);
glTranslatef(+5.0,0.0,0.0);
glPushMatrix();
glRotatef(ang,0.0,1.0,0.0);
glColor3f(1.0f, 0.0f, 0.0f);
for (u=d-1.0;u<=1.0;u+=d)
for (v=d-1.0;v<=1.0;v+=d)
{
glBegin(GL_LINE_LOOP);
sp.uv2xyz(x,y,z,u-d,v-d); glVertex3f(x,y,z);
sp.uv2xyz(x,y,z,u-d,v ); glVertex3f(x,y,z);
sp.uv2xyz(x,y,z,u ,v ); glVertex3f(x,y,z);
sp.uv2xyz(x,y,z,u ,v-d); glVertex3f(x,y,z);
glEnd();
}
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
// sphere mapped corrected
glMatrixMode(GL_MODELVIEW);
glTranslatef(+5.0,0.0,0.0);
glColor3f(0.0f, 0.0f, 1.0f);
for (u=d-1.0;u<=1.0;u+=d)
for (v=d-1.0;v<=1.0;v+=d)
{
glBegin(GL_LINE_LOOP);
sp.uv2xy(x,y,u-d,v-d); glVertex3f(x,y,0.0);
sp.uv2xy(x,y,u-d,v ); glVertex3f(x,y,0.0);
sp.uv2xy(x,y,u ,v ); glVertex3f(x,y,0.0);
sp.uv2xy(x,y,u ,v-d); glVertex3f(x,y,0.0);
glEnd();
}
glTranslatef(-5.0,-5.0,0.0);
}
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glFlush();
SwapBuffers(hdc);
这是结果:
sp.uv2xy
将 2D (u,v) 图像坐标转换为投影校正的 2D (x,y) 坐标 (image)sp.uv2xyz
将 2D (u,v) 图像坐标转换为投影校正的 3D (x,y,x) 坐标(球面,其中 x,y 轴对应于屏幕 x,y 轴)sp.mode
{1,2} 选择您要使用的投影类型sp.u0,v0,m
选择投影图像中点和比例尺sp.x0,y0,z0,r0
定义您投影的球体
[edit2] 等距球体投影
这个2D不需要校正u,v
坐标直接转换为球面角a=long,b=lat
所以对于u,v
范围<0,+1>
:
a=x*2.0*M_PI; b=(y-0.5)*M_PI;
那么3D坐标就是球面变换:
x=x0+(r0*cos(b)*cos(a));
y=y0+(r0*cos(b)*sin(a));
z=z0+(r0*sin(b));
如果要逆向变换google球坐标系