Setting color of mapped image for ThreeJS particles - three.js

Originally I was using ParticleSystem, but I discovered that Raycaster does not work with it. So I'm now modifying my code to simply use individual Particle objects.
The problem is, I can't seem to set the color of the image I'm mapping to the particles like I was able to with ParticleSystem.
I tried the following:
texture = THREE.ImageUtils.loadTexture("ball.png");
material = new THREE.ParticleBasicMaterial({
size : 10,
color: 0x00C1BF,
map : texture,
transparent : true,
});
// Generate some random points...
for (var i = 0; i < pointCount; i++) {
var particle = new THREE.Particle(material);
particle.position.x = Math.random() * (max - min) + min;
particle.position.y = Math.random() * (max - min) + min;
particle.position.z = Math.random() * (max - min) + min;
particle.scale.x = particle.scale.y = particle.scale.z = 3;
plot.add(particle);
}
But the color of ball.png remains the same. If I comment out the image I'm mapping to the points, the colors are changing. But it's not working with the mapped image. When I was using ParticleSystem, inside the for loop where I generate the points, I was adding this:
colors[i] = new THREE.Color(0xffffff);
colors[i].setHSL((x + 1000 ) / 2000, 1, 0.5);
And then set particleSys.colors = colors; outside the loop. That changed the color of the points, but this doesn't seem to work with Particle.
I hate to keep bugging the community with questions like this, but I really would appreciate any guidance on this. Many thanks, as always! :)
Also, here's a link to ball.png that I'm using: http://threejsdoc.appspot.com/doc/three.js/examples/textures/sprites/ball.png

Related

I have a question about using turtle graphic functions and looping methods on p5.js

I have to create these two included images using the turtle function and the loop method on p5js and I am struggling I was given https://editor.p5js.org/dpapanik/sketches/_lbGWWH6N this code on p5js as a start please help, thanksenter image description here
So I've played around with some of the stuff for awhile, and I've created two functions. One that makes a single quadrant of the first problem, and one that creates a single wiggly line for the second problem. This is just a base for you to work of in this process. Here's each of the functions. Also, note that each of them takes in the turtle as a parameter:
function makeLineQuadrant(turtle) {
// this currently makes the top left corner:
let yVal = windowWidth * 0.5;
let xVal = windowWidth * 0.5;
for (let i = 0; i < 13; i++) {
// loop through the 12 lines in one quadrant
turtle.face(0); // reset for the new round
turtle.penUp();
let startLeft = i * ((windowWidth * 0.5) / 12); // decide which component on the button we should start at
let endTop = (12 - i) * ((windowWidth * 0.5) / 12); // how far down the y-axis should we go? You should write this out on paper to see how it works
turtle.goto(startLeft, yVal);
turtle.penDown();
let deg = turtle.angleTo(xVal, endTop); // what direction do I need to turn?
turtle.face(deg);
let distance = turtle.distanceTo(xVal, endTop); // how far away is it?
turtle.forward(distance);
}
}
I tried to add a few comments throughout, but if there is any step that is confusing, please add a comment.
function makeSquiggle(turtle) {
turtle.setColor(color(random(0, 255), random(0, 255), random(0, 255)));
let middleX = windowWidth * 0.5, middleY = windowHeight * 0.5;
turtle.goto(windowWidth * 0.5, windowHeight * 0.5);
// let's start moving in a random direction UNTIL our distance from the center is greater than some number X
let X = 300; // arbitrary distance from center
// some variables that can help us get some random movement for our turtle:
let turtleXvel = random(-3, 3), turtleYvel = random(-3, 3);
while (turtle.distanceTo(middleX, middleY) < X) {
turtle.face(0);
// calculate movement:
let newXmove = turtle.x + turtleXvel, newYmove = turtle.y + turtleYvel;
// direct our turtle:
turtle.face(turtle.angleTo(newXmove, newYmove));
let distance = turtle.distanceTo(newXmove, newYmove); // how far away is it?
// move our turtle
turtle.penDown();
turtle.forward(distance);
// change the velocity a little bit for a smooth curving:
turtleXvel += random(-1, 1);
turtleYvel += random(-1, 1);
}
}
Note that I'm changing the velocities instead of the position directly. This is a classic Calculus / Physics problem where the derivative gives us a smaller range, so adjusting turtleXvel and turtleYvel change the position in much less drastic ways versus:
turtle.x += random(-1, 1);
turtle.y += random(-1, 1);
You should look at the difference as well to visualize this. Beyond this is working with these structural components to finish this up!

