What is causing the peculiar appearance of the shadows on these items in Three.js?

Currently, I am delving into the realm of three.js.

To enhance my understanding, I decided to practice with an example. However, the shadow effects on the objects seem off or rather unusual.

https://i.sstatic.net/pfg6E.jpg

The expected outcome should resemble the one shown in this image (taken from an older tutorial):

https://i.sstatic.net/Ewhpr.jpg

Below is the code snippet:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/89/three.js"></script>

<div id="WebGL-salida">
</div>

<script type="text/javascript">
  $(function() {
    var scene = new THREE.Scene();

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

    var renderer = new THREE.WebGLRenderer();

    var color = new THREE.Color("rgb(200, 250, 250)");
    renderer.setClearColor(new THREE.Color(color));
    renderer.setSize(window.innerWidth, window.innerHeight);

    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap


    var ejesAyuda = new THREE.AxesHelper(20); 
    scene.add(ejesAyuda);

    var planeGeometry = new THREE.PlaneGeometry(60, 20);

    var planeMaterial = new THREE.MeshLambertMaterial({
      color: 0xcccccc
    });

    var plane = new THREE.Mesh(planeGeometry, planeMaterial);

    plane.receiveShadow = true;


    plane.rotation.x = -0.5 * Math.PI; 
    plane.position.x = 15;
    plane.position.y = 0;
    plane.position.z = 0;

    scene.add(plane);


    var cubeGeometry = new THREE.CubeGeometry(4, 4, 4);


    var cubeMaterial = new THREE.MeshLambertMaterial({
      color: 0xff0000
    });
    var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);

    cube.castShadow = true; 

    cube.position.x = -4;
    cube.position.y = 3;
    cube.position.z = 0;

    scene.add(cube);

    var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
    var sphereMaterial = new THREE.MeshLambertMaterial({
      color: 0x7777ff
    });
    var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);

    sphere.castShadow = true; 

    sphere.position.x = 20;
    sphere.position.y = 4;
    sphere.position.z = 2;
    scene.add(sphere);

    camera.position.x = -30;
    camera.position.y = 40;
    camera.position.z = 30;
    camera.lookAt(scene.position);

    var spotLight = new THREE.SpotLight(0xffffff, 0.8);
    spotLight.position.set(-40, 60, -10);
    spotLight.castShadow = true;
    scene.add(spotLight);

    $("#WebGL-salida").append(renderer.domElement);
    renderer.render(scene, camera);
  });
</script>

