adding perlin noise to THREE.js r121 - three.js

I have been struggling with finding a good tutorial to add perlin noise to a newer version of THREE.js. I have found many tutorials and examples but they all seem to break when I add r121 into the mix.
I've tried a great example from Codepen using a script from jeromeetienne.github.io
I've also tried this guys tutorial and file http://www.stephanbaker.com/post/perlinterrain/
I've tried several others and no luck. I am convinced it's due to THREE versions being used. Most of what I can find use an old version of THREE.js. I have forked what others have, and it works with the old version they are using but not the new one.
//land
let landGeo = new THREE.PlaneGeometry(500,500, 50, 50);
let landMat = new THREE.MeshStandardMaterial({
color:'green'
})
land = new THREE.Mesh(landGeo, landMat);
scene.add(land);
noise.seed(Math.random());
for(let i=1;i<100;i++) {
for(let j=1;j<100;j++) {
var value = noise.simplex2(i / 100, j / 100);
// ... or noise.simplex3 and noise.perlin3:
// var value = noise.simplex3(i / 100, j / 100, clock);
landGeo[i][j].r = Math.abs(value) * 256;
}
}
So does anyone know how can get some version of perlin noise working? I am trying to creat terrain.
https://codepen.io/jfirestorm44/pen/mdEEwqb?editors=0010
Thank you

