Calculate uv coordinates from spherical coordinates

I’m using the phyllotaxis algorithm to draw a sphere of dots, and then I’m passing a texture to the vertex shader, but the uv coordinates are not calculated correctly cause the texture is not showing right.

const vector = new THREE.Vector3();
const points = [];
const uvs = [];

for (let i = 0; i < dotCount; i++) {
  // Phyllotaxis algorithm
  const phi = Math.acos(-1 + (2 * i) / dotCount);
  const theta = Math.sqrt(dotCount * Math.PI) * phi;

  vector.setFromSphericalCoords(radius, phi, theta);
  points.push(vector.clone());

  const u = (theta + Math.PI) / (2 * Math.PI);
  const v = 1.0 - phi / Math.PI;

  uvs.push(u, v);
}

const globeGeometry = new THREE.BufferGeometry().setFromPoints(points);
globeGeometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));

This is my vertex shader:

uniform sampler2D globeTexture;
varying float vVisibility;
varying vec3 vNormal;
varying vec3 vPosition;
varying vec3 vMvPosition;
attribute float displacement;
uniform float uTime;
void main() {
  vPosition = position;
  vNormal = normalMatrix * normalize(position);

  // Calculate model view position;
  vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
  vMvPosition = -mvPosition.xyz;

  // texture returns rgb variables for uv position in the globe texture (r, g, b, a)
  vVisibility = texture(globeTexture, uv).r;
  
  vec3 newPosition = position + vNormal * displacement * (sin(uTime * displacement * 1.) * 0.01 + cos(uTime * displacement * 0.5) * 0.01);
  
  gl_PointSize = 3.0 * (vVisibility < 0.5 ? 1. : 0.0);
  
  gl_PointSize *= 0.4 + (dot(normalize(vMvPosition), vNormal) * 0.6);
  gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
}

I was using a different algorithm to calculate the points position and it was working fine

Any thoughts?

you are not setting vUv anywhere in your shader

@makc3d I’m not using vUv anywhere in the shader

You’re sending the UV coordinates using:

globeGeometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));

But don’t you need to declare this in your vertex shader?

attribute uv vec2;

I would expect compilation errors about the uv variable being undefined, since you are using it in the main function, but it isn’t declared anywhere.

@Harold my understanding is that uv is a default attribute in the ShaderMaterial even if it is undefined, so I can’t re-declare it or it will throw an error.

It doesn’t have to do with a variable misplaced or not declared, it’s the way point coordinates are calculated.

Currently I’m using this algorithm, which works fine

const calculateDotPositions2 = (dotCount: number, worldRadius: number) => {
const sphericalCoordinates = new THREE.Spherical();
const points = [];
const uvs = [];

const dlong = Math.PI * (3 - Math.sqrt(5));
const dz = 2 / dotCount;

let r = 0;
let long = 0;
let z = 1 - dz / 2;

for (let i = 0; i < dotCount; i++) {
  r = Math.sqrt(1 - z * z);

  const x = Math.cos(long) * r;
  const y = z;
  const vz = -Math.sin(long) * r;

  const pointCoordinates: THREE.Vector3 = new THREE.Vector3(
    x,
    y,
    vz
  ).multiplyScalar(worldRadius);

  points.push(pointCoordinates);

  z = z - dz;
  long = long + dlong;

  sphericalCoordinates.setFromVector3(pointCoordinates);
  uvs.push(
    (sphericalCoordinates.theta + Math.PI) / (Math.PI * 2),
    1.0 - sphericalCoordinates.phi / Math.PI
  );
}

But i’m not sure how that works, so I wanted to have a simpler version to be sure what I’m doing, so I picked the phyllotaxis algorithm:

export const calculateDotPositions = (
  dotCount: number,
  worldRadius: number
) => {
  const vector = new THREE.Vector3();
  const points = [];
  const uvs = [];
  for (let i = 0; i < dotCount; i++) {
    // Phyllotaxis algorithm
    const phi = Math.acos(-1 + (2 * i) / dotCount);
    const theta = Math.sqrt(dotCount * Math.PI) * phi;

    vector.setFromSphericalCoords(worldRadius, phi, theta);
    points.push(vector.clone());

    const u = (theta + Math.PI) / (2 * Math.PI);
    const v = 1.0 - phi / Math.PI;

    uvs.push(u, v);
  }

  return {
    points,
    uvs,
  };
};

But not sure how to get the UVs

yes, that’s what I am saying

edit aah you mean vVisibility is wrong

@Federico_Toledo
What result do you get? And what is expected result? Any pictures?
Maybe would be better to provide an editable live code example? jsfiddle, codepen, codesandbox etc.

PS A sphere of evenly distributed elements with UV coords:

@prisoner849 sure, Edit fiddle - JSFiddle - Code Playground

If you see the code, there are two functions to generate the dot sphere. One of them works (I think I took that algorithm from one of your answers, lol) and the other one calculateDotPositions(), is not working because the UVs are wrong