Rotation on Sphere Surface and Model lighting

Dear Babylon-Team,
i have new challenges, if someone can help me out :slight_smile:

1. Set rotation of objects (.gltf) on a sphere surface (i.e. planet) related to its position on sphere surface, there are 2 ways I found:

  • 1a. would it work to use lookAt and additionally set rotation += fixValue?
  • 1b. I think better is to get longitude/lattitude and set rotation = {x:longitude, y:lattitude+PI, z:0}? If so do I get longitude by arccos(x / y) and lattitude by arccos(x / z)?
    Up to now I get my sphere surface positions by fibonacci lattice algorithm.

2. What is recommended to light up objects on a planet surface?

  • 2a. Worst option would be to use Pointlight for every object, because of ressources/performance? Unfortunately til now every Pointlight setup did not work, like shown in image below.
  • 2b. I read a bit about Hermisphericlight and tried it but no success. Is there something I need to conside/configure, because of my specific project of space (systems, planets, asteroidfields, nebula etc.)?
  • 2c. Also I read about scene.createDefaultEnvironment, it works in case of lighting my objects, but there are unwanted issues occuring like a wall showing in the back and when i look down (in negative Y direction) the gamma/contrast of the universe is too high (image is below).

3. I have a gltf that dont want to rotate. The root mesh should be a TransformNode, but rotating does not work. Every direct or non-direct children should be affected by rotation of root or is that function a disabled somewhere in TransformNode (root) or gltf, that property I could check out? I checked, all meshes of the gltf are direct or non-direct childs of root. Here the code, how I load the gltf:

Hello @Takemura , how are you doing?

Let me see if I understood your question correctly:

1 - Are you trying to only set the objects rotation? Or are you trying to move them in a orbit around the planet? If the only thing you are trying to do is to set the rotation I think the lookAt + fixValue might work. (If you want to setup a playground we can help you to fine tune it to make it right).

2 - Have you tried to use DirectionalLight? DirectionalLight | Babylon.js Documentation (babylonjs.com), those a good if you want to match a big light source like the sun or a planet. What would be the light source in you project?

3 - Can you provide a Playground example for that?

Greets srzerbetto,

having my fun using Babylon :slight_smile:

  1. No orbiting, just fixed position on sphere surface. I will set PG combined with #3
  2. I m interested in something more playable than realism, so less “shadow” influences like an environmental ambient light, that you can see objects on planet surface, or asteroids. Main light source is the sun. Can the planet light shine through its surface (a Sphere Mesh like in official planet demo) to lighten up objects, while keeping the shadowed side of the planet? The shadow does not have to be as dark as it is now. Would you recommend DirectionalLight for this in suns and planets? As of now I use lensFlareSystem + Pointlight for suns.

Here is the PG to solve #1 and #3: https://playground.babylonjs.com/#EZCN5Q#3

When I try on none equitorial position my setup is incorrect: https://playground.babylonjs.com/#EZCN5Q#4

@Takemura , for the #1 problem, you can use the alignWithNormal and use the vector that points from the center of the sphere to the object as a normal to align with. Also, as it is not garanteed that the actual center of the planet glb will the the geometric center of the sphere I use sphere.getBoundingInfo().boundingBox.center to get the geometric center.

Where it is the updated playground:

Angle on Surface | Babylon.js Playground (babylonjs.com)

1 Like

As for question #3, I’m assuming there are some meshes that are child of the planet mesh but you don’t want to rotate them with when the planet rotates, is that right?

Thank you, #1 and #3 is solved, your solution with alignWithNormal seems to set everything correctly.

For #2 I think DirectionalLight will only work if the sun is really far away from your meshes (Directional Light is made for things that are infinitely far, like the sun when you are on earth). However, if you planets are going to be orbiting the sun, DirectionalLight is not going to work, Pointlight at the sun position will give you better results.

