The SpotLight class in Three.js illuminating a dynamic JSON model

Attempting to understand the light classes in three.js has been a bit of a challenge for me. I've been experimenting with an example in three.js, trying to place a 3D mesh model on the screen and give it a simple rotating animation.

Is there a way to have a stationary light source on the object while it moves? Currently, the light reflecting off the object seems to follow its rotation path.

Here is the code snippet: http://codepen.io/jagomez8/pen/BzByEz

I've tried swapping out different light classes, but I suspect that the issue lies within the MeshPhongMaterial. When I apply flatShading to the material, it gives me the desired result, except for the flat appearance it creates. The relevant code can be found on line 105.

if ( ! Detector.webgl ) Detector.addGetWebGLMessage();

        var renderer = new THREE.WebGLRenderer();

        var camera = new THREE.PerspectiveCamera( 35, window.innerWidth / window.innerHeight, 1, 1000 );

        var controls = new THREE.OrbitControls( camera, renderer.domElement );

        var scene = new THREE.Scene();
        var egg;

        var matFloor = new THREE.MeshPhongMaterial();
        var matBox = new THREE.MeshPhongMaterial( { color: 0x4080ff } );

        var geoFloor = new THREE.BoxGeometry( 2000, 1, 2000 );
        var geoBox = new THREE.BoxGeometry( 3, 1, 2 );

        var mshFloor = new THREE.Mesh( geoFloor, matFloor );
        var mshBox = new THREE.Mesh( geoBox, matBox );

        var ambient = new THREE.AmbientLight( 0xffffff, 0.1 );

        var spotLight = new THREE.SpotLight( 0xffffff, 1 );
        var lightHelper;

        var gui, guiElements, param = { color: '0xffffff' };

        function init() {

            renderer.shadowMap.enabled = true;
            renderer.shadowMap.type = THREE.PCFSoftShadowMap;

            renderer.gammaInput = true;
            renderer.gammaOutput = true;

            camera.position.set( 65, 8, - 10 );

            spotLight.position.set( 15, 40, 35 );
            spotLight.castShadow = true;
            spotLight.angle = Math.PI / 4;
            spotLight.penumbra = 0.05;
            spotLight.decay = 2;
            spotLight.distance = 200;
            spotLight.shadow.mapSize.width = 1024;
            spotLight.shadow.mapSize.height = 1024;
            spotLight.shadow.camera.near = 1;
            spotLight.shadow.camera.far = 200;

            lightHelper = new THREE.SpotLightHelper( spotLight );

            matFloor.color.set( 0x808080 );

            mshFloor.receiveShadow = true;
            mshFloor.position.set( 0, - 0.05, 0 );

            mshBox.castShadow = true;
            mshBox.position.set( 40, 1.8, 0 );

            scene.add( camera );
            scene.add( mshFloor );
            scene.add( mshBox );
            scene.add( ambient );
            scene.add( spotLight );
            scene.add( new THREE.AxisHelper( 10 ) );
            scene.add( lightHelper );

            document.body.appendChild( renderer.domElement );
            renderer.setSize( window.innerWidth, window.innerHeight );

            controls.addEventListener( 'change', render );
            controls.minDistance = 20;
            controls.maxDistance = 500;
            controls.maxPolarAngle = Math.PI / 2;
            controls.enablePan = false;
            controls.target.copy( mshBox.position );
            controls.update();

            window.addEventListener( 'resize', onResize, false );


            var manager = new THREE.LoadingManager();
            manager.onProgress = function( item, loaded, total ) {

                console.log( item, loaded, total );

            };

            var onProgress = function( xhr ) {

                if ( xhr.lengthComputable ) {

                    var percentComplete = xhr.loaded / xhr.total * 100;
                    console.log( Math.round( percentComplete, 2 ) + '% downloaded' );

                }

            };

            var onError = function( xhr ) {
            };

            var loader = new THREE.JSONLoader( manager );
            loader.load( 'http://alexgdm.com/egg.json', function( geometry, material ) {
                ///****3D MESH***///
                egg = new THREE.Mesh( geometry, new THREE.MeshPhongMaterial( { ambient: 0x050505, color: 0x0033ff, specular: 0x555555, shininess: 30/*, shading: THREE.FlatShading */}  )  );
                egg.position.set(0, 1, 1);


                scene.add( egg );
                animate();


            }, onProgress, onError );

        }

        function onResize() {

            renderer.setSize( window.innerWidth, window.innerHeight );
            camera.aspect = ( window.innerWidth / window.innerHeight );
            camera.updateProjectionMatrix();

        }

        function render() {

            lightHelper.update(); // required
            renderer.render( scene, camera );

        }

        function animate() {
            requestAnimationFrame( animate );
            egg.rotation.y += 0.01;

            renderer.render( scene, camera );
        }

        function clearGui() {

            if ( gui ) gui.destroy();

            gui = new dat.GUI();

            gui.open();

        }

        function buildGui() {

            clearGui();

            addGui( 'light color', spotLight.color.getHex(), function( val ) {

                spotLight.color.setHex( val );
                render();

            }, true );

            addGui( 'intensity', spotLight.intensity, function( val ) {

                spotLight.intensity = val;
                render();

            }, false, 0, 2 );

            addGui( 'distance', spotLight.distance, function( val ) {

                spotLight.distance = val;
                render();

            }, false, 0, 200 );

            addGui( 'angle', spotLight.angle, function( val ) {

                spotLight.angle = val;
                render();

            }, false, 0, Math.PI / 3 );

            addGui( 'penumbra', spotLight.penumbra, function( val ) {

                spotLight.penumbra = val;
                render();

            }, false, 0, 1 );

            addGui( 'decay', spotLight.decay, function( val ) {

                spotLight.decay = val;
                render();

            }, false, 1, 2 );

        }

        function addGui( name, value, callback, isColor, min, max ) {

            var node;
            param[ name ] = value;

            if ( isColor ) {

                node = gui.addColor( param, name ).onChange( function() {

                    callback( param[ name ] );

                } );

            } else if ( typeof value == 'object' ) {

                node = gui.add( param, name, value ).onChange( function() {

                    callback( param[ name ] );

                } );

            } else {

                node = gui.add( param, name, min, max ).onChange( function() {

                    callback( param[ name ] );

                } );

            }

            return node;

        }

        init();

        buildGui();

        render();