If you have any recommendations for a helpful three.js tutorial or course suitable for beginners (even if it's not free), please share them. As a web developer eager to explore the world of WebGL, your guidance would be immensely valuable!

Answer №1

To enhance the shadow quality, consider increasing the size of the shadow map by adjusting the resolution (refer to SpotLight):

var spotLight = new THREE.SpotLight(0xffffff, 0.8);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true;
    
spotLight.shadow.mapSize.width = 2048;
spotLight.shadow.mapSize.height = 2048;

You can elevate the quality further by confining the light cone angle of the spotlight:

spotLight.angle = Math.PI / 8.0;

Combined, these two enhancements result in a higher resolution for the shadow map and a reduced mapped area. Consequently, more pixels from the shadow map are projected onto a smaller scene area, leading to improved quality.

Refer to the following code snippet:

var renderer, camera, scene, controls;

var init = function (){
  scene  = new THREE.Scene();
  camera = new THREE.PerspectiveCamera (45, window.innerWidth/window.innerHeight, 0.1, 1000); 
  renderer = new THREE.WebGLRenderer(); 
  controls = new THREE.OrbitControls( camera, renderer.domElement );

  var color = new THREE.Color("rgb(200, 250, 250)");
  renderer.setClearColor(new THREE.Color(color));
  renderer.setSize(window.innerWidth, window.innerHeight);

  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap

  var ejesAyuda = new THREE.AxesHelper(20); 
  scene.add(ejesAyuda); 

  var planeGeometry = new THREE.PlaneGeometry(60, 20);
  var planeMaterial = new THREE.MeshLambertMaterial({color: 0xcccccc});

  var plane = new THREE.Mesh(planeGeometry, planeMaterial);
  plane.receiveShadow = true;
  plane.rotation.x = -0.5*Math.PI; 
  plane.position.x = 15;
  plane.position.y = 0;
  plane.position.z = 0;
  scene.add(plane);

  var cubeGeometry = new THREE.BoxGeometry( 4, 4, 4);
  var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff0000});
  var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
  cube.castShadow = true; 
  cube.position.x= -4;
  cube.position.y = 3;
  cube.position.z = 0;
  scene.add(cube);

  var sphereGeometry = new THREE.SphereGeometry (4, 20, 20);
  var sphereMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff});
  var sphere = new THREE.Mesh (sphereGeometry, sphereMaterial);
  sphere.castShadow = true; 
  sphere.position.x = 20;
  sphere.position.y = 4;
  sphere.position.z = 2;
  scene.add(sphere);

  camera.position.x = -30;
  camera.position.y = 40;
  camera.position.z = 30;
  camera.lookAt(scene.position); 

  var spotLight = new THREE.SpotLight(0xffffff, 0.8);
  spotLight.position.set(-40, 60, -10);
  spotLight.castShadow = true;
  spotLight.angle = Math.PI / 8.0;
  spotLight.shadow.mapSize.width = 2048;
  spotLight.shadow.mapSize.height = 2048;
  scene.add(spotLight);

  document.getElementById("WebGL-salida").append(renderer.domElement);
  resize();
  window.onresize = resize;
};

function animate() {
    requestAnimationFrame( animate );
    renderer.render( scene, camera );
}

function resize() {
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
}

init();
animate();
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="89fde1fbececc9b9a7b8bdbf">[email protected]</a>/build/three.js"></script>
<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="70041802151530405e414446">[email protected]</a>/examples/js/controls/OrbitControls.js"></script>
<div id="WebGL-salida">
</div>

Answer №2

Another option is to boost the number of segments. Currently, you are working with

let sphereGeometry = new THREE.SphereGeometry(4, 20, 20);

The value '20' for both parameters may not be optimal for high-quality rendering. I usually prefer using 32, 32

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

Executing a JavaScript function to submit an HTML form and circumvent the default behavior

Currently, I am utilizing a virtual keyboard that was created using JavaScript for the user interface on an embedded system. If you would like to access the source code for this virtual keyboard, you can find it here: https://codepen.io/dcode-software/pen ...

problem with floating phone

I'm encountering an issue with a script that displays a balloon for first-time visitors. The balloon only shows once when visiting the website from an iPad, but on an iPhone, it keeps appearing whenever the link is accessed. Upon inspecting the code, ...

Using enums in templates - The statement will constantly evaluate to 'true' because the categories of 'RequestStatus.Done' and 'RequestStatus.Rejected' do not intersect at all

My HTML code is in this format, and I want to check the value to display a different form based on certain conditions. I have set up the condition in my HTML like this: requestStatus = RequestStatus; <ng-container *ngIf=" (model.lastStat ...

Vue messaging application fails to display data upon mounting

Recently, I've been experimenting with different Vue chat libraries and encountered this interesting piece of code: <template> <p>{{ this.users[0] }}</p> </template> <script> export default { data() { return ...

Monitor the scrolling progress across four separate HTML pages without the percentage decreasing when scrolling back up

I am in the process of developing an e-learning platform and I would like to include a scrolling indicator that visually represents the progress of pages read. The website consists of four HTML pages, with each page corresponding to 25% of the scroll bar. ...

Exploring the power of onMounted for handling asynchronous tasks in Nuxt 3

