Enhancing speed and efficiency through the utilization of Three.js

I'm currently working on my first Three.js / WebGL application, and while it runs smoothly on my PC (Chrome), I've noticed that the framerate tends to drop below 30 frames per second on other PCs.

Despite the application not being overly complex, I would appreciate any tips you have to offer on how to enhance its performance. You can find a version of the app here:

www.wrodewald.de/StackOverflowExample/

The main element in the application is a dynamic plane with 64² vertices that morphs. It utilizes a matrix to store a static heightmap and wavemap, which is updated every frame to recalibrate itself using filters to maintain consistency among neighboring vertices. As a result, each frame requires updating the plane's color and vertex position, potentially contributing to the performance issue.

The secondary object, a rhombus, should not pose a problem as it is static, only moving slightly.

In terms of lighting, there are three types present (ambient, directional, spherical) without shadows, along with a tilt shift shader and vignette shader.

The following functions are executed per frame:

var render = function() {
    requestAnimationFrame( render );
    var time = clock.getDelta();

    world.updateWorld(time);
    diamond.rotate(time);
    diamond.update(time);
    control.updateCamera(camera, time);
    composer.render();      
    stats.update();
}

This is what world.updateWorld(time) entails:

// Within world.updateWorld(time):
// accmap stores acceleration and wavemap stores position
// this.mapSize indicates the plane's size in vertices (64)

// Update Acceleration Map
for(var iX = 1; iX < (this.mapSize-1); iX++) {
    for(var iY = 1; iY < (this.mapSize-1); iY++) {
        accmap[iX][iY] -=  dT * (wavemap[iX][iY]) * Math.abs(wavemap[iX][iY]);
    }   
}

// Smooth Acceleration Map
for(var iX = 1; iX < (this.mapSize-1); iX++) {
    for(var iY = 1; iY < (this.mapSize-1); iY++) {
        // Calculation omitted for brevity
    }
}

// Update Wave Map
// Wave map update and vertex calculation code provided

Here are the "diamond" functions:

this.rotate = function(dT) {
    // Rotation logic explained
}

this.update = function(dT) {
    // Color interpolation logic described
}

Can you identify any specific reasons causing the inconsistent framerate in the application?

Answer №1

Update:

I have identified 2 key areas for improvement:

Implementing Plane Updates with GPU

Speedup: High

Lets focus on your code in plane.js

    timer += dT;
    if(timer > 0.1) {           
        var x = 2 + Math.floor(Math.random() * (this.mapSize - 4));
        var y = 2 + Math.floor(Math.random() * (this.mapSize - 4));
        //accmap[x][y] += 30000 * Math.random () - 15000
    }


    // UPDATE ACCELERATION MAP
    for(var iX = 1; iX < (this.mapSize-1); iX++) {
        for(var iY = 1; iY < (this.mapSize-1); iY++) {
            accmap[iX][iY] -=  dT * (wavemap[iX][iY]) * Math.abs(wavemap[iX][iY]);
        }   
    }

You are currently updating 4096 vertices every 17 ms with the CPU. You are not utilizing the advantages of GPU processing. Here's how it can be done more effectively:

  • First, create buffers for vertex positions, normals, texture coordinates, indices, etc. This collection is referred to as a mesh.
  • Then, create a model consisting of one or more meshes along with a modelViewMatrix. The matrix 4x4 represents the position, rotation, and scale of the model.
  • For each render, perform this operation in the vertex shader:

    "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",

    This snippet is from your cg/shaders/VerticalTiltShiftShader.js file

  • If you want to rotate your plane, instead of multiplying each vertex, multiply the model matrix once using three.js function:

    projectionMatrix.makeRotationY(dT);

    Each vertex will then be multiplied by this matrix in the vertex shader, resulting in much faster processing.

Javascript Coding Style

Speedup: None - Medium, but it enhances coding efficiency

Let's take a look at your plane.js for illustration.

// Interface definition
function PlaneWorld () {
    this.init = function() {};
    this.updateVertices = function() {};
    this.updateWorld = function(dT) {};
    // ... and more
}

// Instantiation somewhere else:
var world = new PlaneWorld();

If your project only involves one plane, treating it as a singleton is acceptable. But when dealing with multiple planes, all functions are recreated for each instance (new PlaneWorld()). A better approach would be:

function PlaneWorld () {
    ...
}

PlaneWorld.prototype.init = function() {};    
PlaneWorld.prototype.updateVertices = function() {};    
PlaneWorld.prototype.updateWorld = function(dT) {};  
// ... and more

var world = new PlaneWorld();

// Method calls remain the same
world.updateVertices();

Or a more advanced version using an anonymous function:

var PlaneWorld = (function() {

    // Private static variables here

    var PlaneWorld = function () {
        ...
    }

    PlaneWorld.prototype = {
        init: function() {},
        updateVertices: function() {},
        updateWorld: function(dT) {} 
        // ... and more
    }

    return PlaneWorld();
})();

var world = new PlaneWorld();
// Method calls remain the same
world.updateVertices();

This optimization reduces the cost of creating new instances. It's important that every instance shares the same mesh while having its own modelViewMatrix.

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

Guide: Executing an Angular web application using Express