Answer №1

It seems that your egg-shaped model is not accurate due to incorrect vertex normals.

The issue may be caused by the y-component and z-component being swapped. To fix this, try reassigning them with .y = -.z, and .z = .y.

Alternatively, you can simply use the code snippet below to recalculate the vertex normals:

geometry.computeVertexNormals();

By doing so, you should see improved results in the vertex normals.

If needed, you can utilize THREE.VertexNormalsHelper to visualize the normals.

This advice pertains to three.js version r.77.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the procedure for utilizing Javascript to redirect a user on a website back to the login page if necessary?

Is there a way to redirect a website user back to the login page if they try to access a secure page without logging in first? I'm looking to implement this using JavaScript and cookies. Any suggestions or ideas on how to achieve this seamlessly for t ...

Experience a login page similar to Google's with a fully functional back button

I'm currently working on a login page that requires the user to enter their username and have it validated before proceeding. Once the username is confirmed as valid, a new input field for password entry should appear. However, I'm facing an issu ...

Ways to verify the timeframe between two specific dates

Having two distinctive arrays: accomodation: [ { id: 1, name: "Senator Hotel Fnideq", address: "Route de Ceuta, 93100 Fnidek, Morocco", checkin: "September 1", fullCheckinDate: "2021-09-01", ...

The FormControlLabel radio button within the RadioGroup is experiencing difficulty in becoming selected

Utilizing a RadioGroup component to showcase a dynamic list of Radio options using FormControlLabel. The dynamic radio choices are appearing correctly on the screen and I can obtain the selected radio option through onChange in RadioGroup. However, after s ...

What is causing the newly generated input tags to disappear from the page?

I'm having an issue where my code successfully creates new input tags based on user input, but they disappear shortly after being generated. What could be causing this to happen? <form> <input type='text' id='input' /> ...

Challenge involving CSS and Javascript for solving puzzles

In my attempt to create a puzzle with 2 rows and 3 columns using CSS and JavaScript, I envision the pieces of the puzzle being black and cut into various shapes. The objective is for users to drag these pieces onto the board in order to complete it. I have ...

Async/Await function is not behaving as intended

Our current approach involves storing short strings as keys. These keys are linked to longer values, which serve as labels. I am attempting to update the corresponding longer value for each key. However, a problem arises where console.log(record) always ...

Tips for handling errors in ajax calls with alternative promises

While working on an application that offers weather data based on user location coordinates, I created the code provided below (also accessible in this codepen http://codepen.io/PiotrBerebecki/pen/QNVEbP). The user's location information will be retr ...

Javascript updating text according to input in textbox functions properly initially, but the changes disappear afterward

This solution functions correctly for a brief moment before disappearing. function checkTFW() { var TFW = document.getElementById("TFW").value if (TFW == "mexicans") { document.getElementById("image").innerHTML = "<h1>Success!</h1>"; ...

Tips for using Cypress to confirm that an HTML element's height or width exceeds a specific value

This thread on Github demonstrates the method for using Cypress to verify that an element has a specific height. cy.get(mainMenu).should('have.css', 'height', '55px') How can I utilize Cypress to confirm that an element exce ...

Issue with declaring an interface in Angular 2/4

When working with Angular, it is common to declare an interface before parsing JSON data retrieved from a server. While this approach works well with small JSON structures, it becomes challenging when dealing with large files containing hundreds of key-val ...

Show the word "Delivered" on the submission button once the message has been sent successfully

Is it possible to customize the "Submit" button text on Contact Form 7 in WordPress? I would like it to change to "Sent" once the user has submitted the form, providing them with feedback that the message has been successfully sent. Thank you for your he ...

Determine the width of a dynamically generated div element in JavaScript using the createElement method

Currently, I am utilizing the JavaScript function createElement to generate a new div element and then assigning its innerHTML. Following that action, I am attempting to determine the necessary width required to display the div with all of its content. var ...

Creating a cutting-edge single page web application with the MERN stack: A comprehensive guide

After researching various articles on building dynamic apps with Express.js using technologies like EJS and Handlebars, I have decided that I want my web app to be dynamic and single page by utilizing React instead. My goal is to render templates and inse ...

Unable to refresh HTML table using Vuex data in Vue JS

I am new to working with Vue.js and particularly Nuxt. I am facing an issue with updating a table using data fetched from the backend. Even though I can see the data in the Vuex tab when the page loads, I cannot seem to populate the table with it. The func ...

Issues with loading Angular 9 application on Internet Explorer 11

Having trouble with my app not loading in IE 11 after adding ngx-treeview. Encountering the error (SCRIPT1002: Syntax error), Script Error Error point in vendor.js Unsure how to resolve this issue. Works fine in chrome and firefox, but in IE11 all I se ...

Is it possible to activate the nearby dropdown based on the user's selection?

On my html webpage, I have a form that consists of three dropdown menus each with different options: The first dropdown (A) includes choices from 1 to 6, as well as 'not set'. The second dropdown (B) allows selections from 1 to 7, and also has ...

How can I achieve the functionality of an image changing when clicked and going back to its original state upon release with the help of HTML

I am facing an issue with styling an element to look like a button and function as a clickable link. My goal is to create a visual effect of a pressed button when it is clicked, while also loading the target page. For reference, here is a (non-working) J ...

What is the most effective method for generating dial images through programming?

Currently, I am in the process of creating a website that makes use of variously sized and styled dials to indicate progress. The filled portion of the dial represents how close an item is to being 100% complete. I am seeking a universal solution that wil ...

How to Implement Recursive Function Calls in JavaScript?

My goal is to create an interactive function using HTML and jQuery. I am utilizing jQuery to update and change attributes and text for a more seamless experience without page reloads, especially since it's not image-heavy. I have structured "scenes" ...