You are right, my planets are simply orbiting around the sun. That is why I already wondered how to parent DirectionalLight. I tried to change your PG solution to be more like my project in terms of scales ( https://playground.babylonjs.com/#EZCN5Q#8 ). How do I make the planet reflect, so that some parts of the building aren’t black? FYI Here is the constructor of the Planet Class:

       // HERE necessary datas are saved

       const options = {
            biomes: 1,
            clouds: true,
            mapSize: 1024,
            upperColor: new BABYLON.Color3(2, 1, 0),
            lowerColor: new BABYLON.Color3(0, .2, 1),
            haloColor: new BABYLON.Color3(0, .2, 1),
            maxResolution: 64,
            seed: .3,
            cloudSeed: .55,
            lowerClamp: new BABYLON.Vector2(.6, 1),
            groundAlbedo: 1.1,
            cloudAlbedo: .9,
            directNoise: false,
            lowerClip: new BABYLON.Vector2(0, 0),
            range: new BABYLON.Vector2(.3, .35)
        }

        const terrain = new BABYLON.DynamicTexture('random', 128, this.scene, false, BABYLON.Texture.NEAREST_SAMPLINGMODE);
        const clouds = new BABYLON.DynamicTexture('random', 128, this.scene, false, BABYLON.Texture.NEAREST_SAMPLINGMODE);

        let updateRandomSurface = function (random) {
            let context = random.getContext();
            let imgData = context.getImageData(0, 0, 512, 512);
            for (let i = 0; i < 512 * 512 * 4; i++) {
                imgData.data[i] = Math.random() * 256 | 0
            }
            context.putImageData(imgData, 0, 0);
            random.update()
        };

        let noiseTexture;
        let cloudTexture;

        const cx =  (ox > 0) ? ox * Math.cos(0): x;
        const cy =  (oy > 0) ? oy * Math.cos(0): y;
        const cz =  (oz > 0) ? oz * Math.sin(0): z;

        this.body = BABYLON.MeshBuilder.CreateSphere('Planet', { segments: 64, diameter: 2*this.data['planet_radius']}, this.scene);
		this.body.parent = this.mesher.body;

// HERE buildings for planet are created

this.body.position = new BABYLON.Vector3(cx, cy, cz);
        this.body.receiveShadows = true;
		this.body.isBlocker = true;
        this.body.checkCollisions = true;

        let shaderMaterial = new BABYLON.ShaderMaterial('shader', this.scene, {
            vertex: './assets/textures/planets/planet',
            fragment: './assets/textures/planets/planet'
        }, {
            attributes: ['position', 'normal', 'uv'],
            uniforms: ['world', 'worldView', 'worldViewProjection', 'view', 'projection'],
            needAlphaBlending: true
        });
        shaderMaterial.setVector3('cameraPosition', this.camera.position);
        shaderMaterial.setVector3('lightPosition', this.light.position);
        this.body.material = shaderMaterial;

       const planetShell = this.body;
		var angle = 0;
        this.scene.registerBeforeRender(function () {
            const ratio = game.scene.getAnimationRatio();
            planetShell.rotation.y += .0001 * ratio;
            shaderMaterial.setMatrix('rotation', BABYLON.Matrix.RotationY(angle));
            angle -= 4e-4 * ratio;
            shaderMaterial.setVector3('options', new BABYLON.Vector3(options.clouds, options.groundAlbedo, options.cloudAlbedo));
        });
       
      var generateBiome = function(biome) {
             // HERE biome options are changed if not default planet type

             if (noiseTexture) {
                noiseTexture.dispose();
                cloudTexture.dispose()
            }
            updateRandomSurface(terrain);
            updateRandomSurface(clouds);
            noiseTexture = new BABYLON.ProceduralTexture('noise', options.mapSize, './assets/textures/noises/noise', game.scene, null, true, true);
            noiseTexture.setColor3('upperColor', options.upperColor);
            noiseTexture.setColor3('lowerColor', options.lowerColor);
            noiseTexture.setFloat('mapSize', options.mapSize);
            noiseTexture.setFloat('maxResolution', options.maxResolution);
            noiseTexture.setFloat('seed', options.seed);
            noiseTexture.setVector2('lowerClamp', options.lowerClamp);
            noiseTexture.setTexture('randomSampler', terrain);
            noiseTexture.setVector2('range', options.range);
            noiseTexture.setVector3('options', new BABYLON.Vector3(options.directNoise ? 1 : 0, options.lowerClip.x, options.lowerClip.y));
            shaderMaterial.setTexture('textureSampler', noiseTexture);
            cloudTexture = new BABYLON.ProceduralTexture('cloud', options.mapSize, './assets/textures/noises/noise', game.scene, null, true, true);
            cloudTexture.setTexture('randomSampler', clouds);
            cloudTexture.setFloat('mapSize', options.mapSize);
            cloudTexture.setFloat('maxResolution', 256);
            cloudTexture.setFloat('seed', options.cloudSeed);
            cloudTexture.setVector3('options', new BABYLON.Vector3(1, 0, 1));
            shaderMaterial.setTexture('cloudSampler', cloudTexture);
            shaderMaterial.setColor3('haloColor', options.haloColor);
     };

// Rest should be irrelevant for this case