WebGL-在平面上显示球体 [英] WebGL - display a sphere on a plane

查看:107
本文介绍了WebGL-在平面上显示球体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想显示一个像在球体上但在飞机上的图像.此操作的一个示例是Mercatore投影,即从行星展开"的地球地图.为了更好地说明自己,在球体上具有正方形纹理-不是在整个球体上,而是仅在一部分球上-我想在飞机上展示在球体上看到这种纹理的结果.我已经找到了:

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

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

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

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

我认为您想要第一个选择

因此将中间点(x0,y0)计算为边界框的中心或均匀分布的点平均点.计算每个点的 ang 并通过 atan2 以弧度!!!

为坐标(从中间点开始)

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

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

[注释]

还有另一种基于射线投射的方法,可能还有更多...

[edit1] C ++示例

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

 <代码>//---------------------------------------------------------------------------#include< Math.h>类sphere_projection{上市:浮点数x0,y0,z0,r0;//3D球面浮点u0,v0;//2D图像的中点浮动米;//缩放2D图像整数模式;//哪种投影类型sphere_projection(){x0 = 0.0;y0 = 0.0;z0 = 0.0;r0 = 1.0;u0 = 0.0;v0 = 0.0;m = 1.0;模式= 1;}无效uv2xyz(float& x,float& y,float& z,float u,float v){如果(mode == 1){浮动a,b;//2D位置围绕中点缩放,并从弧长转换为角度u =(u-u0)* m/r0;v =(v-v0)* m/r0;//修正两个轴上的半径变形a = u/cos(v);b = v/cos(u);//计算曲面上的3D笛卡尔点z = z0 +(r0 * cos(b)* cos(a));x = x0 +(r0 * cos(b)* sin(a));y = y0 +(r0 * sin(b));}如果(mode == 2){//2D位置围绕中点缩放x =(u-u0)* m;y =(v-v0)* m;//计算曲面上的3D笛卡尔点x = x0 + x;y = y0 + y;z = z0 + sqrt(r0 * r0-x * x-y * y);}}无效uv2xy(float& x,float& y,float u,float v){如果(mode == 1){浮动a,b,z;//2D位置围绕中点缩放,并从弧长转换为角度a =(u-u0)* m/r0;b =(v-v0)* m/r0;//修正两个轴上的半径变形并转换回2D位置x = u0 +(a * r0/(m * cos(b)));y = v0 +(b * r0/(m * cos(a)));}如果(mode == 2){浮点z;//围绕中点+ Z轴缩放的2D位置x =(u-u0)* m;y =(v-v0)* m;z = sqrt(r0 * r0-x * x-y * y);//计算弧长并转换回2D位置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);静态浮点ang = 0.0;ang + = 2.5;浮点数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;对于(sp.mode = 1; sp.mode< = 2; sp.mode ++){//原始2D网格glMatrixMode(GL_MODELVIEW);glTranslatef(-5.0,0.0,0.0);glColor3f(1.0f,1.0f,1.0f);对于(u = d-1.0; u <= 1.0; u + = d)对于(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();}//球面贴图已更正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);对于(u = d-1.0; u <= 1.0; u + = d)对于(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();//球面贴图已更正glMatrixMode(GL_MODELVIEW);glTranslatef(+5.0,0.0,0.0);glColor3f(0.0f,0.0f,1.0f);对于(u = d-1.0; u <= 1.0; u + = d)对于(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

将这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球面坐标系

I would like to show an image like it was on a sphere - but on a plane. An example of this operation, would be the Mercatore projection, the map of the earth "unrolled" from the planet. To better explain myself, having a squared texture on a sphere - not on the WHOLE sphere, but on a part of it only - I would like to show on a plane the result of seeing this texture on the sphere. I found this, already: How do I 'wrap' a plane over a sphere with three.js?

But I would like to do it with shaders, because it might the most efficient, but probably also the most difficult. I have problems finding the right formula for it. There exists any mathematical framework for it?

解决方案

You should specify what projection you really want. There are many approaches for curved surfaces (not just for spheres). Your problem is the inverse of such transform so first the direct projection (plane -> sphere surface). I use these two (both are used for specific purposes):

  1. distances from middle of area on the sphere match the distances on the plane

    this is used to correct textures on curved surfaces for example Ornament dekors on glasses etc...

  2. perpendicular distances to the view axis on the sphere match the distances on the plane

    so if you are looking from the view axis you see the same image on the sphere and on the plane just set the coordinate system so Z axis is the viewing direction and x,y axises are corresponding to your 2D plane axises. Then just compute z-coordinate to match sphere surface

I think you want the first option

so compute middle point (x0,y0) as center of bounding box or for evenly spaced point average point. Compute ang for each point and coordinate (from middle point) via atan2 in radians !!!

Then compute dx,dy and compute 2D coordinates as (x,y)=(x0+dx,y0+dy)

Here example of the result (I use this for any kind of curvature):

[Notes]

There are also another approaches based on ray casting,and possibly much more ...

[edit1] C++ example

Busted small C++ class for you:

//---------------------------------------------------------------------------
#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);
            }
        }
    };
//---------------------------------------------------------------------------

This is how to use this (render in 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);

This is the result:

  • sp.uv2xy converts 2D (u,v) image coordinate to projection corrected 2D (x,y) coordinate (image)
  • sp.uv2xyz converts 2D (u,v) image coordinate to projection corrected 3D (x,y,x) coordinate (sphere surface where x,y axises corresponds with screen x,y axises)
  • sp.mode {1,2} selects whitch type of projection you want to use
  • sp.u0,v0,m selects the projection image mid point and scale
  • sp.x0,y0,z0,r0 defines the sphere on which you are projecting

[edit2] Sphere EquirectangularProjection

There is no correction needed for this one 2D u,v coordinate is directly converted to spherical angles a=long,b=lat so for u,v in range <0,+1>:

a=x*2.0*M_PI; b=(y-0.5)*M_PI;

Then the 3D coordinate is just spherical transformation:

x=x0+(r0*cos(b)*cos(a));
y=y0+(r0*cos(b)*sin(a));
z=z0+(r0*sin(b));

if you want the reverse transform google spherical coordinate system

这篇关于WebGL-在平面上显示球体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