4
\$\begingroup\$

Okay, I think the problem that I'm having is that when I create my DirectionalLight with three.js, when I then rotate my object with OrbitControls the entire scene is rotating, not just the sphere I have in the middle of it ( the only object I have in my scene ).

I want to make it so that I can rotate my sphere with my mouse like OrbitControls does, but not have the camera move. Or have the light not move with the camera? Here's a gif of my scene so far that might better explain my dilemma—

super alpha globe

I want the light to be.. y'know.. more like the sun, coming from one point in space and shining at (0,0,0) in my scene, while still being able to rotate the sphere independently. Am I using OrbitControls wrong here? Here is what I have to get to the point I have in the gif above:

var width = section.offsetWidth,
height = section.offsetHeight;

var topOfElement = section.getBoundingClientRect().top - height;
var bottomOfElement = section.getBoundingClientRect().bottom;

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
camera.position.z = 10;
camera.position.x = -2.5;

const controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.enableZoom = false;
controls.maxPolarAngle = Math.PI;

const renderer = new THREE.WebGLRenderer( { alpha: true } );
renderer.setClearColor( 0x000000, 0 );
renderer.setSize(width, height);
section.appendChild(renderer.domElement);

const onEverythingLoaded = () => {
    render();
};

const loadingManager = new THREE.LoadingManager(onEverythingLoaded);
const loader = new THREE.TextureLoader(loadingManager);

const earthGeomerty = new THREE.SphereGeometry( 5.5, 32, 32 );
const earthDiffuse = loader.load('assets/drought_earth_texture.png');
const earthSpec = loader.load('assets/drought_earth_texture_bump.png');

const material = new THREE.MeshPhongMaterial( {
    specular: 0xffffff,
    map: earthDiffuse,
    specularMap: earthSpec,
    normalScale: new THREE.Vector2( 1, 1 ),
    shininess: 30,
    transparent: true,
    depthTest: true,
    depthWrite: false,
    polygonOffset: true,
    polygonOffsetFactor: - 4,
    wireframe: false
} );

const sphere = new THREE.Mesh( earthGeomerty, material );
const ambientLight = new THREE.AmbientLight( 0x667777 );

const directionalLight = new THREE.DirectionalLight( 0xffffdd, 2 );
directionalLight.position.set( 4, 0, 0 );
scene.add( sphere );
scene.add( ambientLight );
scene.add( directionalLight );

const render = () => {
    animationFrame = requestAnimationFrame( render );
    sphere.rotation.y += 0.001;
    renderer.render( scene, camera );
};

For further clarification, I want to have my light work like this:

ideal earth!!

while still being able to rotate the earth with my mouse like with OrbitControls. But the light doesn't move.

Is OrbitControls the wrong tool here? Any documentation / helpful links would be appreciated I am so, so lost.

(I cross-posted this from stackoverflow because I thought maybe the people over here would know more about this, hope that's ok!)

Update

After much fussing about with the OrbitControls functions I realised that what I'm trying to do isn't what OrbitControls is for, because "orbiting" isn't what I'm trying to do— I'm trying to rotate the actual sphere with my mouse, which is entirely different. Welp.

\$\endgroup\$
2
  • \$\begingroup\$ Not meaty enough for an actual answer, but right now it looks like you have your controls hooked up to the camera, so it's no surprise that you're getting results that look like you're moving the camera around the scene (i.e., that the light moves). If you want to be able to manipulate the sphere with your controls, then you'll need to hook your controls up to the sphere rather than the camera. \$\endgroup\$ Sep 30 '16 at 21:50
  • \$\begingroup\$ You're correct, I was rotating with the camera, not rotating the object itself, which I don't think I can use OrbitControls for.. now to find a different solution! \$\endgroup\$
    – shan
    Sep 30 '16 at 23:42
2
\$\begingroup\$

[PROBLEM SOLVED]

Hi!

I created the 'ObjectControls' module for ThreeJS that allows you to rotate a single OBJECT (or a Group), and not the SCENE.

You can find here a live demo https://albertopiras.github.io/threeJS-object-controls/

Here the repo https://github.com/albertopiras/threeJS-object-controls

It's very simple to use,

Hope this helps :)

Bye

Alberto

\$\endgroup\$
4
  • \$\begingroup\$ Please consider including a minimal summary of how the code works, so that the answer remains useful long into the future even if the links ever change. \$\endgroup\$
    – DMGregory
    Sep 20 '17 at 16:04
  • \$\begingroup\$ Hey, cool plugin. A couple things. The link to your demo example is broken, although I was able to find a working example demo on the github. Also, It appears this plugin as is doesn't rotate vertically, but only horizontally. Do you know how you could add vertical option as well? \$\endgroup\$
    – Starfs
    Jun 26 '18 at 20:54
  • \$\begingroup\$ Hi @Starfs, I updated the demo link, thank you:). Yes you could add the vertical rotation working on the function mouseMove(). You should write a control for "deltaMove.y" \$\endgroup\$ Jun 28 '18 at 6:53
  • \$\begingroup\$ Hi @Staff, now also vertical rotation is supported! check the new version of the project to set max angle rotations, change the mesh to rotate and other features! :) \$\endgroup\$ May 16 '19 at 14:37
2
\$\begingroup\$

Turns out I was using the wrong thing for the job I was trying to do. OrbitControls .. y'know.. orbits the scene, and I wanted to rotate the sphere itself. I ended up implementing some mouse drag logic like so..

let isDragging = false;
let previousMousePosition = {
    x: 0,
    y: 0
};

const toRadians = (angle) => {
    return angle * (Math.PI / 180);
};

const toDegrees = (angle) => {
    return angle * (180 / Math.PI);
};

const renderArea = renderer.domElement;

renderArea.addEventListener('mousedown', (e) => {
    isDragging = true;
});

renderArea.addEventListener('mousemove', (e) => {
    let deltaMove = {
        x: e.offsetX-previousMousePosition.x,
        y: e.offsetY-previousMousePosition.y
    };

    if (isDragging) {

        let deltaRotationQuaternion = new THREE.Quaternion().

        setFromEuler(
            new THREE.Euler(toRadians(deltaMove.y * 1), toRadians(deltaMove.x * 1), 0, 'XYZ')
        );

        sphere.quaternion.multiplyQuaternions(deltaRotationQuaternion, sphere.quaternion);
    }

    previousMousePosition = {
        x: e.offsetX,
        y: e.offsetY
    };
});

document.addEventListener('mouseup', (e) => {
    isDragging = false;
});

.. and this drags my sphere around like I wanted to!

\$\endgroup\$
1
\$\begingroup\$

If you have nothing else in the scene you can fake it by moving the light source to the same (relative) direction as the camera before rendering.

OrbitControls will move the camera but if the light follows the camera it'll give the illusion that it's only the earth rotating.

Like a spaceship engine that moves the entire galaxy backward rather than move the ship forward.

Everything is relative.

\$\endgroup\$
1
\$\begingroup\$

You can use OrbitControls for that. If using a directional light, the light will have a target (any object with a position). By default this is a Vector3(0, 0, 0)

Since it is the camera that is actually rotating (and moving), all you have to do is keep the light position the same as that of the camera.

light.position.copy(camera.position);

It took me a little while to wrap my head around that. I assumed rotation was applied to the scene, but it turns out: it's just a matter of following the camera position.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.