Trying to create a sphere in Processing

I’m familiar with old OpenGL but just getting up to speed on shaders. I was writing code in Processing and took a working demo that draws a cylinder and attempted to convert it to draw a sphere.

There are three components to the code. First, there is the processing code, a class called PShape that encapsulates an OpenGL shape. The code for a cylinder uses a single QUAD_STRIP. For a sphere, I didn’t come up with one, though it occurs to me that’s not a bad approach. So, question 1: Does anyone have code that maps a sphere, not as a grid of quads with a north pole and south pole cap, but some kind of gently curving path that completely covers the sphere? If not, what is the best way? I was originally intending to create bands of quads that cover the majority of the sphere except for the poles, and an endcap at each pole.

When I inadvertently had a single QUAD_STRIP with the ends not connected to each other, I could see the earth, with blue and white noise on top. I realized that my code was doing a band of latitudes, and that the end of each strip was not connected to the next strip. So I tried to create a number of strips. When I do, I get all white.

I realize that this may be something involving processing, but most of the expertise for this is in OpenGL, so I’m asking here about that aspect.

bad sphere:
[ATTACH=CONFIG]577[/ATTACH]

With current code, no texture shows at all. I have left in the “can” code so you can see the code that worked creating a cylinder.
I will show first the shaders, then the Processing code that calls them. I have not changed the shaders at all.

#define PROCESSING_TEXTURE_SHADER


uniform mat4 transform;
uniform mat4 texMatrix;


attribute vec4 vertex;
attribute vec4 color;
attribute vec2 texCoord;


varying vec4 vertColor;
varying vec4 vertTexCoord;


void main() {
  gl_Position = transform * vertex;
    
  vertColor = color;
  vertTexCoord = texMatrix * vec4(texCoord, 1.0, 1.0);
}



#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif


uniform sampler2D texture;


varying vec4 vertColor;
varying vec4 vertTexCoord;


void main() {
  gl_FragColor = texture2D(texture, vertTexCoord.st) * vertColor;
}



Here is the code in processing that invokes the shaders

PImage label;
PShape can;
float angle;


PShader texShader;


void setup() {
  size(1280, 800, P3D);  
  label = loadImage("earth.jpg");
  //can = createCan(100, 200, 32, label);
  can = createSphere(350, 64, label);
  texShader = loadShader("texfrag.glsl", "texvert.glsl");
}


void draw() {    
  background(0);
  shader(texShader);      
  translate(width/2, height/2);
  rotateX(-PI/2);
  rotateZ(angle);  
  shape(can);  
  angle += 0.01;
}


PShape createCan(float r, float h, int detail, PImage tex) {
  textureMode(NORMAL);
  PShape sh = createShape();
  sh.beginShape(QUAD_STRIP);
  sh.noStroke();
  sh.texture(tex);
  for (int i = 0; i <= detail; i++) {
    float angle = TWO_PI / detail;
    float x = sin(i * angle);
    float z = cos(i * angle);
    float u = float(i) / detail;
    sh.normal(x, 0, z);
    sh.vertex(x * r, -h/2, z * r, u, 0);
    sh.vertex(x * r, +h/2, z * r, u, 1);    
  }
  sh.endShape(); 
  return sh;
}


PShape createSphere(float r, int detail, PImage tex) {
  textureMode(NORMAL);
  PShape sh = createShape();
  sh.noStroke();
  sh.texture(tex);
  final float dA = TWO_PI / detail; // change in angle
  
  // process the sphere one band at a time
  // going from almost south pole to almost north
  // poles must be handled separately
  float theta2 = -PI/2+dA;
  float SHIFT = PI/2;
  float z2 = sin(theta2); // height off equator
  float rxyUpper = cos(theta2); // closer to equator
  for (int i = 1; i < detail; i++) {
    float theta1 = theta2; 
    theta2 = theta1 + dA;
    float z1 = z2;
    z2 = sin(theta2);
    float rxyLower = rxyUpper;
    rxyUpper = cos(theta2); // radius in xy plane
    sh.beginShape(QUAD_STRIP);
    for (int j = 0; j <= detail; j++) {
      float phi = j * dA; //longitude in radians
      float xLower = rxyLower * cos(phi);
      float yLower = rxyLower * sin(phi);
      float xUpper = rxyUpper * cos(phi);
      float yUpper = rxyUpper * sin(phi);
      float u = phi/TWO_PI;
      sh.normal(xUpper, yUpper, z2);
      sh.vertex(r*xUpper, r*yUpper, r*z2, u,(theta2+SHIFT)/PI);    
      sh.normal(xLower, yLower, z1);
      sh.vertex(r*xLower, r*yLower, r*z1, u,(theta1+SHIFT)/PI);
    }
    sh.endShape();   
  }
  return sh;
}