Did you check the browser console errors? It should be the first thing you check.
I see the error Cannot read property '1' of undefined.
Which tells me that something is wrong with the line landGeo[i][j].r = Math.abs(value) * 256;
The vertices are in a single dimension array and are vector objects. I am pretty sure it should be:
landGeo.vertices[i+j].z = Math.abs(value) * 256;.
Also, I am not sure you are calling the noise function with the correct parameters, though I could be wrong and you may be wanting it like it is.
var value = noise.simplex2(i / 100, j / 100);
Also, you are starting your for loops at 1, for(let i=1;i<100;i++) {, I am pretty sure you want to start them at 0. for(let i=0;i<100;i++) {
There is always the option of doing the displacement in a shader like this 3d example, only in 2d

Related

Three.js - repositioning vertices in a 'particle' mesh

I have a basic three.js game working and I'd like to add particles. I've been searching online, including multiple questions here, and the closest I've come to getting a 'particle system' working is using a THREE.BufferGeometry, a THREE.BufferAttribute and a THREE.Points mesh. I set it up like this:
const particleMaterial = new THREE.PointsMaterial( { size: 10, map: particleTexture, blending: THREE.AdditiveBlending, transparent: true } );
const particlesGeometry = new THREE.BufferGeometry;
const particlesCount = 300;
const posArray = new Float32Array(particlesCount * 3);
for (let i = 0; i < particlesCount; i++) {
posArray[i] = Math.random() * 10;
}
const particleBufferAttribute = new THREE.BufferAttribute(posArray, 3);
particlesGeometry.setAttribute( 'position', particleBufferAttribute );
const particlesMesh = new THREE.Points(particlesGeometry, particleMaterial);
particlesMesh.counter = 0;
scene.add(particlesMesh);
This part works and displays the particles fine, at their initial positions, but of course I'd like to move them.
I have tried all manner of things, in my 'animate' function, but I am not happening upon the right combination. I'd like to move particles, ideally one vertex per frame.
The current thing I'm doing in the animate function - which does not work! - is this:
particleBufferAttribute.setXYZ( particlesMesh.counter, objects[0].position.x, objects[0].position.y, objects[0].position.z );
particlesGeometry.setAttribute( 'position', particleBufferAttribute );
//posArray[particlesMesh.counter] = objects[0].position;
particlesMesh.counter ++;
if (particlesMesh.counter > particlesCount) {
particlesMesh.counter = 0;
}
If anyone has any pointers about how to move Points mesh vertices, that would be great.
Alternatively, if this is not at all the right approach, please let me know.
I did find Stemkoski's ShaderParticleEngine, but I could not find any information about how to make it work (the docs are very minimal and do not seem to include examples).
You don't need to re-set the attribute, but you do need to tell the renderer that the attribute has changed.
particleBufferAttribute.setXYZ( particlesMesh.counter, objects[0].position.x, objects[0].position.y, objects[0].position.z );
particleBufferAttribute.needsUpdate = true; // This is the kicker!
By setting needsUpdate to true, the renderer knows to re-upload that attribute to the GPU.
This might not be concern for you, but just know that moving particles in this way is expensive, because you re-upload the position attribute every single frame, which includes all the position data for every particle you aren't moving.

Three.js r125 BufferGeometry `vertices` does not exist

I'm working on updating Three.js and I find that when I upgrade to r125, attempts to set vertices on BufferGeometry fail due to the method missing. It also appears to have removed verticesNeedUpdate. The migration guide does not appear to warn about this and the changelog doesn't seem to address it from what I can see.
I unfortunately didn't write the original code so I'm unsure of how to resolve it. The code looks like this:
this.geometry.vertices[0].x = this.geometry.vertices[2].x = -this.canvas.width / 2;
this.geometry.vertices[1].x = this.geometry.vertices[3].x = this.canvas.width / 2;
this.geometry.vertices[0].y = this.geometry.vertices[1].y = this.canvas.height / 2;
this.geometry.vertices[2].y = this.geometry.vertices[3].y = -this.canvas.height / 2;
this.geometry.verticesNeedUpdate = true;
Update using Don's Answer Below
After applying Don's suggested change, we wind up with this:
const negativeWidth = -this.canvas.width / 2;
const positiveWidth = this.canvas.width / 2;
const positiveHeight = this.canvas.height / 2;
const negativeHeight = -this.canvas.height / 2;
this.geometry.attributes.position.setXY(0, negativeWidth, positiveHeight);
this.geometry.attributes.position.setXY(1, positiveWidth, positiveHeight);
this.geometry.attributes.position.setXY(2, negativeWidth, negativeHeight);
this.geometry.attributes.position.setXY(3, positiveWidth, negativeHeight);
this.geometry.attributes.position.needsUpdate = true;
The first changelog entry for three.js r125 is the relevant one:
Geometry has been removed from the core. It is now located in examples/jsm/deprecated/Geometry.js.
The THREE.Geometry class has been been deprecated for a while, but some older code and examples outside of the project repository still refer to it. The recommended replacement is THREE.BufferGeometry, which is more performant. The BufferGeometry class does not have a .vertices property, so that's probably the cause of the particular error you're seeing. Instead, you can update vertices like this:
geometry.attributes.position.setXYZ( index, x, y, z );
geometry.attributes.position.needsUpdate = true;

Creating a light trail effect by dynamically updating the curve of a TubeBufferGeometry

I have a third person game using react-three-fiber and I want to add a sort of trailing light effect wherever the player moves. The light trail will disappear after a while so I was thinking of having a fixed size array for the points. This was my initial attempt at a solution:
const point = new THREE.Vector3();
const points = new Array(50).fill(null).map(p => new THREE.Vector3());
let index = 0;
const Trail = () => {
const ref = useRef();
const playerBody = useStore(state => state.player); // contains player position
const [path, setPath] = useState(new THREE.CatmullRomCurve3(points));
useFrame(() => { // equivalent to raf
const { x, y, z } = playerBody.position;
point.set(x, y, z);
points[index].copy(point);
index = (index + 1) % 50;
setPath(new THREE.CatmullRomCurve3(points));
if (ref && ref.current) {
ref.current.attributes.position.needsUpdate = true;
ref.current.computeBoundingSphere();
}
});
return (
<mesh>
<tubeBufferGeometry ref={ref} attach="geometry" args={[path, 20, .5, 8, false]} />
<meshBasicMaterial attach="material" color={0xffffff} />
</mesh>
)
}
Basically my thought process was to update the curve on every frame (or every x frames to be more performant) and to use an index to keep track of which position in the array of points to update.
However I get two problems with this:
TubeBufferGeometry doesn't update. Not sure if it's even possible to update the geometry after instantiation.
The pitfall I foresee in using this fixed array / index method is that once I hit the end of the array, I will have to wrap around to index 0. So then the curve interpolation would mess up because I'm assuming it takes the points sequentially. The last point in the array should connect to the first point now but it won't be like that.
To solve #2, I tried something like
points.unshift();
points.push(point.clone);
instead of points[index].copy(point); but I still couldn't get the Tube to update in the first place.
I wanted to see if there's a better solution for this or if this is the right approach for this sort of problem.
If you want to update the path of a TubeBufferGeometry, you also need to update all the vertices and normals, it is like building again the geometry.
Take a look here to understand how it works : https://github.com/mrdoob/three.js/blob/r118/src/geometries/TubeGeometry.js#L135
The important part is the generateSegment() function, and don't forget this part before :
const frames = path.computeFrenetFrames( tubularSegments, closed );
I made an example last year, feel free to use my code : https://codepen.io/soju22/pen/JzzvbR

three.js Geometry.faceVertexUvs setup is confusing

I am working on implementing a custom model importer. The file contains all the necessary info (vertices, vertex normals, vertex uv coordinates, materials, etc.)
It's important to note that the file contains models with multiple materials.
The setup of the vertices is fairly straight forward and is working correctly.
The setup of the faces I'm not 100% sure about. I do the following:
meshDict[name].faces.push(
new THREE.Face3(parseInt(triangles[t]),
parseInt(triangles[t + 1]),
parseInt(triangles[t + 2]),
[normals[parseInt(triangles[t])],
normals[parseInt(triangles[t + 1])],
normals[parseInt(triangles[t + 2])]],
new THREE.Vector3(1, 1, 1), matIndex));
Here: t is the index iterator of the triangles array, normals is an array that holds the normal information of the vertices and matIndex is the material index of the face based on the sub-mesh number from the object file. This also seems to work correctly.
Now for the hard part. I searched all day for a clear explanation and/or good example of how to set-up the faceVertexUvs of a multi material mesh, but every second post I found shows a different method to set this up. After a lot of trial and error I got to this solution that works, but throws a LOT of warnings...
for (var f = 0; f < faces.length; f++)
{
if (currentMesh.faceVertexUvs[faces[f].materialIndex] == undefined)
{
currentMesh.faceVertexUvs[faces[f].materialIndex] = []
faceOffset = (faces[f].materialIndex == 0? 0 : 1) * f;
}
currentMesh.faceVertexUvs[faces[f].materialIndex].push(f);
currentMesh.faceVertexUvs[faces[f].materialIndex][f - faceOffset] = [];
currentMesh.faceVertexUvs[faces[f].materialIndex][f - faceOffset].push(uvs[faces[f].a]);
currentMesh.faceVertexUvs[faces[f].materialIndex][f - faceOffset].push(uvs[faces[f].b]);
currentMesh.faceVertexUvs[faces[f].materialIndex][f - faceOffset].push(uvs[faces[f].c]);
}
Here uvs is an array of Vector2 of the same length as the vertices array.
Basically I am doing:
faceVertexUvs[materialIndex][faceIndex][uvs[a], uvs[b], uvs[c]].
The number of material indexes is equal to the number of sub-meshes that the object has.
So this solution kinda works OK, but some of the textures are not looking correct (I suspect because the UV mapping of that area is not being set correctly), and I am getting a lot of warnings that say:
Important to note that all the models look OK in the exporting program so the issue is not from there.
Any ideas as to what I am doing wrong here?
So I think I finally managed to figure it out and I got it working correctly, and since the documentation is severely lacking in this area, and the examples are not quite clear I'll post my understanding of this topic here for anyone having the same issue.
So it goes like this:
Geometry.faveVertexUvs[UV LAYER][face index][uv[face.a], uv[face.b], uv[face.c]]
As far as I understand unless you have an AO (ambient occlusion) map, you only use UV LAYER 0.
Now, if you define a material index when setting up the faces of the geometry then each face will be rendered with the corresponding material, and there is no need to split the UVs into separate areas like I was doing in my question. So you only have to use:
Geometry.faces.push(new THREE.Face3(v0, v1, v2, [n0, n1, n2], vertexColor, materialIndex));

Is it possible to loop through a sprite group in three.js with a tween that ends in a different position for each sprite?

I'm confused.
I've made a group of 10 sprites and added them to a THREE.Object3D() called fireworkGroup. I have another Object3D called explode. The tween loops through the sprites changing them from their initial position to explode.position.
for ( var i = 0; i < fireworkGroup.children.length; i ++ )
{
explode.position.x =(Math.random() - 0.5)*4;
explode.position.y =4;
explode.position.z =2;
var tweenLaunch = new TWEEN.Tween(fireworkGroup.children[i].position).to(explode.position, 4000).easing( TWEEN.Easing.Quartic.In);
tweenLaunch.start();
}
The tween is moving all the sprites from their start position to the same end position. So I thought this might be because "tweenLaunch" is being overwritten with a different explode.position each time as the tween is rendered so I'm only seeing the last one created in the loop. When I refresh the page they do all move to a different position, consistent with the Math.random().
But then why do all the sprites move to the explode.position? If "tweenLaunch" is being overwritten then why is it not moving only the last sprite?
Is it possible to have a loop with a tween in it that also changes?
Many Thanks.
I've managed to work out what was wrong by reading around the subject on Stackoverflow questions and answers, looking at a great particle example by stemkoski then trial and error.
view-source:http://stemkoski.github.io/Three.js/Particles.html
I used console.log to look at explode.position that I was using as the second position in the tween. It wasn't holding the values I wanted (a different Math.random on each loop).
So I created fireworkAttributes:
fireworkAttributes = { startPosition: [], explodePosition: [], nextPosition: [] };
and then cloned the sprite position in the function that created the sprites using:
fireworkAttributes.explodePosition.push( sprite.position.clone() );
then looped it in it's own function:
for (var i = 0; i < fireworkGroup.children.length; i++)
{
fireworkAttributes.explodePosition[i].x = (Math.random() - 0.5)*4;
fireworkAttributes.explodePosition[i].y = 4;
fireworkAttributes.explodePosition[i].z = 2;
}
then changed the code in the original question to:
for ( var a = 0; a < fireworkGroup.children.length; a ++ )
{
//need to use this new object as tweening to fireworkAttributes.explodePosition[a] does not work
explodeSite.position = fireworkAttributes.explodePosition[a];
var tweenLaunch = new TWEEN.Tween(fireworkGroup.children[a].position).to(explodeSite.position, 4000).easing( TWEEN.Easing.Quartic.In);
tweenLaunch.start();
}
There may be a more elegant way to do this and I will be working to clean up the code where possible but this does work.

Resources