Reputation: 1773
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?
Upvotes: 2
Views: 2512
Reputation: 51845
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):
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...
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 usesp.u0,v0,m
selects the projection image mid point and scalesp.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
Upvotes: 2