Creating unique sizes for each quad in Three.js using InstancedBufferGeometry and ShaderMaterial

I'm working on creating a visually dynamic scene filled with points of varying widths and heights. The challenge I'm facing is figuring out how to manipulate the vertices in the vertex shader to achieve customized sizes for each point. Here's a glimpse of the scene setup:

// setting up the scene
var scene = new THREE.Scene();
scene.background = new THREE.Color(0xaaaaaa);

// configuring the camera
var aspectRatio = window.innerWidth / window.innerHeight;
var camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.1, 10000);
camera.position.set(0, 1, -10);

// initializing the renderer
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio); // <3 retina
renderer.setSize(window.innerWidth, window.innerHeight); // canvas size
document.body.appendChild(renderer.domElement);

// implementing controls
var controls = new THREE.TrackballControls(camera, renderer.domElement);

// adding lighting effects
var ambientLight = new THREE.AmbientLight(0xeeeeee);
scene.add(ambientLight);

/**
* Placing the points
**/

var BA = THREE.BufferAttribute;
var IBA = THREE.InstancedBufferAttribute;
var geometry  = new THREE.InstancedBufferGeometry();

var n = 10000, // number of observations
    rootN = n**(1/2),
    cellSize = 20,
    translations = new Float32Array(n * 3),
    widths = new Float32Array(n),
    heights = new Float32Array(n),
    translationIterator = 0,
    widthIterator = 0,
    heightIterator = 0;
for (var i=0; i<n*3; i++) {
  translations[translationIterator++] = (Math.random() * n) - (Math.random() * n);
  translations[translationIterator++] = (Math.random() * n) - (Math.random() * n);
  translations[translationIterator++] = (Math.random() * n) - (Math.random() * n);
  widths[widthIterator++] = Math.random() * 20;
  heights[heightIterator++] = Math.random() * 20;
}

// defining dimensions for the template box
var size = 10,
    verts = [
  0, 0, 0, // lower left
  size, 0, 0, // lower right
  size, size, 0, // upper right
  0, size, 0, // upper left
]

var positionAttr = new BA(new Float32Array(verts), 3),
    translationAttr = new IBA(translations, 3, true, 1),
    widthAttr = new IBA(widths, 1, true, 1),
    heightAttr = new IBA(heights, 1, true, 1);

// creating triangles using distinct vertices
geometry.setIndex([0,1,2, 2,3,0])

geometry.addAttribute('position', positionAttr);
geometry.addAttribute('translation', translationAttr);
geometry.addAttribute('width', widthAttr);
geometry.addAttribute('height', heightAttr);

var material = new THREE.RawShaderMaterial({
  vertexShader: document.getElementById('vertex-shader').textContent,
  fragmentShader: document.getElementById('fragment-shader').textContent,
});
material.side = THREE.DoubleSide;
var mesh = new THREE.Mesh(geometry, material);
mesh.frustumCulled = false; 
scene.add(mesh);

// rendering loop
function render() {
  requestAnimationFrame(render);
  renderer.render(scene, camera);
  controls.update();
};

// displaying some geometries
var geometry = new THREE.TorusGeometry(10, 3, 16, 100);
var material = new THREE.MeshNormalMaterial({ color: 0xffff00 });