I'm having trouble figuring out what's going on with this code snippet – the value of the repository ref doesn't seem to be updating. Even though I used async/await, it doesn't appear to be functioning correctly. The repository is ju ...

Steps to generating a dynamic fabric canvas upon opening a new window

There seems to be an issue with the code below. When I click the button next to the canvas, my intention is to have a new window open displaying the canvas in full view. However, it's not working as expected. Can someone please assist me in troublesho ...

Modify the size of the fabricjs text box to perfectly match the chosen text

I'm currently facing an issue with my code snippet that is supposed to create a new Textbox: new fabric.Textbox('Add some text') The problem I'm encountering is that the text box appears too small compared to its content: https://i.s ...

When the Button is clicked, the component utilizing the Router fails to appear

My current task involves creating a page where users can choose between two options: Button 1 leads to TestOption.js, while Button 2 redirects to TestOption2 (currently using TestOption for testing purposes). The default landing page is SelectionPage. The ...

Tips on utilizing normalizr to flatten an array with various object types?

Upon receiving a JSON response from the server, it typically looks something like this: { data: [ { id: 1, type: 'person', emails: [ { id: 1 }, { id: 3 } ], phones: [] }, { id: 2, type: 'person', emails: [ { id: 2 } ], p ...

unable to bypass mongoose nested documents

I'm currently working with mongoose and node in an effort to paginate data from sub-documents. I've managed to limit the subdocuments, but skipping them seems to be a challenge. The specific versions I'm using are: Mongo 3.0.0 Node 0.10.3 ...

If the condition evaluates to true, then all other conditions will be skipped

xyz.html <p id = x ngClick = close($event)></p> <p id = y ngClick = close($event)></p> <p id = z ngClick = close($event)></p> <h1 id = a ngClick = close()></h1> xyz.js function close(event) if (event.targe ...

Troublesome Outcome: Next.js Forms Function Smoothly in Local Environment but Encounter Issues when Deploy

I am facing a puzzling issue with my Next.js application. Despite successfully using Nodemailer locally to submit forms, I keep encountering an "Internal Server Error" when deploying to cPanel. I have tried different solutions, but the problem persists. As ...

What is the process for adding JSON object data to an existing JSON file using an HTML input form?

I have a JSON file named "WordMeanings.json" on my computer with the following data: { "WordMeanings": [ { "bangla": "ei je", "example": "Hi there!", "english" ...

Angular 5 - Strategies for excluding specific properties from Observable updates

Currently, I am in the process of developing a webpage where users can view and like various videos. The video content and user likes are stored in a database, and I have implemented two Angular services for handling data retrieval and storage. However, I ...

Optimizing the management of optional post fields in an Express.js application

When creating an endpoint with Express that includes both mandatory and non-mandatory fields in the post request, what is the optimal strategy for handling this? Would it be best to use something like if (field exists in req.body) { set variable } else { ...

Creating a popup window for multiple file uploads in ASP.NET using VB.NET

Hello, I am trying to create a popup window with multiple file upload options. When the 'UploadDocument' button is clicked, I want the popup window to appear. I have attempted to do this but it is not working as expected. Currently, the popup ap ...

JQuery's accordion with the heightStyle set to "fill" is causing a vertical scrollbar

I recently integrated the JQuery accordion effect into my website and specified the heightStyle: fill so that it would occupy the entire window. However, it seems to be taking up an additional 1 or 2 pixels, causing the vertical scroll bar to appear. I su ...

Dynamic material variation with Three.js JSON technology

Recently, I exported a json file from Blender. I'm now looking to update the textures for specific materials on the json models. Can anyone guide me on how to achieve this? ...

What is the best way to show a macOS progress pie loading icon alongside files?

While using macOS, a pie loading icon appears next to a file during downloading or transferring. To illustrate, here is an example of a file being downloaded from Chrome: https://i.sstatic.net/8jS4X.png I am interested in implementing a similar feature i ...