【问题标题】:WebGL - display a sphere on a planeWebGL - 在平面上显示一个球体
【发布时间】:2015-07-31 11:32:34
【问题描述】:

我想展示一个像在球体上的图像 - 但在平面上。 这种操作的一个例子是 Mercatore 投影,即从地球上“展开”的地球地图。 为了更好地解释我自己,在球体上具有方形纹理 - 不是在整个球体上,而是在它的一部分上 - 我想在平面上显示在球体上看到这种纹理的结果。 我已经找到了这个: How do I 'wrap' a plane over a sphere with three.js?

但我想用着色器来做,因为它可能是最有效的,但也可能是最困难的。我很难找到合适的公式。有什么数学框架吗?

【问题讨论】:

  • 我不太明白。你有什么形式的数据,你想产生什么?你能用一张图片解释一下吗?
  • 嗨@NicoSchertler,编辑了问题!
  • 那么你有什么数据?如果你想做 Mercatore 投影,有公式。你想知道什么?
  • 我有一张图片,我想像右边的图片一样显示它,但是使用 WebGL。此图取自 Hugin 软件。
  • 这只有在您使用两种不同的投影(一个向前一个向后)时才有意义。无论如何,只需查找您要使用的投影公式即可。

标签: opengl-es glsl webgl shader vertex-shader


【解决方案1】:

您应该指定您真正想要的投影。曲面有很多方法(不仅仅是球体)。你的问题是这种变换的逆,所以首先是直接投影(平面->球面)。我使用这两个(都用于特定目的):

  1. 球体上区域中心的距离与平面上的距离相匹配

    这用于校正曲面上的纹理,例如眼镜上的装饰品等...

  2. 球体上到视轴的垂直距离与平面上的距离相匹配

    因此,如果您从视轴看,您会在球体和平面上看到相同的图像,只需设置坐标系,Z 轴是观察方向,x,y 轴对应于您的 2D 平面轴.然后只需计算 z 坐标以匹配球面

我想你想要第一个选项

所以计算中间点 (x0,y0) 作为边界框的中心或均匀间隔的点平均点。通过atan2 计算每个点的ang 和坐标(从中间点)弧度!!!

然后计算dx,dy 并计算二维坐标为(x,y)=(x0+dx,y0+dy)

这里的结果示例(我将其用于任何类型的曲率):

[备注]

还有其他基于光线投射的方法,可能还有更多...

[edit1] C++ 示例

为您准备的小型 C++ 类:

//---------------------------------------------------------------------------
#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) 坐标(图像)
  • 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] Sphere EquirectangularProjection

这个二维u,v坐标不需要校正直接转换成球角a=long,b=lat所以u,v&lt;0,+1&gt;范围内:

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));

如果你想要反向变换谷歌球坐标系

【讨论】:

  • 谢谢。但实际上,最后两张图像对我来说不是很清楚.. 我想将 1)图像投影到球体上(如问题图像左侧所示),然后从那里,2)投影图像在平面上的等角投影(如右图所示)。我认为,从您的代码来看,2) 选项是第一部分,虽然不会放大图像的底部和顶部,但只会限制中心的侧面
  • @Novalink 它是与中点等距的示例。透明度有点混乱,基本上来源是方形网格图像(2D 矢量格式),然后对其应用校正,图像在平面上(右侧)显示校正后的图像,在弯曲的圆柱表面上映射相同的图像(在左边)。是的,它仅应用于 x 轴,因为这是该特定程序的目的,但您可以同时将其应用于展位轴(这很容易显示图像,因为我已经开始工作了)
  • 我认为您在 2) 中的公式存在一些问题,它似乎将图像投影到双曲面上,而不是球体上..
  • 事实上,如果我在根中平方半径并在 y 前面加上一个减号,它就会投影到一个球体上..
  • @Novalink 添加了Equirectangular 示例
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-10
  • 2020-05-01
  • 2012-04-29
  • 1970-01-01
  • 1970-01-01
  • 2014-04-01
  • 2015-04-28
相关资源
最近更新 更多