Calculate the vertex while creating terrain from heightmap using ThreeJs

I'm reading "create terrain from heightmap" example from ThreeJs Cookbook
This example load GrandCanyon: http://lh5.ggpht.com/_-B0hFoGrn-w/SvHiYk39yAI/AAAAAAAABOQ/6IGZwifUYGA/GrandCanyon.png
And create a 3D terrain: http://www.smartjava.org/tjscb/02-geometries-meshes/02.06-create-terrain-from-heightmap.html
There are some code pieces I can not understand:
// draw on canvas
ctx.drawImage(img, 0, 0);
var pixel = ctx.getImageData(0, 0, width, depth);
var geom = new THREE.Geometry;
var output = [];
for (var x = 0; x < depth; x++) {
for (var z = 0; z < width; z++) {
// get pixel
// since we're grayscale, we only need one element
var yValue = pixel.data[z * 4 + (depth * x * 4)] / heightOffset;
var vertex = new THREE.Vector3(x * spacingX, yValue, z * spacingZ);
geom.vertices.push(vertex);
}
}
why is yValue calculated with that value ? why don't we use var yValue = pixel.data[z * 4 + (depth * x )] or something like that ?
And do we really need spacingX and spacingZ ?
Source code is here: https://github.com/josdirksen/threejs-cookbook/blob/master/02-geometries-meshes/02.06-create-terrain-from-heightmap.html
Could you please help me ?
Thank you very much!
You don't NEED spacingX and spacingZ, no. You could adjust scale in other ways, like applying a scale matrix to the entire THREE.Geometry after you've populated the vertices. Up to you, really.
As fort the yValue, the indexing is to adjust for the way the data for the texture is laid out. There are four channels, usually RGBA, but in this case we only need one of them as a height.

2D Scatter Plot in Three.js

Using Three.js, I created a 3D scatter plot. Here is an example as a fiddle...
http://jsfiddle.net/jmg157/ynFzw/19/
I generate some random points (x, y, z) and add them to the plot. Here is an example of the for loop I use to do this:
for (var i = 0; i < 50; i++) {
colors[i] = new THREE.Color(1, 1, 1);
colors[i].setHSL(1000 / 2000, 1, 0.5);
var material = new THREE.PointCloudMaterial({
size: 5,
vertexColors: THREE.VertexColors,
transparent: true,
useScreenCoordinates: false
});
material.color.setHSL(1.0, 0.2, 0.7);
var vertex = new THREE.Vector3();
var max = 50;
var min = -50;
vertex.x = Math.random() * (max - min) + min;
vertex.y = Math.random() * (max - min) + min;
vertex.z = Math.random() * (max - min) + min;
geometry.vertices.push(vertex);
}
In practice, the points are plotted based on values in an array. So let's say I have a dataset that contains only x and y values (2D). I'd still like to be able to show a plot using Three.js.
I've created a fiddle that sort of does this, by only creating an XY grid, and commenting out the z value. But unfortunately, this seems to put the points a distance away from the grid, not directly on the grid like you would see in a typical 2D plot. Here is my fiddle for this...
http://jsfiddle.net/jmg157/exr6xc42/1/
I know it might sound a little silly to use Three.js for a 2D plot, but I want to be able to have the option in case a dataset only has x and y values as plottable.
Based on the fiddle above, how can I get the points to appear on the grid, like a standard 2D plot? Do I need to set the z coordinate to something specific?
Thanks in advance!
Isn't it enough to change this line (line 43) from
gridXY.position.set(0, 0, -50);
to
gridXY.position.set(0, 0, 0);
?

Change the opacity of a Particle object in Three.js (NOT ParticleSystem)?

