Struggling to assign an image to a vertex material buffer geometry in Three.js

For a recent project, I have been utilizing Three.js to create a unique point material object that users can interact with. My goal is to map the pixels of an image onto the vertices of a buffer geometry so that the image appears as a cluster of points resembling a point cloud. Specifically, I am working with a downscaled map of the Earth consisting of 106 x 53 pixels.

To achieve this effect, I start by drawing the image on a canvas, extracting the image data, determining the color based on the pixel coordinates, and then applying these colors to my geometry (a sphere buffer geometry in this case). However, it seems like something is off in my mapping process.

The snippet below illustrates how I extract colors and populate them in an array for the geometry:

let colors = [];
let color = new THREE.Color();
let positionAttribute = g.attributes.position;

for (let x = 0; x < img.width; x++) {
  for (let y = 0; y < img.height; y++) {
    let c = earthCanvas.getContext("2d");
    let p = c.getImageData(x, y, 1, 1).data;
    let hex = "#" + ("000000" + rgbToHex(p[0], p[1], p[2])).slice(-6);

    color.set(hex);

    colors.push(color.r, color.g, color.b);
  }
}
g.setAttribute("color", new THREE.Float32BufferAttribute(colors, 3));

This method results in the following output: https://i.sstatic.net/Ki7fK.png

I wonder if there's a way to make this representation resemble the Earth as a globe. Could it be a matter of incorrectly assigning the pixel coordinates?

The code snippet responsible for the geometry setup looks like this:

g = new THREE.SphereBufferGeometry(3, 104, 52);

count = g.attributes.position.count;
console.log(count);
g.center();

let pointShape = new THREE.TextureLoader().load("./models/particle.png");

m = new THREE.PointsMaterial({
  size: pointSize,
  map: pointShape,
  vertexColors: true,
  depthTest: false,
  opacity: 1
});

Here's the HTML and JavaScript snippet for the canvas:

function drawImageSource(source, canvas) {
  img = new Image();

  img.addEventListener("load", function () {
    canvas
      .getContext("2d")
      .drawImage(
        img,
        0,
        0,
        img.width,
        img.height,
        0,
        0,
        canvas.width,
        canvas.height
      );
  });
  img.crossOrigin = "Anonymous";
  img.setAttribute("src", source);
}
<div id="canvasDiv">
    <canvas id="earthCanvas", width="106", height="53" hidden />
</body>

You can access the code sandbox project through this link: https://codesandbox.io/s/small-hill-mvhgc?file=/src/index.js:5956-6389 (Please excuse any messy code—it's still a prototype.)

Answer №1

In my opinion, a different strategy would be to customize the existing PointsMaterial by leveraging the .onBeforeCompile() method and passing a texture through a uniform.

body{
  overflow:hidden;
  margin:0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="9febf7edfafadfafb1aeaca9b1af">[email protected]</a>";
import {OrbitControls} from "https://cdn.skypack.dev/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="50243822353510607e6163667e60">[email protected]</a>/examples/jsm/controls/OrbitControls";

let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 0, 10);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

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

let g = new THREE.SphereGeometry(4, 360, 180);
let m = new THREE.PointsMaterial({
  size: 0.05,
  onBeforeCompile: shader => {
    shader.uniforms.tex = {value: new THREE.TextureLoader().load("https://threejs.org/examples/textures/uv_grid_opengl.jpg")};
    shader.vertexShader = `
      varying vec2 vUv;
      ${shader.vertexShader}
    `.replace(
      `#include <begin_vertex>`,
      `#include <begin_vertex>
        vUv = uv;
      `
    );
    //console.log(shader.vertexShader);
    shader.fragmentShader = `
      uniform sampler2D tex;
      varying vec2 vUv;
      ${shader.fragmentShader}
    `.replace(
      `vec4 diffuseColor = vec4( diffuse, opacity );`,
      `
      vec3 col = texture2D(tex, vUv).rgb;
      col *= diffuse;
      vec4 diffuseColor = vec4( col, opacity );`
    );
    //console.log(shader.fragmentShader);
  }
});
let p = new THREE.Points(g, m);
scene.add(p);

renderer.setAnimationLoop(() => {
  renderer.render(scene, camera);
});
</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

Using jQuery functionality on rendered results in Angular

Currently, I'm working on truncating a string based on its character count and then adding an ellipsis if the number of characters exceeds 21. I've included a truncate class in the HTML div using jQuery. Everything seems to be functioning correc ...

Unexpected Results: React Multiline MUI Text Box Fails to Show Intended Content