render();
html, body { width: 100vw; height: 100vh; background: #000; }
body { margin: 0; overflow: hidden; }
canvas { width: 100vw; height: 100vh; }
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js'></script>
<script src='https://threejs.org/examples/js/controls/TrackballControls.js'></script>

<script type='x-shader/x-vertex' id='vertex-shader'>
precision highp float;

uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;

uniform vec3 cameraPosition;

attribute vec3 position; 
attribute vec3 translation; 
attribute float width;
attribute float height;

void main() {
  // adjusting point positions based on widths and heights
  vec3 pos = position + translation;
  gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
</script>

<script type='x-shader/x-fragment' id='fragment-shader'>
precision highp float;

void main() {
  gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
</script>

I'm seeking guidance on utilizing the width and height attributes effectively to ensure that each point adheres to its specified proportions. Is there a way to access the index of the vertex being drawn within its instance? Any tips or insights would be greatly appreciated!

Answer №1

One interesting capability of the vertex shader is the ability to manipulate point positions. Take a look at this example:

void main() {
  // set point position
  vec3 pos = position + translation;
  pos.x = pos.x * width;
  pos.y = pos.y * height;

  vec4 projected = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
  gl_Position = projected;
}

Here's the full snippet that demonstrates this functionality in action:

// generate a scene object
var scene = new THREE.Scene();
scene.background = new THREE.Color(0xaaaaaa);

// generate a camera
var aspectRatio = window.innerWidth / window.innerHeight;
var camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.1, 10000);
camera.position.set(0, 1, -10);

// generate a renderer
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio); // <3 retina
renderer.setSize(window.innerWidth, window.innerHeight); // canvas size
document.body.appendChild(renderer.domElement);

// generate controls
var controls = new THREE.TrackballControls(camera, renderer.domElement);

// generate some lights
var ambientLight = new THREE.AmbientLight(0xeeeeee);
scene.add(ambientLight);

/**
* Add the points
**/

var BA = THREE.BufferAttribute;
var IBA = THREE.InstancedBufferAttribute;
var geometry  = new THREE.InstancedBufferGeometry();

var n = 10000, 
    rootN = n**(1/2),
    cellSize = 20,
    translations = new Float32Array(n * 3),
    widths = new Float32Array(n),
    heights = new Float32Array(n),
    translationIterator = 0,
    widthIterator = 0,
    heightIterator = 0;
for (var i=0; i<n*3; i++) {
  translations[translationIterator++] = (Math.random() * n) - (Math.random() * n);
  translations[translationIterator++] = (Math.random() * n) - (Math.random() * n);
  translations[translationIterator++] = (Math.random() * n) - (Math.random() * n);
  widths[widthIterator++] = Math.random() * 20;
  heights[heightIterator++] = Math.random() * 20;
}

// coordinates for template box
var size = 10,
    verts = [
  0, 0, 0, 
  size, 0, 0, 
  size, size, 0, 
  0, size, 0, 
]

var positionAttr = new BA(new Float32Array(verts), 3),
    translationAttr = new IBA(translations, 3, true, 1),
    widthAttr = new IBA(widths, 1, true, 1),
    heightAttr = new IBA(heights, 1, true, 1);


geometry.setIndex([0,1,2, 2,3,0])

geometry.addAttribute('position', positionAttr);
geometry.addAttribute('translation', translationAttr);
geometry.addAttribute('width', widthAttr);
geometry.addAttribute('height', heightAttr);

var material = new THREE.RawShaderMaterial({
  vertexShader: document.getElementById('vertex-shader').textContent,
  fragmentShader: document.getElementById('fragment-shader').textContent,
});
material.side = THREE.DoubleSide;
var mesh = new THREE.Mesh(geometry, material);
mesh.frustumCulled = false; 
scene.add(mesh);

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

var geometry = new THREE.TorusGeometry(10, 3, 16, 100);
var material = new THREE.MeshNormalMaterial({ color: 0xffff00 });

render();
html, body { width: 100vw; height: 100vh; background: #000; }
body { margin: 0; overflow: hidden; }
canvas { width: 100vw; height: 100vh; }
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js'></script>
<script src='https://threejs.org/examples/js/controls/TrackballControls.js'></script>

<script type='x-shader/x-vertex' id='vertex-shader'>
precision highp float;

uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;

uniform vec3 cameraPosition;

attribute vec3 position; 
attribute vec3 translation; 
attribute float width;
attribute float height;

void main() {
  vec3 pos = position + translation;
  vec4 projected = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
  gl_Position = projected;
}
</script>

<script type='x-shader/x-fragment' id='fragment-shader'>
precision highp float;

void main() {
  gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
</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

Discover the steps to execute a continuous loop of animations on a singular component using framer-motion

I'm currently working on a website that incorporates framer-motion for animations One key component of the site is an image displayed as follows: <motion.img ref={scope} initial={{ x: -200 }} alt="pa ...

For an unknown reason, I am facing difficulties in using the Storage feature from @angular/fire in ANGULAR 16

Recently I started exploring Angular/Fire and decided to test out some of its features by creating a basic app. Firestore and authentication were working smoothly, but when I attempted to include Storage, an error message popped up: ERROR FirebaseError: ...

Issue encountered while trying to render an item from a state array in React

I encountered an issue with retrieving state data within the render function. Everything seems to work fine and displays the array when I utilize console.log(items) However, attempting to access the first item from the array results in an error console. ...

Tips for clearing a saved password in a browser using Angular.js and Javascript

There is an issue with the password and username fields in my Angular.js login page. When a user clicks on the 'remember me' option in their browser after logging in, the saved username and password are automatically displayed in the respective f ...

box tick does not alter appearance

I've been struggling with this seemingly simple issue for an hour now. I have a radio button set up with two buttons: <input class="form-control icheck" id="cash_prize" name="cash_prize" type="radio" value="1" style="position: absolute; opacity: 0 ...

What is the best method for deleting scripts to optimize for mobile responsiveness?

My current plugin.js file houses all my plugins for responsive design, but it is unnecessarily large and cumbersome for mobile devices. I am considering creating two separate plugin.js files to toggle between for mobile and desktop views. What are the r ...

Do we really need Renderer2 in Angular?

Angular utilizes the Renderer2 class to manipulate our view, acting as a protective shield between Angular and the DOM, making it possible for us to modify elements without directly interacting with the DOM ourselves. ElementRef provides another way to al ...

Implementing JQuery to Traverse Through JSON Data in AJAX Response

I am currently working on an AJAX call that retrieves JSON data from a query: <script> function retrieveTrips(){ // Fetching the history of trips $.ajax({ url:'cfcs/mileagedata.cfc?method=getTrips&returnform ...

Looking for a discreet JavaScript-based text editor with advanced features?

Our CMS has been using the now-unsupported RichTextBox control for a while, and we want to find a lighter-weight alternative with better cross-browser support. Initially, we were considering different ASP.NET components, but I am starting to think that an ...

Optimizing Animation Effects: Tips for Improving jQuery and CSS Transitions Performance

Wouldn't it be cool to have a magic line that follows your mouse as you navigate through the header menu? Take a look at this example: It works seamlessly and smoothly. I tried implementing a similar jQuery script myself, but it's not as smoot ...

The error message "Unable to iterate over undefined property in Node.js using EJS and JavaScript" popped up

I've been struggling with the error "Cannot read property 'forEach of undefined" for two days now and I just can't seem to figure out the problem. Any help would be greatly appreciated. Here is the code: bruidstaart.js page where I am tryin ...

Update the jQuery script tag with new content - rewrite everything

I am facing an issue with jquery. I need to change the link to src only when "document.write" is present. Below is my code: myscript.js document.write("TEST ABCD"); test.html <html> <body> <button id="example">click me</button&g ...

Eliminate items from a list that have duplicate properties

I have a collection of objects, each with a unique NAME property. However, there are duplicates in the collection where some objects share the same NAME. const arr = [ {name: "x", place: "a", age: "13" }, {name: "x", place: "b", age: "14" }, { ...

Can a single value be stored in a table using a radio button?

I have created an HTML table that is dynamically generated from a database. I am using a for loop to populate the table. However, I am facing an issue where each radio button in the table holds only one value. What I actually want is for each row to have ...

Verifying Kentico Cloud webhook signatures using Express.js

Is there a way to verify the signature of webhooks using Express.js? I've looked through the documentation on notification signatures, but I'm unsure how to integrate it with Express.js. This question was originally posted on the official Ken ...

Appending a forward slash at the end of a URL seamlessly directs the user to a serendipitous webpage experience, while

I'm currently developing a project on this website: Interestingly, when you append a forward slash to the end of the URL, all the images mysteriously disappear. Consequently, I am unable to include Google AdWords tracking code at the end of any URLs: ...

What is the best way to handle exceptions when dealing with MongoDB?

Issue Encountering a problem where the try block fails to capture errors when utilized within the MongoClient's connect function in MongoDB. System Details Operating System: Linux (Mint, Tessa) Node.js Version: v10.16.0 (utilizing ES6 with nodem ...

Developing a search feature using Ajax in the MVC 6 framework

Embarking on a new project, I have chosen c# .net 6 MVC in VS2022... In my previous projects, this code has run flawlessly. @section Scripts { <script type="text/javascript"> $("#Klijent_Name").autocomplete({ ...

Error message: The import from './components/headerComponent/header' failed because it does not have a default export. Make sure to export it as default to be able to import it

I've been trying to bring a header from one file into another, but it's not cooperating. import React from 'react'; import { Typography, Card, CardContent } from '@material-ui/core'; import Header from './components/head ...

What is the best way to calculate the interpolated rotation value for THREE.Object3D()?

I'm attempting to linearly interpolate a player object (THREE.Object3d())'s position and rotation values to the desired ones (THREE.Vector3()). This is my current code: player.position.lerp(desiredPosition, 0.2); player.rotation.lerp(desiredRo ...