Plane to Sphere Shader

Hi!
I am a bit of a newbie with shader, so excuse me if this is too basic.
I am trying to map a plannar FBO into a sphere of changing radius. I guess that this should be done in the vertex shader better than in the fragment shader., but I can’t find the way to do it.
Any suggestion? How to map the cartesian 2D into a sphere? I’ve found info about Mercator projection, -it should be the other way around, but that’s OK- but I can’t find the way to implement it in the vertex file.

Thanks a lot!
D!

Hi @dmelladom!

I have very little experience with shader programming, but mapping a plane to a sphere is not too difficult (depending on exactly what you want…)

A sphere has a circumference of 2pi (or 360 degrees if you want), so you need to map the x coordinates from 0 to 2pi and the y coordinates from 0 to pi. Then you can convert these coordinates (basically latitude and longitude) to spherical coordinates to plot them on a sphere. Think about a map of the earth you want to wrap around a sphere…

the code to map an image/fbo with a given width/height to a sphere would look something like this…

ofMesh mesh;
float width = 500;
float height = 250;
vector <float> longitude;
	vector <float> latitude;
	float SphereRadius = 250.0;



for (int x=0; x<width; x++) {
    for (int y=0; y<height; y++) {
        latitude.push_back( ofMap(x, 0, width, 0, (2*PI) ) );
        longitude.push_back( ofMap(y, 0, height, 0, PI ) );
    }
}

for (unsigned i = 0; i<longitude.size(); i++) {
    float longTMP = longitude[i];
    float latTMP = latitude[i];
    float X = ( SphereRadius * cos(latTMP) * sin(longTMP) );
    float Y = ( SphereRadius * sin(latTMP) * sin(longTMP) );
    float Z = ( SphereRadius * cos(longTMP) );
    ofVec3f temp = ofVec3f(X,Y,Z);
    mesh.addVertex(temp);
    mesh.addColor( ofFloatColor(1,1,1));
}

I used code similar to this to generate a globe that would not look out of place in the next tron movie :smiley:

Unfortunately, I don’t know how to apply this trick in a shader, maybe someone else can help out?

1 Like

Hi innodle!
You project looks really interesting! I hope this could help.
This code has been developed by Javier Villaroel as estated in the coments. I used it in a fragment shader with great results. Begin it before drawing the FBO, pass the paramenters, draw and finish it after all , just the regular way.

//////////////////////////////////////////////////////////////////////
//                                                                  //
//                                                                  //
//  Code developed by Javier Villaroel,                             // 
//                  Madrid, Spain, March 2015                       //
//                  javier@ultra-lab.net                            //
//                  www.ultra-lab.net                               //
//                                                                  //
//////////////////////////////////////////////////////////////////////

#ifdef GL_ES
precision mediump float;
#endif

uniform sampler2DRect texture0;

uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;


void main(void)
{
   vec2 center = resolution/2.0;

   vec2 pos = gl_TexCoord[0].xy;
   float pi = 3.1415;
   float R;
   vec2 r = pos - center; 
   float mr = length(r);
   vec2 posM;
   float angT = 0.0;//10.0*time;

   float expansionT = 5.0;
   float giroT = 20.0;
   float radioMax = 4000.0;



    if (time < expansionT){

        R = radioMax - time*(radioMax - 0.5*resolution.y)/expansionT;
    } else {
        if ( time < giroT + expansionT){
            R = resolution.y * 0.5;
            angT =  0.5 * resolution.y * sin(2.0*pi*(time - expansionT)/giroT);
        } else {
            R =  -(radioMax - 0.5*resolution.y)*(giroT+expansionT)/expansionT
           + time*(radioMax - 0.5*resolution.y)/expansionT
           + resolution.y*0.5;
        }
 } 


if(length(r) < R)
{

    float theta = acos(-r.y/R)/pi;  //angulo con respecto a los polos
    float phi = abs(atan(sqrt(abs(R*R - r.x*r.x - r.y*r.y))/r.x))/pi; //angulo en el plano XY de la esfera

    float ajusteP = resolution.y / 2.0 + resolution.x / 2.0 - angT;
    float ajusteN = resolution.x / 2.0 - resolution.y / 2.0 - angT;      
  
     if(R <= resolution.y / 2.0)
    {
        posM.y = resolution.y * theta;
        posM.x = resolution.y * phi;                  
    } 
  
    if(R > resolution.y / 2.0 )
    {
        float alpha = acos(resolution.y/(2.0*R))/pi;
        float A = resolution.y /(1.0-(2.0*alpha));

        float beta = acos(resolution.y/(2.0*R))/pi;
        float B = resolution.y /(1.0-(2.0*beta));

        posM.y = A * (theta - alpha);
        posM.x = B * (phi - beta);
    }

    if(r.x > 0.0){
        posM.x =  ajusteP - posM.x;
    } else {
       posM.x += ajusteN;
    }

   if (posM.x <= 0.0 || posM.y <= 0.0 || posM.y > resolution.y || posM.x > resolution.x){
       discard;
    } else {
       vec4 color = texture2DRect(texture0, posM);
       gl_FragColor = color;
    }

 
    } else {

       discard;
 
    }

   
}