I am using the CanvasRendererand Particlein Three.JS. I am generating some random particles using this approach:
texture = THREE.ImageUtils.loadTexture("img.png");
material = new THREE.ParticleBasicMaterial({
map : texture,
transparent : true,
});
for (var i = 0; i < pointCount; i++) {
var particle = new THREE.Particle(material);
particle.position.x = Math.random() * (max - min) + min;
particle.position.y = Math.random() * (max - min) + min;
particle.position.z = Math.random() * (max - min) + min;
// Set the size of the particle
particle.scale.x = particle.scale.y = particle.scale.z = Math.floor(Math.random() * 6) + 2;
particles.push(particle);
scatterPlot.add(particle);
}
I would like to (if possible) be able to change the opacity of individual Particles, as I am using transparency as a dimension in my plot (i.e., transparency of a particle reflects the magnitude of a variable). I know I can use particle.material.opacity, but that changes the opacity for all particles. I tried particles[i].material.opacitybut got the same result.
One possibility of course would be to have an array of materials with different opacities. But I'm not sure if there's perhaps a simpler way to do this?
Many thanks!
I think I might have figured this out -- and was simpler than I was thinking. It at least seems to be working for me. I moved the material declaration into the for loop. I think this generates a different ParticleBasicMaterial for each Particle, but I suppose that is okay.
I am then able to access the individual Particle'smaterial by using particles[i].material, which allows me to change the opacity of specific particles.
Here is my updated code:
texture = THREE.ImageUtils.loadTexture("img.png");
for (var i = 0; i < pointCount; i++) {
material = new THREE.ParticleBasicMaterial({
map : texture,
transparent : true,
});
var particle = new THREE.Particle(material);
particle.position.x = Math.random() * (max - min) + min;
particle.position.y = Math.random() * (max - min) + min;
particle.position.z = Math.random() * (max - min) + min;
// Set the size of the particle
particle.scale.x = particle.scale.y = particle.scale.z = Math.floor(Math.random() * 6) + 2;
particles.push(particle);
scatterPlot.add(particle);
}
// Example opacity change
particles[0].material.opacity = 0.5;
If anyone has any other suggestions, I'd greatly appreciate them. But at least this seems to be giving me the desired behavior for now.

Smart Centering and Scaling after Model Import in three.js

Is there a way to determine the size and position of a model and then auto-center and scale the model so that it is positioned at the origin and within the view of the camera? I find that when I import a Collada model from Sketchup, if the model was not centered at the origin in Sketchup, then it is not centered in three.js. While that makes sense, it would be nice to auto-center to origin after importing.
I've seen some discussion in the different file loaders about getting the bounds of the imported model, but I have been unable to find any references to how to do that.
The scaling issue is less important, but I feel like it relates to a bounds function, which is why I asked it too.
EDIT:
More info after playing around a bit and a few more google searches...
The code for my callback function on loading the collada file now looks like this:
loader.load(mURL, function colladaReady( collada ) {
dae = collada.scene;
skin = collada.skins[ 0 ];
dae.scale.x = dae.scale.y = dae.scale.z = 1;
dae.updateMatrix();
//set arbitrary min and max for comparison
var minX = 100000;
var minY = 100000;
var minZ = 100000;
var maxX = 0;
var maxY = 0;
var maxZ = 0;
var geometries = collada.dae.geometries;
for(var propName in geometries){
if(geometries.hasOwnProperty(propName) && geometries[propName].mesh){
dae.geometry = geometries[propName].mesh.geometry3js;
dae.geometry.computeBoundingBox();
bBox = dae.geometry.boundingBox;
if(bBox.min.x < minX) minX = bBox.min.x;
if(bBox.min.y < minY) minY = bBox.min.x;
if(bBox.min.z < minZ) minZ = bBox.min.z;
if(bBox.max.x > maxX) maxX = bBox.max.x;
if(bBox.max.y > maxY) maxY = bBox.max.x;
if(bBox.max.z > maxZ) maxZ = bBox.max.z;
}
}
//rest of function....
This is generating some interesting data about the model. I can get an overall extreme coordinate for the model, which I'm assuming (probably incorrectly) would be close to an overall bounding box for the model. But trying to do anything with those coordinates (like averaging and moving the model to the averages) generates inconsistent results.
Also, it seems inefficient to have to loop through every geometry for a model, is there a better way? If not, can this logic be applied to other loaders?
You can use THREE.Box3#setFromObject to get the bounding box of any Object3D, including an imported model, without having to loop through the geometries yourself. So you could do something like
var bBox = new THREE.Box3().setFromObject(collada.scene);
to get the extreme bounding box of the model; then you could use any of the techniques in the answers that gaitat linked in order to set the camera position correctly. For instance, you could follow this technique (How to Fit Camera to Object) and do something like:
var height = bBox.size().y;
var dist = height / (2 * Math.tan(camera.fov * Math.PI / 360));
var pos = collada.scene.position;
camera.position.set(pos.x, pos.y, dist * 1.1); // fudge factor so you can see the boundaries
camera.lookAt(pos);
Quick fiddle: http://jsfiddle.net/p19r9re2/ .
try geometry.center()
center: function () {
var offset = new Vector3();
return function center() {
this.computeBoundingBox();
this.boundingBox.getCenter( offset ).negate();
this.translate( offset.x, offset.y, offset.z );
return this;
};
}(),

Resources