three.js BufferGeometry blob code update error - javascript

I currently learning three js and I was following this tutorial however noticed that the code is invalid for the current version of threejs and you can see the main change below. Going by this document I've updated the bit of code to:
var k = 1;
const positions = sphere.geometry.attributes.position.array;
for (var i = 0; i < positions.length - 2; i++) {
const v = new THREE.Vector3(
positions[i],
positions[i + 1],
positions[i + 2]
).normalize()
.multiplyScalar(
1 + 0.3 * noise.perlin3(
positions[i] * k + time,
positions[i + 1] * k,
positions[i + 2] * k));
positions[i] = v.x;
positions[i + 1] = v.y;
positions[i + 2] = v.z;
}
sphere.geometry.attributes.position.needsUpdate = true;
However as you can see from the snippet the vertex displacement is not smooth and does not resemble the tutorial. What I'm missing here?
var renderer = new THREE.WebGLRenderer({ canvas : document.getElementById('canvas'), antialias:true});
// default bg canvas color //
renderer.setClearColor(0x7b7b7b);
// use device aspect ratio //
renderer.setPixelRatio(window.devicePixelRatio);
// set size of canvas within window //
renderer.setSize(window.innerWidth, window.innerHeight);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 45, window.innerWidth/window.innerHeight, 0.1, 1000 );
camera.position.z = 5;
var sphere_geometry = new THREE.SphereGeometry(1, 128, 128);
const material = new THREE.MeshPhongMaterial({
color: 0x0000FF,
shininess: 1000,
})
var sphere = new THREE.Mesh(sphere_geometry, material);
scene.add(sphere);
const ambientLight = new THREE.AmbientLight(0x798296)
scene.add(ambientLight)
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5)
directionalLight.position.set(5, 10, 7)
scene.add(directionalLight);
sphere.geometry.attributes.position.needsUpdate = true;
var update = function() {
// change '0.003' for more aggressive animation
var time = performance.now() * 0.003;
//console.log(time)
//go through vertices here and reposition them
// change 'k' value for more spikes
var k = 1;
const positions = sphere.geometry.attributes.position.array;
for (var i = 0; i < positions.length - 2; i++) {
const v = new THREE.Vector3(
positions[i],
positions[i + 1],
positions[i + 2]
).normalize()
.multiplyScalar(
1 + 0.3 * noise.perlin3(positions[i] * k + time, positions[i + 1] * k,
positions[i + 2] * k));
positions[i] = v.x;
positions[i + 1] = v.y;
positions[i + 2] = v.z;
}
sphere.geometry.attributes.position.needsUpdate = true;
}
function animate() {
sphere.rotation.x += 0.01;
sphere.rotation.y += 0.01;
update();
/* render scene and camera */
renderer.render(scene,camera);
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
html, body {
margin:0;
overflow:hidden
}
<script src="https://fariskassim.com/stage/rebel9/teaf/blob/v4/js/perlin.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<canvas id="canvas"></canvas>

This is how you can deform a sphere with noise, using BufferGeometry:
var renderer = new THREE.WebGLRenderer({ canvas : document.getElementById('canvas'), antialias:true});
// default bg canvas color //
renderer.setClearColor(0x7b7b7b);
// use device aspect ratio //
renderer.setPixelRatio(window.devicePixelRatio);
// set size of canvas within window //
renderer.setSize(window.innerWidth, window.innerHeight);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 45, window.innerWidth/window.innerHeight, 0.1, 1000 );
camera.position.z = 5;
var sphere_geometry = new THREE.SphereGeometry(1, 128, 128);
const material = new THREE.MeshPhongMaterial({
color: 0x0000FF,
shininess: 1000,
})
var sphere = new THREE.Mesh(sphere_geometry, material);
scene.add(sphere);
const ambientLight = new THREE.AmbientLight(0x798296)
scene.add(ambientLight)
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5)
directionalLight.position.set(5, 10, 7)
scene.add(directionalLight);
sphere.geometry.attributes.position.needsUpdate = true;
var update = function() {
// change '0.003' for more aggressive animation
var time = performance.now() * 0.003;
//console.log(time)
//go through vertices here and reposition them
// change 'k' value for more spikes
var k = 1;
var v3 = new THREE.Vector3();
const positions = sphere.geometry.attributes.position;
for (var i = 0; i < positions.count; i++) {
v3.fromBufferAttribute(positions, i).setLength(k);
let n = noise.perlin3(v3.x, v3.y, v3.z);
v3.setLength(1 + 0.3 * n);
positions.setXYZ(i, v3.x, v3.y, v3.z);
}
positions.needsUpdate = true;
sphere.geometry.computeVertexNormals(); // don't forget to call this
}
function animate() {
sphere.rotation.x += 0.01;
sphere.rotation.y += 0.01;
update();
/* render scene and camera */
renderer.render(scene,camera);
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
html, body {
margin:0;
overflow:hidden
}
<script src="https://fariskassim.com/stage/rebel9/teaf/blob/v4/js/perlin.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<canvas id="canvas"></canvas>

Related

How to free-draw a line in three.js r144 on mouse move, and using BufferGeometry?

I'm using code from this repo - scribble, which is using three.js r87. I followed the Updating THREE.Geometry to THREE.BufferGeometry tutorial in order to upgrade the code to three.js r144. I've got one function correct, but the other one is giving me trouble.
The mousePressed function was easy to update:
function mousePressed() {
const point = new THREE.Vector3(mouseX, mouseY, 0);
// const geometry = new THREE.Geometry();
// geometry.vertices.push(point);
let points = [];
points.push(point);
let geometry = new THREE.BufferGeometry().setFromPoints(points);
const line = new THREE.Line(geometry, material);
scene.add(line);
selected = line;
}
But I updated mouseDragged(), and I don't understand why it's not working:
function mouseDragged() {
const line = selected;
const point = new THREE.Vector3(mouseX, mouseY, 0);
const oldgeometry = line.geometry;
// const newgeometry = new THREE.Geometry();
// newgeometry.vertices = oldgeometry.vertices;
// newgeometry.vertices.push(point);
let newgeometry = new THREE.BufferGeometry();
let positions = oldgeometry.attributes.position.array;
for (let i = 0; i < positions.length; i += 3) {
const v = new THREE.Vector3(positions[i], positions[i + 1], positions[i + 2]);
positions[i] = v.x;
positions[i + 1] = v.y;
positions[i + 2] = v.z;
}
positions.push(point); // I should just be able to add the new point, no?
newgeometry.attributes.position.needsUpdate = true;
line.geometry = newgeometry;
scene.add(line); // I re-added the line, just in case. But the line does not show up, whether or not I do this.
selected = line;
}
Help is much appreciated!
Thanks
The code from the resource is highly inefficient since you continuously allocate geometries all the time without disposal management which will lead to a memory leak. Try the following approach:
let camera, scene, renderer, line;
const frustumSize = 4;
let index = 0;
const coords = new THREE.Vector3();
init();
render();
function init() {
const aspect = window.innerWidth / window.innerHeight;
camera = new THREE.OrthographicCamera(frustumSize * aspect / -2, frustumSize * aspect / 2, frustumSize / 2, frustumSize / -2, 0.1, 20);
camera.position.z = 5;
scene = new THREE.Scene();
const geometry = new THREE.BufferGeometry();
const positionAttribute = new THREE.BufferAttribute(new Float32Array(1000 * 3), 3); // allocate large enough buffer
positionAttribute.setUsage(THREE.DynamicDrawUsage);
geometry.setAttribute('position', positionAttribute);
const material = new THREE.LineBasicMaterial()
line = new THREE.Line(geometry, material);
scene.add(line);
// initial points
addPoint(0, 0, 0); // start point
addPoint(1, 0, 0); // current pointer coordinate
//
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
renderer.domElement.addEventListener('pointerdown', onPointerDown);
renderer.domElement.addEventListener('pointermove', onPointerMove);
window.addEventListener('resize', onWindowResize);
}
function addPoint(x, y, z) {
const positionAttribute = line.geometry.getAttribute('position');
positionAttribute.setXYZ(index, x, y, z);
positionAttribute.needsUpdate = true;
index++;
line.geometry.setDrawRange(0, index);
}
function updatePoint(x, y, z) {
const positionAttribute = line.geometry.getAttribute('position');
positionAttribute.setXYZ(index - 1, coords.x, coords.y, 0);
positionAttribute.needsUpdate = true;
}
function onPointerDown(event) {
coords.x = (event.clientX / window.innerWidth) * 2 - 1;
coords.y = -(event.clientY / window.innerHeight) * 2 + 1;
coords.z = (camera.near + camera.far) / (camera.near - camera.far);
coords.unproject(camera);
addPoint(coords.x, coords.y, 0);
render();
}
function onPointerMove(event) {
coords.x = (event.clientX / window.innerWidth) * 2 - 1;
coords.y = -(event.clientY / window.innerHeight) * 2 + 1;
coords.z = (camera.near + camera.far) / (camera.near - camera.far);
coords.unproject(camera);
updatePoint(coords.x, coords.y, 0)
render();
}
function onWindowResize() {
const aspect = window.innerWidth / window.innerHeight;
camera.left = -frustumSize * aspect / 2;
camera.right = frustumSize * aspect / 2;
camera.top = frustumSize / 2;
camera.bottom = -frustumSize / 2;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
render();
}
function render() {
renderer.render(scene, camera);
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.144/build/three.min.js"></script>
The idea is to allocate a single large buffer to store all current and future points of a line. You then use setDrawRange() to define, what parts of the buffer should be rendered.

Accessing the vertices using the new PlaneBufferGeometry in ThreeJS

I understand PlaneGeometry has been deprecated and we should use PlaneBufferGeometry with the latest releases. The following code worked with the build before R125, I just don't know how to tweak the code to make it work with PlaneBufferGeometry:
var flagGeom = new THREE.PlaneBufferGeometry(4.5, 2.2, 40, 100, 200); // Replaced "PlaneGeometry " with "PlaneBufferGeometry" here.
flagGeom.translate(2.2, 1.1, 0);
flagGeom.vertices.forEach(v => { // Undefined error here.
v.init = v.clone()
});
Looks like your question is related to this forum topic.
You can do the things this way (just an option, not the ultimate solution): use an additional array stored in userData, and use .setXYZ() method.
body{
overflow: hidden;
margin: 0;
}
<script src="https://josephg.github.io/noisejs/perlin.js"></script>
<script type="module">
console.clear();
import * as THREE from 'https://cdn.skypack.dev/three#0.129.0/build/three.module.js';
import { OrbitControls } from 'https://cdn.skypack.dev/three#0.129.0/examples/jsm/controls/OrbitControls.js';
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(-1, 0.5, 2).setLength(2.5);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x401000);
document.body.appendChild(renderer.domElement);
var controls = new OrbitControls(camera, renderer.domElement);
var light = new THREE.DirectionalLight(0xff5500, 2);
light.position.setScalar(10);
scene.add(light);
scene.add(new THREE.AmbientLight(0xff0000, 1));
var loader = new THREE.TextureLoader();
// flag
var flagGeom = new THREE.PlaneGeometry(4, 2, 40, 20);
flagGeom.translate(2, 1, 0);
let pos = flagGeom.attributes.position;
flagGeom.userData = {
init: []
}
for(let i = 0; i < pos.count; i++){
flagGeom.userData.init.push(new THREE.Vector3().fromBufferAttribute(pos, i));
}
console.log(flagGeom.userData.init);
var flagMat = new THREE.MeshStandardMaterial({
color: 0x777777,
map: loader.load("https://cywarr.github.io/small-shop/PW_flag/PW_flag_map.png"),
alphaMap: loader.load("https://cywarr.github.io/small-shop/PW_flag/PW_flag_alphaMap.png"),
alphaTest: 0.5,
side: THREE.DoubleSide,
metalness: 0.5,
roughness: 0.5
});
var flag = new THREE.Mesh(flagGeom, flagMat);
flag.position.set(-2, -1, 0);
flag.rotation.x = THREE.Math.degToRad(-18);
scene.add(flag);
// staff
var staff = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 4, 8), new THREE.MeshStandardMaterial({
color: "black"
}));
flag.add(staff);
// background canvas texture
var canvas = document.createElement("canvas");
canvas.width = 128;
canvas.height = 64;
var ctx = canvas.getContext('2d');
var image = ctx.createImageData(canvas.width, canvas.height);
var data = image.data;
var canvasTexture = new THREE.CanvasTexture(canvas);
scene.background = canvasTexture;
window.addEventListener( 'resize', onWindowResize );
var clock = new THREE.Clock();
var delta = 0;
var time = 0;
var v = new THREE.Vector3();
render();
function render() {
requestAnimationFrame(render);
delta = clock.getDelta();
time += delta;
flagGeom.userData.init.forEach( (vi, idx) => {
v.copy(vi);
let yFade = Math.sin(v.y / flagGeom.parameters.height * Math.PI) * 0.25;
v.x = v.x + yFade;
let xFade = (v.x / flagGeom.parameters.width);
v.z = noise.perlin2((v.x - (time * 2)) / flagGeom.parameters.width * 4, v.y / flagGeom.parameters.height * 2) * xFade;
pos.setXYZ(idx, v.x, v.y, v.z);
});
flagGeom.computeVertexNormals();
pos.needsUpdate = true;
drawFrame(time);
canvasTexture.needsUpdate = true;
renderer.render(scene, camera);
}
function drawFrame(time) {
var cWidth = canvas.width;
var cHeight = canvas.height;
for (var x = 0; x < cWidth; x++) {
for (var y = 0; y < cHeight; y++) {
var value = Math.abs(noise.simplex3(x / cWidth, y / cHeight, time * 0.25));
var cell = (x + y * cWidth) * 4;
data[cell] = 128 + Math.floor(128 * value);
data[cell + 1] = Math.floor(256 * value);
data[cell + 2] = 0;
data[cell + 3] = 255; // alpha.
}
}
ctx.fillColor = 'black';
ctx.fillRect(0, 0, 100, 100);
ctx.putImageData(image, 0, 0);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
</script>

Create plane from polyline points using threejs

I am trying to achieve something like this:
I have a Polyline and its points now I want to create a sheet same as shown in the image. Above is polyline and below a sheet created from that polyline points.
I followed solution from this post Extruding a line in three.js but when I try this it renders nothing.
Here is the code which I tried:
let containerThreeJs = document.getElementById('threed-view-container');
let w = containerThreeJs.offsetWidth;
let h = containerThreeJs.offsetHeight;
let renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(w, h);
containerThreeJs.appendChild(renderer.domElement);
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(5, 1, 1, 1000);
camera.position.setScalar(300);
let threeDpoints = [
[88.5, 370],
[229.5, 268],
[300.5, 333],
[373.5, 290],
[426.5, 392]
];
let geometry = extrudePath(threeDpoints, 100);
var material = new THREE.MeshBasicMaterial({
color: 0x00ff00,
side: THREE.DoubleSide
});
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
render();
function resize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render() {
if (resize(renderer)) {
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
function extrudePath(points, depth) {
var geometry = new THREE.PlaneGeometry(10, 10, points.length - 1, 1);
var vertices = geometry.vertices;
// if I comment this loop then the plane is visible
for (var i = 0, l = points.length, p; i < l; i++) {
p = points[i];
vertices[i].x = vertices[i + l].x = p[0];
vertices[i].y = vertices[i + l].y = p[1];
vertices[i].z = p[2];
vertices[i + l].z = p[2] + depth;
}
geometry.computeFaceNormals();
return geometry;
}
<script src="http://mrdoob.github.io/three.js/build/three.min.js"></script>
<script src=http://mrdoob.github.io/three.js/examples/js/controls/OrbitControls.js></script>
<div id="threed-view-container" style="width: 100%; height: 500px"></div>
If I remove the for-loop from extrudePath then the simple plane is visible, but if I keep it nothing seems to render.
I'm not sure, but i see this: In the points array threeDpoints, every point has 2 parts. However in the extrudePath function it's looking for 3. (p[0], p[1], p[2])
`let threeDpoints = [
[88.5, 370],
[229.5, 268],
[300.5, 333],
[373.5, 290],
[426.5, 392]
];
`
`p = points[i];
vertices[i].x = vertices[i + l].x = p[0];
vertices[i].y = vertices[i + l].y = p[1];
vertices[i].z = p[2];
vertices[i + l].z = p[2] + depth;`

Highlighting the nearest edge from mouse pointer in three js

In threejs webgl I want to highlight the nearest edge from my mouse pointer and select it to get the length of the edge.
I am using Edgesgeometry to create the edges.
var edgeGeometry = new THREE.EdgesGeometry(meshes[meshIndex]); // or WireframeGeometry
var edgeMaterial = new THREE.LineBasicMaterial({ color: 0x00000, linewidth: 2 });
var wireframe = new THREE.LineSegments(edgeGeometry, edgeMaterial);
var object = new THREE.Mesh(meshes[meshIndex], material);
object.add(wireframe);
Just an idea.
You know which face you're intesecting and you have its edges, you can use THREE.Line3(), built from vertices of each edge (thus you'll use THREE.Line3() three times). Then, you know the point of intersection and have three lines of THREE.Line3(), now you can find the nearest edge by choosing the closest line to the point of intersection, and .closestPointToPoint() method will help. This method returns the closes point on an edge relatively to the point of intersection.
Means, that you just find three distances from the point of intersection to three edges of the face. And you'll highlight the edge with the minimal distance from it to the point of intersection.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 10);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var geom = new THREE.PlaneBufferGeometry(8, 8).toNonIndexed();
var mat = new THREE.MeshBasicMaterial({
color: "blue"
});
var mesh = new THREE.Mesh(geom, mat);
scene.add(mesh);
renderer.domElement.addEventListener("mousemove", onMouseMove, false);
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var intersects = [];
var localPoint = new THREE.Vector3();
let closestPoint = new THREE.Vector3();
var edgeGeom = new THREE.BufferGeometry().setFromPoints([
new THREE.Vector3(),
new THREE.Vector3()
]);
var edge = new THREE.Line(edgeGeom, new THREE.LineBasicMaterial({
color: "aqua"
}));
scene.add(edge);
var pos = mesh.geometry.attributes.position;
function onMouseMove(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
intersects = raycaster.intersectObject(mesh);
if (intersects.length === 0) return;
let faceIdx = intersects[0].faceIndex;
let lines = [
new THREE.Line3(
new THREE.Vector3().fromBufferAttribute(pos, faceIdx * 3 + 0),
new THREE.Vector3().fromBufferAttribute(pos, faceIdx * 3 + 1)
),
new THREE.Line3(
new THREE.Vector3().fromBufferAttribute(pos, faceIdx * 3 + 1),
new THREE.Vector3().fromBufferAttribute(pos, faceIdx * 3 + 2)
),
new THREE.Line3(
new THREE.Vector3().fromBufferAttribute(pos, faceIdx * 3 + 2),
new THREE.Vector3().fromBufferAttribute(pos, faceIdx * 3 + 0)
)
];
let edgeIdx = 0;
mesh.worldToLocal(localPoint.copy(intersects[0].point));
let minDistance = 1000;
for (let i = 0; i < 3; i++) {
lines[i].closestPointToPoint(localPoint, true, closestPoint);
let dist = localPoint.distanceTo(closestPoint);
if (dist < minDistance) {
minDistance = dist;
edgeIdx = i;
}
}
let pStart = mesh.localToWorld(lines[edgeIdx].start);
let pEnd = mesh.localToWorld(lines[edgeIdx].end);
edgeGeom.attributes.position.setXYZ(0, pStart.x, pStart.y, pStart.z);
edgeGeom.attributes.position.setXYZ(1, pEnd.x, pEnd.y, pEnd.z);
edgeGeom.attributes.position.needsUpdate = true;
}
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
body{
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/95/three.min.js"></script>

How do I plot random meshes on top of a terrain using a heightmap in three.js?

So as the title states I'd like to know how to plot randomly generated meshes at the y-position that matches the terrain's corresponding y-position in three.js. I've looked through the docs and feel like using a raycaster might work, but I can only see examples that uses the detection as a mouse event, and not before render, so I'm not sure how to implement it.
Here is my code for the terrain, heightmap, and mesh plotting so far. It all works technically, but as you can see the plotAssets meshes y-positions are just sitting at zero right now. Any insights would be very much appreciated, I'm pretty new to three.js.
Terrain:
var heightmaploader = new THREE.ImageLoader();
heightmaploader.load(
"assets/noisemaps/cloud.png",
function(img) {
data = getHeightData(img);
var terrainG = new THREE.PlaneBufferGeometry(700, 700, worldWidth - 1, worldDepth - 1);
terrainG.rotateX(-Math.PI / 2);
var vertices = terrainG.attributes.position.array;
for (var i = 0, j = 0, l = vertices.length; i < l; i++, j += 3) {
vertices[j + 1] = data[i] * 5;
}
terrainG.computeFaceNormals();
terrainG.computeVertexNormals();
var material = new THREE.MeshLambertMaterial({
map: terrainT,
//side: THREE.DoubleSide,
color: 0xffffff,
transparent: false,
});
terrain = new THREE.Mesh(terrainG, material);
terrain.receiveShadow = true;
terrain.castShadow = true;
terrain.position.y = 0;
scene.add(terrain);
plotAsset('boulder-photo-01.png', 30, 18, data);
plotAsset('boulder-outline-01.png', 20, 20, data);
plotAsset('birch-outline-01.png', 10, 50, data);
plotAsset('tree-photo-01.png', 20, 50, data);
plotAsset('grass-outline-01.png', 10, 20, data);
plotAsset('grass-outline-02.png', 10, 20, data);
}
);
Plot Assets:
function plotAsset(texturefile, amount, size, array) {
console.log(array);
var loader = new THREE.TextureLoader();
loader.load(
"assets/textures/objects/" + texturefile,
function(texturefile) {
var geometry = new THREE.PlaneGeometry(size, size, 10, 1);
var material = new THREE.MeshBasicMaterial({
color: 0xFFFFFF,
map: texturefile,
side: THREE.DoubleSide,
transparent: true,
depthWrite: false,
depthTest: false,
alphaTest: 0.5,
});
var uniforms = { texture: { value: texturefile } };
var vertexShader = document.getElementById( 'vertexShaderDepth' ).textContent;
var fragmentShader = document.getElementById( 'fragmentShaderDepth' ).textContent;
// add bunch o' stuff
for (var i = 0; i < amount; i++) {
var scale = Math.random() * (1 - 0.8 + 1) + 0.8;
var object = new THREE.Mesh(geometry, material);
var x = Math.random() * 400 - 400 / 2;
var z = Math.random() * 400 - 400 / 2;
object.rotation.y = 180 * Math.PI / 180;
//object.position.y = size * scale / 2;
object.position.x = x;
object.position.z = z;
object.position.y = 0;
object.castShadow = true;
object.scale.x = scale; // random scale
object.scale.y = scale;
object.scale.z = scale;
scene.add(object);
object.customDepthMaterial = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: vertexShader,
fragmentShader: fragmentShader,
side: THREE.DoubleSide
} );
}
}
);
}
Height Data:
function getHeightData(img) {
var canvas = document.createElement('canvas');
canvas.width = 2048 / 8;
canvas.height = 2048 / 8;
var context = canvas.getContext('2d');
var size = 2048 / 8 * 2048 / 8,
data = new Float32Array(size);
context.drawImage(img, 0, 0);
for (var i = 0; i < size; i++) {
data[i] = 0
}
var imgd = context.getImageData(0, 0, 2048 / 8, 2048 / 8);
var pix = imgd.data;
var j = 0;
for (var i = 0, n = pix.length; i < n; i += (4)) {
var all = pix[i] + pix[i + 1] + pix[i + 2];
data[j++] = all / 40;
}
return data;
}
Yes, using of THREE.Raycaster() works well.
A raycaster has the .set(origin, direction) method. The only thing you have to do here is to set the point of origin higher than the highest point of the height map.
var n = new THREE.Mesh(...); // the object we want to aling along y-axis
var collider = new THREE.Raycaster();
var shiftY = new THREE.Vector3();
var colliderDir = new THREE.Vector3(0, -1, 0); // down along y-axis to the mesh of height map
shiftY.set(n.position.x, 100, n.position.z); // set the point of the origin
collider.set(shiftY, colliderDir); //set the ray of the raycaster
colliderIntersects = collider.intersectObject(plane); // plane is the mesh of height map
if (colliderIntersects.length > 0){
n.position.y = colliderIntersects[0].point.y; // set the position of the object
}
jsfiddle example

Categories