The lighting discrepancies on Three.js planes do not align with the material used

After creating a shape in Three.Js by combining a curved plane and two flat planes, I encountered an issue. While the vertical plane and curved plane blend seamlessly, there is a noticeable harsh line where the horizontal plane meets the curve. The lighting appears misaligned, and I'm aiming for the three planes to present themselves as a single object with smooth shading. Could I be making an error in my approach?

CodePen link

addShape() {
                var radius =58, height=100, startAngle=THREE.Math.degToRad(0), endAngle=THREE.Math.degToRad(90), horizontalSegments=25, verticalSegments=25;
                var width = radius * 2 * Math.PI;
                var plane = new THREE.PlaneGeometry(width, height, horizontalSegments, verticalSegments);
                var index = 0;

                for(var i=0; i<=verticalSegments; i++) {
                    for(var j=0; j<=horizontalSegments; j++) {
                        var angle = startAngle + (j/horizontalSegments)*(endAngle - startAngle);
                        plane.vertices[index].z = radius * Math.cos(angle);
                        plane.vertices[index].x = radius * Math.sin(angle);
                        index++;
                    }
                }
                var material = new THREE.MeshLambertMaterial({color: 0xa2cddd, side: THREE.DoubleSide});
                var mesh = new THREE.Object3D();
                var curve = new THREE.Mesh(plane, material);
                curve.rotation.z = THREE.Math.degToRad(-90)


                var plane1 = new THREE.PlaneGeometry(height, height, horizontalSegments, verticalSegments);
                var side1 = new THREE.Mesh(plane1, material);
                side1.rotation.z = THREE.Math.degToRad(270)
                side1.position.z = radius;
                side1.position.x = -radius * 0.85;

                var plane2 = new THREE.PlaneGeometry(height, height, 1, 1);
                var side2 = new THREE.Mesh(plane2);
                side2.rotation.y = THREE.Math.degToRad(90)
                side2.position.x = radius * 1.0
                side2.position.z = -radius * 0.8;

                plane.mergeMesh(side1);
                plane.mergeMesh(side2);
                mesh.rotation.y = THREE.Math.degToRad(180);
                mesh.add(curve);
                this.mesh = mesh;
                this.scene.add(mesh);
            }
addLight() {
    let light1 = new THREE.PointLight(0xffffff, 1, 200);
    light1.position.set(0, 20, 10);
    this.scene.add(light1);

    let light2 = new THREE.AmbientLight(0x404040); // soft white light
    this.scene.add(light2);
  }

https://i.sstatic.net/LuvFP.png

Answer №1

To begin with, the first step is to calculate the vertex normals. You can achieve this by using the

THREE.Geometry.computeVertexNormals
method.
Refer to THREE.Geometry for more information.

plane.computeVertexNormals();


Another approach to simplify the process is to create the THREE.PlaneGeometry in a single band. This way, you have the flexibility to choose the start angle and end angle according to your preference, ensuring seamless connection between the planes at the start and end points with the curved segment:

var radius = 58, height = 100,
    startAngle = THREE.Math.degToRad(-20),
    endAngle = THREE.Math.degToRad(110),
    horSegs = 25, vertSegs = 25,
    startLen = 100, endLen = 100;
var width = startLen + endLen + radius * (endAngle-startAngle);
var plane = new THREE.PlaneGeometry( width, height, horSegs+2, vertSegs);

var index = 0;
for (var i = 0; i <= vertSegs; i++) {
  plane.vertices[index].z = radius * Math.cos(startAngle) + startLen * Math.sin(startAngle);
  plane.vertices[index].x = radius * Math.sin(startAngle) - startLen * Math.cos(startAngle);
  index++;
  for (var j = 0; j <= horSegs; j++) {
    var angle = startAngle + j / horSegs * (endAngle - startAngle);
    plane.vertices[index].z = radius * Math.cos(angle);
    plane.vertices[index].x = radius * Math.sin(angle);
    index++;
  }
  plane.vertices[index].z = radius * Math.cos(endAngle) - endLen * Math.sin(endAngle);
  plane.vertices[index].x = radius * Math.sin(endAngle) + endLen * Math.cos(endAngle);
  index++;
}
plane.computeVertexNormals();


