2

I'm currently writing a program to randomly generate low reoslution planets. I already have a way to render flat 2D planets but now I'd like to add an option to give them the illusion of being 3D as well.

To achive this I had the Idea to create a 3D noise Sphere. First, I Translate (x,y) coordinates to the corresponding (x,y,z) coordinates on the Sphere. Then I rotate the Sphere to get the coordinates at animationFrame i and use those to sample the noise at these coordinates.

For this I wrote the following code:

for (let x = -radius; x < radius; x++)
        for (let y = -radius; y < radius; y++)
            for (let z = 0; z < animationLength; z++) {

                //rotation for this frame
                //map re-maps a number from one range to another.
                const angle = map(z, 0, animationLength, 0, 2 * Math.PI);

                //translate 2D to 3D point 
                const longitude = (x * Math.PI) / 180;
                const latitude = (y * Math.PI) / 180;

                const SphereX = radius * Math.cos(latitude) * Math.cos(longitude);
                const SphereY = radius * Math.cos(latitude) * Math.sin(longitude);
                const SphereZ = radius * Math.sin(latitude);

                //Rotate Sphere on Y Axis depending on animationFrame
                const rotX = (SphereX * Math.cos(angle) + SphereZ * -Math.sin(angle));
                const rotZ = (SphereX * Math.sin(angle) + SphereZ * Math.cos(angle));

                const noiseVal = noise3D(rotX , SphereY, rotZ );

                let val = noiseVal / (1.5);

                val = Math.pow(val * 1.15, 0.9);

                texture[x + radius][y + radius][z] = val

            }

The porblem is that (I think) the Sphere is rendered from the origin at (0,0), making it look like this: Rotating planet from inside

To solve this, I think I would have to look at the sphere from a fixed position outside it, but I don't know how I would go about that.

If anyone knows how this can be achived I'd greatly appreciate that.

LexieVer
  • 31
  • 4
  • your conversion from `x,y = <-r , +r>` to angles looks wrong.... you just convert from `[deg]` to `[rad]` but in such case the for loops should go `x = <-180 , +180>` and `y = <-90 , +90>` if you want real raycast then look at these [sphere in fragment shader](https://stackoverflow.com/a/41442375/2521214) , [Atmospheric scattering](https://stackoverflow.com/a/19659648/2521214) , [GLSL back ray tracer](https://stackoverflow.com/a/45140313/2521214) you can add transform matrices to add the rotation ... – Spektre Apr 03 '22 at 09:04
  • to enhance your way check this [Is there a more efficient way of texturing a circle?](https://stackoverflow.com/a/61097673/2521214) you do not need goniometrics for rendering sphere ... even the 3th coordinate can be computed just by `z = sqrt(r*r - x*x - y*y)` – Spektre Apr 03 '22 at 09:08

1 Answers1

1

So, I found a way to achive what I wanted now by doing a raycast, like mentioned in the comment by Spektre.

First, I define the center of the Sphere as well as an origin point from wich to cast the ray. Next I iterate over all pixels I want to sample and use the (x,y) coordinate to calculate the direction of the ray from the origin.

Since I now have a Sphere with a radius and center, as well as infinite line, I use a simple ray-sphere-intersection test to get the point on the Sphere from this raycast.

And finally, I rotate the found coordinate with the Rodrigues' rotation formula by the angle determined by the frame z.

This is the relevant code:

const origin: Vector3 = new Vector3(0, 0, -100);
const center: Vector3 = new Vector3(0, 0, 0);

for (let x = -radius; x < radius + 0; x++)
    for (let y = -radius; y < radius + 0; y++)
        for (let z = 0; z < depth; z++) {

            const angle = map(z, 0, depth, 0, 2 * Math.PI);

            //calculate Sphere based on line-sphere-intersection
            //https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-sphere-intersection
            let t0: number, t1: number;

            const direction: Vector3 = new Vector3(x, y, 40);

            const L: Vector3 = subVec3(origin, center);

            const a: number = direction.dot(direction);
            const b: number = 2 * direction.dot(L);
            const c: number = L.dot(L) - (radius * radius);

            //returns either (x1, x2), (x1 = x2) or (NaN) for both values depending on if  
            //the equation can be solved
            [t0, t1] = solveQuadratic(a, b, c);

            if (isNaN(t0)) {
                continue;
            }

            //use t0 to calculate 
            const hit: Vector3 = addVec3(origin, multiplyScalar(direction, t0));

            //RotationAxis
            const unitVec: Vector3 = new Vector3(0, -1, 0);

            //Rodrigues' rotation formula
            const vRot: Vector3 = addVec3(
                addVec3(multiplyScalar(hit, Math.cos(angle)), multiplyScalar(crossProduct(unitVec, hit), Math.sin(angle))),
                multiplyScalar(mulVec3(unitVec, mulVec3(unitVec, hit)), (1 - Math.cos(angle)))
            );

            texture[x + radius][y + radius][z] = noise3D(vRot.x, vRot.y, vRot.z);
        }

And this is the Result:

RotationgPlanet

LexieVer
  • 31
  • 4