Encountering an issue with an AngularJS (v1) project running on an express server. The problem arises when the application displays a blank "ng-view" along with this error message. Strangely enough, the controller functions correctly. Interestingly, runnin ...

Specification for dynamically changing CSS in Bootstrap tooltips

The default tooltip template in Bootstrap is a black box with white text. However, it is possible to modify the .tooltip css class to have a white background instead: .tooltip{position:absolute;display:none;color:#333;text-align:left;font-size:0.9em;width ...

Using jQuery to Populate a Dropdown List in ASP.NET Core 2.1

I'm trying to implement a function where clicking a button creates a new div row with multiple dropdowns. How can I populate these dropdowns using the viewmodel loaded into my view? @model App.Data.ViewModels.FilterDocumentsViewModel <button type ...

How can you locate the position of a selector within a parent or set using jQuery

Could someone please help me determine the position of the current selector within its parent using jQuery? I need this information in order to effectively use the .insertAfter() and .insertBefore() methods to rearrange elements within their nested structu ...

Effectively detect the 'scrollend' event on mobile devices

When implementing the -webkit-overflow-scrolling: touch; style on a mobile element, dealing with scroll events can be quite challenging as they are triggered by various actions such as 'flicking', 'panning' and when the scroll comes to ...

Google Cloud Platform (GCP) reported a Stripe webhook error stating that no matching signatures were found for the expected signature

Current Stripe version: "8.107.0" I am encountering an issue with Stripe webhook verification whenever I deploy my webhook on Google Cloud Platform (GCP). Despite trying various methods to include the raw body in the signature, including the cod ...

Having trouble running Webpack Config in React.js 2020?

After running 'watch' to execute webpack dev server with the command "watch": "webpack-dev-server --progress", there were no issues detected in the terminal. However, upon navigating to http://localhost:8080, an error message 'Cannot Get&apo ...

Get rid of all numbers from a jQuery selection except for the first and last

I am dealing with an array let numberArray = ["500", "600", "700", "800", "900", "1000", "1100", "1200"] My objective is to remove all elements except for the first and last ones. The challenge arises when the array contains only one value, as I must ens ...

Develop a regular expression tailored for jQuery validation purposes

I'm working with jQuery validation in my web form and I need to validate a text field with specific criteria: 1. Must contain a number. 2. The letters 'a' or 'A', 'b' or 'B' are allowed after the number. 3. Th ...

Unable to display using a simulated array in ReactJS/JS

It's strange that this code only renders once on the screen instead of ten times. {[Array(10)].map((e,i)=>{ return( <div key={i} className="w-[250px] slide flex align-center p-[15px]"> ...

Tool to stop automatic logouts on websites

In the web application where I work, users are automatically logged out after a period of inactivity. Unfortunately, I am unable to control this feature. The code responsible for logging the user out is as follows: var windoc = window.document; var timeou ...

Use PipeTransform to apply multiple filters simultaneously

Is it possible to apply multiple filters with PipeTransform? I attempted the following: posts; postss; transform(items: any[]): any[] { if (items && items.length) this.posts = items.filter(it => it.library = it.library ...

Can we modify the styling of elements in Angular based on an object property?

I have a class named Task with the following properties: export class Task { name: string; state: number; } Within my component.ts file, I have an array of objects consisting of instances of the Task class (tasks). When displaying them in the tem ...

Preserve the data from various select boxes using their respective ids

I am facing an issue with handling select boxes within an ng-repeat loop in my AngularJS application. Each object has one or more select boxes, and the user needs to select 3 priorities from these boxes and save them by clicking a button. In the save funct ...

How can we stop the interval when the object is no longer in use?

I am working on a client class that communicates with a server via WebSocket. I want to set up a system that regularly pings the server to measure latency. However, I'm worried that using setInterval within the class might cause issues if it continues ...

Chrome browser experiencing a disappearing vertical scroll bar issue on a Bootstrap Tab

<div class="tabs-wrap left relative nomargin" id="tabs"> <ul class="nav ultab" id="fram"> <li class="active"><a href="#history" data-toggle="tab" id="history1" >History< ...

Vue.js | Dynamically add a new key and value pair to an array being built using $ajax

Currently, our goal is to implement a calendar using Vue.js where the values are fetched via an $ajax call. Following this, I aim to manipulate some of the children's data in Vue.js. I've spent over 2 hours trying to discover a suitable method f ...

Utilizing the Google Maps JavaScript API in npm to generate route directions

After not working with Javascript for a couple of years, I decided to try loading the Google Maps Javascript API npm package in order to display a map and draw directions between two points (Lille and Biarritz, passing through Paris and Bordeaux). Unfortun ...

Having issues with the functionality of AngularJS ng-show

I am trying to dynamically show and hide a button using the ng-show directive. Below is the HTML code I have written: <button class="btn btn-info" ng-show="editBtn">Save Edit <span class="glyphicon glyphicon-ok"></span> < ...

Stop modal content from automatically opening

Is there a way to prevent the content inside from opening before it is clicked? Every time I try to create a modal or dropdown list menu, the content always opens automatically before being clicked. Why does this happen? var modal = document.getElemen ...