Check out the code snippet below:

class World {
  constructor() {
    this.scene = new THREE.Scene();
    this.renderer = new THREE.WebGLRenderer();
    this.renderer.setClearColor(0x000000);
    document.body.appendChild(this.renderer.domElement);
    this.resize();

    // this.addCube();
    this.addShape();
    this.addLight();

    requestAnimationFrame(this.render.bind(this));
    window.addEventListener("resize", this.resize.bind(this));
  }

  update() {
    if (this.cube) this.cube.rotation.y += 0.01;
    if (this.mesh) this.mesh.rotation.y += 0.01;
  }

  addLight() {
    let light1 = new THREE.PointLight(0xffffff, 1, 200);
    light1.position.set(0, 20, 10);
    this.scene.add(light1);

    let light2 = new THREE.AmbientLight(0x404040); // soft white light
    this.scene.add(light2);
  }

  
  addShape() {
    var radius = 58, height = 100,
        startAngle = THREE.Math.degToRad(-20),
        endAngle = THREE.Math.degToRad(110),
        horSegs = 25, vertSegs = 25,
        startLen = 100, endLen = 100;
    var width = startLen + endLen + radius * (endAngle-startAngle);
    var plane = new THREE.PlaneGeometry( width, height, horSegs+2, vertSegs);
    
    var index = 0;
    for (var i = 0; i <= vertSegs; i++) {
      plane.vertices[index].z = radius * Math.cos(startAngle) + startLen * Math.sin(startAngle);
      plane.vertices[index].x = radius * Math.sin(startAngle) - startLen * Math.cos(startAngle);
      index++;
      for (var j = 0; j <= horSegs; j++) {
        var angle = startAngle + j / horSegs * (endAngle - startAngle);
        plane.vertices[index].z = radius * Math.cos(angle);
        plane.vertices[index].x = radius * Math.sin(angle);
        index++;
      }
      plane.vertices[index].z = radius * Math.cos(endAngle) - endLen * Math.sin(endAngle);
      plane.vertices[index].x = radius * Math.sin(endAngle) + endLen * Math.cos(endAngle);
      index++;
    }
    plane.computeVertexNormals();

    var material = new THREE.MeshLambertMaterial({
      color: 0xa2cddd,
      side: THREE.DoubleSide
    });
    
    var mesh = new THREE.Object3D();
    var curve = new THREE.Mesh(plane, material);
    curve.rotation.z = THREE.Math.degToRad(-90);

    mesh.add(curve);
    this.mesh = mesh;
    this.scene.add(mesh);
  }

  render() {
    this.update();
    requestAnimationFrame(this.render.bind(this));
    this.renderer.render(this.scene, this.camera);
  }

  resize() {
    this.camera = new THREE.PerspectiveCamera(
      75,
      window.innerWidth / window.innerHeight,
      0.1,
      1000
    );

    this.camera.position.z = 200;

    this.renderer.setSize(window.innerWidth, window.innerHeight);
  }
}

var _w = new World();
<script src="https://threejs.org/build/three.min.js"></script>

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

How can I create a fading trail effect in Three.js that diminishes over time?

I am interested in achieving a similar effect to the example I found on this website: However, my goal is to have the old trail gradually fade into the background over time instead of cluttering the screen with persistent marks. I discovered that by usin ...

webstorm error: unresolved function or method when using npm azure-storage modules

Encountering a problem with WebStorm IDE when using azure-storage library as it is unable to find the correct methods for intelligent coding (although the code runs without errors). View image of unresolved function or method Appreciate any help you can ...

Is there a way to detect duplicate usernames in a form without actually submitting the form, and then automatically focus on the username field if a duplicate is

I need help with a user registration form that I am creating. I want the form to automatically search for an existing username as soon as the user starts typing in the username field. If the username already exists, I want the field to receive focus. In my ...

Debugging a script designed to output a value of 1 if the Mean equals the Mode, and 0 if they are not equal

As a beginner coder, I am working on a code that should return 1 if the mean is equal to the mode and 0 otherwise. However, my current code only outputs 0 even when it should be returning 1. Any guidance or assistance in identifying where I may have made ...