In the process of developing a React application, I have come across a requirement to implement a multiline text box for editing job advertisements. The text box should initially display a default value containing dynamic placeholders such as [displayTitle ...

Closing the GWT Java window: A step-by-step guide to logging out

After researching, I discovered that in order to Log Out of the application, one must close the window with the following code snippet: If you are looking for a solution, you can find it in this answer: How to run JavaScript function from GWT Java with JS ...

I noticed that while my shareService is effectively sending values in Angular 2, I encounter an issue where my other components are displaying default values instead

I'm in the process of developing an application using angular 2. I have a UserService that sends a value to other components indicating whether a user is logged in or not, and it's working correctly in terms of data transmission. The issue I&apos ...

Pinia throws a useStore is not callable error

I have been trying to resolve the issue with (0 , pinia__WEBPACK_IMPORTED_MODULE_1__.useStore) is not a function but unfortunately, I haven't been able to find a solution. Can anyone point out what mistake I am making? Here is my store.js code: im ...

Having trouble with AngularJS ngdialog watch function not working properly?

I inserted a plugin on my webpage. Inside a popup, I added a search box but the watch function is not working for the search box within the popup. However, if I place the search box outside of the popup, the watch function works fine. How can I resolve thi ...

Creating and sending an email using a contact form in Create-React-App

Currently, I am in the process of developing a website using create-react-app from GitHub. My next task is to create a contact page where user information will be submitted and sent to a designated email address. However, my lack of experience with back-e ...

The style property of the element is not defined

Encountering an issue where the following error is being thrown: TypeError: div1.style is undefined This error prevents the function from being executed, resulting in no action taking place. Javascript <script type="text/javascript"> function ...

Leveraging Javascript to generate universal HTML content for various Javascript files

Present Situation: I have a timesheet feature that enables users to input their leave, TOIL, and sick days along with the respective hours. Additionally, there is a table that dynamically adds a new row every time the plus button is clicked using the foll ...

What is the best way to navigate to the top of a form panel?

I'm currently developing a Sencha Touch mobile app. I have a form panel with multiple fields, so I made it scrollable. However, every time I open the screen (panel), scroll down, navigate to other screens, and then return to the form panel, it stays s ...

Getting a ReferenceError while trying to use a MongoDB Collection variable in an external resolver file that had been imported through mergeResolvers

Here is a simplified example to illustrate the issue at hand. When using the resolver Query getAllUsers, the MongoDB Collection Users is not accessible in the external resolver file user.js. This results in the following error when executing the query: ...

Create immersive experiences with React VR by seamlessly integrating Three.js elements into your scene

After using the react-vr init App command to create a new project, I successfully generated spheres in my scene by mapping through some data: { data.map(node => return <Sphere key={`node-${node.id}`} radius={1} widthSegments={8} heightSegments= ...

Error: Unexpected Meteor Impact

Currently diving into the world of programming and could use some assistance in troubleshooting an issue with my app. Feel free to take a look at the Repo on Github. Error Description: W202306-18:14:53.145(-4)? (STDERR) /Users/Ed/appTest/.meteor/local/bu ...

Incorporate additional text to the downloads hyperlink using jquery

Is it possible to achieve this using jQuery since I am unable to directly modify the HTML markup? I am looking to append download.php?file= to a URL without replacing the entire href attribute. Here's an example of what I'm trying to achieve: & ...

How can I define the boundaries for the scrolling of a static background image?

Is there a way to limit how far a fixed background image can scroll down a page? Essentially, is it possible to change the background attachment to static when the bottom of the background image is 10px away from the bottom edge of its div container? In t ...

jQuery - Password Validation

I need help using jQuery to validate two password fields against each other. Below is the code snippet I am currently using: jQuery('#settingsForm').validate( rules : { npassword : { }, pass_check : { equ ...

What is the most effective way to transfer a Mongoose Collection to a function in a JavaScript file?

Currently, I am working on a project that involves creating a search functionality for a motorcycle database. This search will be similar to the one found on Revzilla.com under My Garage. The form for this search will be used in multiple parts of the proje ...

What could be causing my form to malfunction when attempting to submit data using Ajax and an external PHP script that is handling two string inputs?

Hello, I am facing an issue while trying to utilize Ajax to interact with a PHP file and submit both inputs (fullname and phonenumber). When I click the submit button, it simply refreshes the page without performing the desired action. Below is the code I ...

What is the best way to create a straightforward menu for my HTML game?

I have been working on a basic HTML game and it seems to be functioning more or less. I'm wondering if it's possible to create a simple menu with an image in the background and a "click to start" button within the same file. Any suggestions or ti ...

Error: JsonWebTokenError - node.js is expecting a string for the jwt parameter

Encountering an issue where I'm receiving the error JsonWebTokenError: jwt must be a string while trying to get the jwt from the front end (react.js) and utilize it in middleware for token verification. Attempting to convert it to a string using toStr ...