Countdown Timer App using Flask

I'm currently working on a Flask-based game that involves countdown timers for each round. My goal is to have the timer decrease by 1 second every round without the need to reload the page. I've tried using time.sleep in my Python code to update ...

What is the best way to refresh a navigation bar after making an API request, such as when using Google Sign-In?

Struggling to grasp the hook concept in this particular scenario: The flow goes like this - the user logs in with Google, which updates the session state. Consequently, the visitorType state transitions from 'viewer' to 'buyside'... A ...

Unable to modify $scope value within the directive

This special function triggers once ng-repeat is done iterating through the list using scope.$last: simpleSearchApp.directive('afterResults', function($document) { return function(scope, element, attrs) { if (scope.$last){ scope.windowHe ...

Output the initial value and subsequently disregard any values emitted during a specified time interval

Check out my code snippet: app.component.ts notifier$ = new BehaviorSubject<any>({}); notify() { this.notifier$.next({}); } app.component.html <div (scroll)="notify()"></div> <child-component [inp]="notifier$ | async" /> ...

Is the variable leaping to a superior scope?

A few days back, I encountered a strange bug in my code that has left me puzzled. It appears that a variable declared within a narrower scope is somehow leaking into a broader one. Any insights into what might be going wrong here? Here's a simplified ...

Guide on implementing a bootstrap loading spinner during the process of retrieving data from an external api

Is there a way to use the bootstrap load spinner to mask the delayed information retrieval from a url and enhance the website's interactivity? ...

Executing React Fetch API Twice upon loading the page

Double-fetching Issue with React Fetch API on Initial Page Load import React, { useState, useEffect } from 'react' import axios from 'axios'; import { Grid, Paper, TextField } from '@mui/material' import PersonOut ...

Error in Typescript: Attempting to access the property 'set' of an undefined value

Currently, I am in the process of setting up a basic example of push notifications on Android using Nativescript and Typescript. Although my code may seem a bit messy, I am struggling with properly rewriting "var Observable = require("data/observable");" a ...

Setting a Vue child component class directly from its parent component

In my Vue component, I have multiple child components that are inputs of the same type. The number of inputs can vary depending on the user, but there is a rule that at least one input must be filled. For example, there could be 5 inputs with 4 of them emp ...

Is there a replacement for findIndex in Internet Explorer?

I am currently working on a code snippet for smooth navigation scroll to different sections. var lastId; var topMenu = $(".community-nav"); var topMenuHeight = topMenu.outerHeight() - 19; if(window.matchMedia("(max-width: 768px)").matches) ...

Tips for saving HTML data locally

Is there a way to persist my HTML element tag data even after the user closes the browser? For example: <div class="classOne" data-random="50"> If I use jQuery to change the data attribute like this: $(".classOne").attr("data-random","40") How ca ...

Creating React elements dynamically with material-ui can be done by utilizing state expressions in properties. This allows for the generation

Within a functional component, I aim to dynamically generate material-ui context menus by utilizing a state object: let legendContextMenuStatesObject = {}; for (let key of keys) { legendContextMenuStatesObject[key] = initialState; } const [lege ...

Contrasting ./ and $ in React project module imports

The creator of this particular project has utilized a different path to import a component: import { client } from '$lib/graphql-client' I'm curious: What is the significance of the $ symbol in this case? How does it differ from something ...

Transferring properties from React Router to child components on the server side

For my Isomorphic app using React, react-router v3, and material-ui, it's crucial to pass the client's user agent to the theme for server-side rendering. This is necessary for MUI to properly prefix inline styles. Initially, the root component o ...

Will a JavaScript source map file continue to function properly even after the source code file has been minified?

My experience I specialize in utilizing TypeScript and Visual Studio to transform highly organized code into functional JavaScript. My skills involve configuring the project and Visual Studio to perform various tasks: Merging multiple compiled JavaScrip ...

Adding new pages in express.js without increasing the complexity of the routes

I am currently developing a project using Express.js and have been enjoying the experience. While the site is running smoothly, I am now looking for a way to allow users to contribute by adding pages to the site through either a form with set fields or by ...