I'm back for question two on .points. This time wondering how to change the opacity from 0, to 1 and then back within certain pixel distances from the emitter.
var particleCount = 14,
particles = new THREE.Geometry(),
pMaterial = new THREE.PointsMaterial({
map: new THREE.TextureLoader().load("x.png"),
blending: THREE.multiplyBlending,
flatShading: true,
size: 40,
transparent: true,
depthTest: true,
sizeAttenuation: true,
opacity: 1
});
var particleSystem;
My main confusion is that even though I've given it transparency I can't change the value within the update comp I've made for my emitter.
function update() {
//particleSystem.rotation.y += 0.01;
pCount = particleCount;
while (pCount--) {
particle = particles.vertices[pCount];
(This is where a bunch of validation is for where the points are)
particleSystem.geometry.verticesNeedUpdate = true;
particleSystem.rotation.y += (Math.random()*0.001)
}
Render loop:
renderer.setAnimationLoop(() => {
update();
composer.render(scene, camera);
});
I want to make it fade out and not appear in the scene for 20 or so pixels and then fade in. But I'm not entirely sure on how to change the opacity as particle.opacity += 0.1 won't work.
Edit: I'm also uncertain about Size as I want to do a similar thing with it but from 20 to 40, I could probably base it depending on it's Y cordinate. Anyway; I'm also uncertain how to gradually change that too.
Sorry if this is a obvious answer, duplicate question and any help I get. Any alternate methods of what I've seen is in an alternate structure that I don't understand or in array in which I don't know how to put into what I want.
(Thanks in advance)
The issue is that the opacity and the size is a property of the THREE.PointsMaterial. If the pints should have different sizes it is not sufficient to have a list of different vertices in one THREE.Points. There has to be a list of different THREE.Points with different HREE.PointsMaterials.
Create a list of THREE.Points with different materials:
var texture = new THREE.TextureLoader().load( "..." );
var particleSystemCount = 14;
var particleSystems = [];
for (var i = 0; i < particleSystemCount; ++ i) {
var geometry = new THREE.Geometry();
var pMaterial = new THREE.PointsMaterial({
size: 20,
map: texture,
blending: THREE.AdditiveBlending,
transparent: true,
depthTest: false,
sizeAttenuation: true,
opacity: 0
});
// ...
var points = new THREE.Points(geometry, pMaterial);
scene.add(points);
particleSystems.push(points);
}
So in update the opacity and size can be changed individually:
function update() {
for (var i = 0; i < particleSystems.length; ++ i) {
var points = particleSystems[i];
var material = points.material;
var particle = points.geometry.vertices[0];
// ....
if ( material.size < 40 )
material.size += 0.5;
if ( material.opacity < 1 )
material.opacity += 0.01;
// ....
}
}
var canvas_w = window.innerWidth, canvas_h = window.innerHeight;
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, canvas_w/canvas_h, 1, 1000);
camera.position.set(0, 0, 400);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(canvas_w, window.innerHeight);
document.body.appendChild(renderer.domElement);
window.onresize = function() {
canvas_w = window.innerWidth, canvas_h = window.innerHeight;
renderer.setSize(canvas_w, canvas_h);
camera.aspect = canvas_w/canvas_h;
camera.updateProjectionMatrix();
}
var texture = new THREE.TextureLoader().load("https://threejs.org/examples/textures/sprites/circle.png");
var particleSystemCount = 14;
var particleSystems = [];
for (var i = 0; i < particleSystemCount; ++ i) {
var geometry = new THREE.Geometry();
var pMaterial = new THREE.PointsMaterial({
size: 20,
map: texture,
blending: THREE.AdditiveBlending,
transparent: true,
depthTest: false,
sizeAttenuation: true,
opacity: 0
});
var px = (Math.random() - 0.5) * 100;
var py = (Math.random() - 0.5) * 100 + 200;
var pz = (Math.random() - 0.5) * 100;
var particle = new THREE.Vector3(px, py, pz);
particle.velocity = new THREE.Vector3(0, 0, 0);
geometry.vertices.push(particle);
var points = new THREE.Points(geometry, pMaterial);
scene.add(points);
particleSystems.push(points);
}
function update() {
for (var i = 0; i < particleSystems.length; ++ i) {
var points = particleSystems[i];
var material = points.material;
var particle = points.geometry.vertices[0];
if (particle.y < -200) {
particle.x = (Math.random() - 0.5) * 100;
particle.y = (Math.random() - 0.5) * 100 + 200;
particle.z = (Math.random() - 0.5) * 100;
particle.velocity.y = 0;
material.size = 20;
material.opacity = 0;
}
particle.velocity.y -= Math.random() * .1;
particle.add(particle.velocity);
if ( material.size < 40 )
material.size += 0.25;
if ( material.opacity < 1 )
material.opacity += 0.01;
points.geometry.verticesNeedUpdate = true;
points.rotation.y += (Math.random()*0.001)
}
}
renderer.setAnimationLoop(() => {
update();
renderer.render(scene, camera);
});
body { overflow: hidden; margin: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.min.js"></script>
Right now, I am trying to make a navigational menu, but to do this, I need to detect what side is clicked by the user. Is there any way to do this with raycasting, or if not, any other way?
Here is my code if you need it:
CodePen Link
The short version is here
var geometry = new THREE.BoxGeometry(200, 200, 200);
var material = new THREE.MeshLambertMaterial(
{
color: 65535,
morphTargets: true
});
for (var i = 0; i < 8; i++)
{
var vertices = [];
for (var v = 0; v < geometry.vertices.length; v++)
{
vertices.push(geometry.vertices[v].clone());
if (v === i)
{
vertices[vertices.length - 1].x *= 2;
vertices[vertices.length - 1].y *= 2;
vertices[vertices.length - 1].z *= 2
}
}
geometry.morphTargets.push(
{
name: "target" + i,
vertices: vertices
})
}
mesh = new THREE.Mesh(geometry, material);
mesh.position.y = 0;
scene.add(mesh);
scene.background = new THREE.Color(15790320);
var params = {
influence1: 1,
influence2: 1,
influence3: 1,
influence4: 1,
influence5: 1,
influence6: 1,
influence7: 1,
influence8: 1
};
var geometry = new THREE.PlaneBufferGeometry(5e3, 5e3);
geometry.rotateX(-Math.PI / 2);
var material = new THREE.MeshBasicMaterial(
{
color: 975132,
overdraw: .5
});
onMouseDown(event) {
this.mouse.x = (event.pageX / window.innerWidth) * 2 - 1;
this.mouse.y = -(event.pageY / window.innerHeight) * 2 + 1;
this.raycaster.setFromCamera(this.mouse, this.camera);
let intersectCube = this.raycaster.intersectObjects( Cube , true );
}
Make a raycaster on your mouse and check for intersections with the Cube or its faces
I'm trying to turn this plainGeometry, into this (sort of mask).
This code works: (to sum-up, create two materials, divide the plane to segments and decide for each one which material is it with MeshFaceMaterial)
Button.onClick(function () {
var obj = editor.selected;
var material = obj.material;
var tex = material.map;
var objHeight = obj.geometry.parameters.height;
var objWidth = obj.geometry.parameters.width;
var texHeight = tex.image.height;
var texWidth = tex.image.width;
var geometry = new THREE.PlaneGeometry(objWidth, objHeight, objWidth, objHeight);
var facesNum = objHeight * objWidth * 2;
var facesX = objWidth * 2;
var facesInX = texWidth * 2;
var materials = [];
materials.push(material);
materials.push(new THREE.MeshBasicMaterial({ }));
for (var i = 0; i < facesNum; i++) {
if ((i % facesX >= objWidth - texWidth) &&
(i % facesX <= (facesInX + objWidth - texWidth - 1)) &&
(i <= (texHeight * objWidth * 2) - 1)) {
geometry.faces[i].materialIndex = 0;
}
else {
geometry.faces[i].materialIndex = 1;
}
}
obj.geometry = geometry;
obj.material = new THREE.MeshFaceMaterial(materials);
editor.signals.materialChanged.dispatch(obj);
});
But I'm wondering if there is a simpler way to go. Any suggestions?
Another way to do this is to use an alpha channel on your texture. You can do this with Gimp or Photoshop.
You then duplicate the mesh and push it just a tad out the axis with polygonOffsetFactor on the material. Apply the background material to the first mesh, and the foreground material with the texture with alpha to the second.
See this fiddle alphaTest. (you may need to disable cross-domain access security so the texture can load in this fiddle, chrome will allow this if you run it with the --disable-web-security flag)
The advantage to this method is that the image can be of any shape and location, and doesn't need to fit into a geometry face.
Another way, if the geometry you were using is complicated, is to use Three.DecalGeometry to cut a mesh piece out and use it shifted a bit with polygonOffsetFactor on the material. See the three.js decals example for this.
Source for the example fiddle is below:
var renderer;
var pointLight;
var scene;
var plane1;
var plane2;
function addPlane()
{
var material1 = new THREE.MeshPhongMaterial({ color: 0xFFFFFF });
var material2;
var loader = new THREE.TextureLoader();
loader.load('http://i.imgur.com/ETdl4De.png',
function ( texture ) {
var material2 = new THREE.MeshPhongMaterial({
color: 0xFFFFFF,
map: texture,
alphaTest: 0.7,
polygonOffset: true,
polygonOffsetFactor: - 4,
});
var geometry1 = new THREE.PlaneGeometry(12, 12, 12, 12);
var geometry2 = geometry1.clone();
plane1 = new THREE.Mesh(geometry1,material1);
plane2 = new THREE.Mesh(geometry2,material2);
scene.add(plane2);
scene.add(plane1);
}
);
}
(function() {
'use strict';
scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 10000);
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
addPlane();
camera.position.z = 52;
pointLight = new THREE.DirectionalLight(0xffffff,1);
pointLight.position.x = 11;
pointLight.position.y = 5;
pointLight.position.z = 25;
scene.add(pointLight);
var reqAnimFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame;
var render = function() {
reqAnimFrame(render);
renderer.render(scene, camera);
};
render();
}());
this did it, eventually:
var obj = editor.selected;
var tex = obj.material.map.;
var materials = [];
materials.push(new THREE.MeshBasicMaterial({ map: tex }));
materials.push(new THREE.MeshBasicMaterial({}));
var material = new THREE.MeshFaceMaterial(materials);
var objHeight = obj.geometry.parameters.height;
var objWidth = obj.geometry.parameters.width;
var texHeight = tex.image.height;
var texWidth = tex.image.width;
tex.repeat = new THREE.Vector2(3, 3);
tex.offset = new THREE.Vector2(0, 0);
var geometry = new THREE.PlaneGeometry(objWidth, objHeight, 3, 3);
var v = geometry.vertices;
var facesNum = geometry.faces.length;
v[1] = new THREE.Vector3(-texWidth / 2, objHeight / 2, 0);
v[2] = new THREE.Vector3(texWidth / 2, objHeight / 2, 0);
v[5] = new THREE.Vector3(-texWidth / 2, (objHeight / 2) - texHeight, 0);
v[6] = new THREE.Vector3(texWidth / 2, (objHeight / 2) - texHeight, 0);
v[9] = v[13];
v[10] = v[14];
v[4] = v[8] = v[12];
v[7] = v[11] = v[15];
for (var i = 0; i < facesNum; i++) {
if (i !== 2 && i !== 3) geometry.faces[i].materialIndex = 1;
}
obj.material = material;
obj.geometry = geometry;
editor.signals.materialChanged.dispatch(obj);
I've been following a couple of examples on mapping arcs around a three.js globe. I've nearly got this to work although I'm having trouble getting the math correct and the resulting projection is incorrect. I'd appreciate anyone taking a look at the code and telling me what I'm doing wrong. Thanks
import oHoverable from 'o-hoverable';
import d3 from 'd3';
import oHeader from 'o-header';
import THREE from 'three.js';
// var OrbitControls = require('three-orbit-controls')(THREE);
// console.log("orbitControls=",OrbitControls);
oHoverable.init(); // makes hover effects work on touch devices
document.addEventListener('DOMContentLoaded', function () {
oHoverable.init(); // makes hover effects work on touch devices
var dataset=spreadsheet.data
console.log(dataset)
// This let's you inspect the resulting image in the console.
//console.log(canvas.node().toDataURL()); //F
var startLon=45
var startLat=75
var endLon=0
var endLat=0
var posX = 200;
var posY = 600;
var posZ = 1800;
var width = document.getElementById('main').getBoundingClientRect().width;
var height = 600;
var FOV = 45;
var NEAR = 2;
var FAR = 4000;
var controls;
// some global variables and initialization code
// simple basic renderer
var renderer = new THREE.WebGLRenderer();
renderer.setSize(width,height);
renderer.setClearColor( 0x000000, 1);
// add it to the target element
var globeDiv = document.getElementById("globeDiv");
globeDiv.appendChild(renderer.domElement);
// setup a camera that points to the center
var camera = new THREE.PerspectiveCamera(FOV,width/height,NEAR,FAR);
camera.position.set(posX,posY, posZ);
camera.lookAt(new THREE.Vector3(0,0,0));
// create a basic scene and add the camera
var scene = new THREE.Scene();
scene.add(camera);
//spotlight set up in the same location as the camera
var light = new THREE.DirectionalLight(0xffffff, 1.0, 200 );
light.position.set(posX,posY,posZ);
scene.add(light);
//Add Earth
var radious=650
var line
// var earthGeo=new THREE.SphereGeometry(radious,100,100);
// var earthMat=new THREE.MeshPhongMaterial();
// earthMat.map=THREE.ImageUtils.loadTexture("images/world.jpg");
// earthMat.bumpMap=THREE.ImageUtils.loadTexture("images/bumpmap.jpg");
// earthMat.bumpScale=8;
// earthMat.shininess=10;
// var earthObject = new THREE.Mesh(earthGeo,earthMat);
// scene.add(earthObject);
// //Add clouds
// var cloudGeo=new THREE.SphereGeometry(radious,60,60);
// var cloudsMat=new THREE.MeshPhongMaterial({
// opacity: 0.5,
// transparent: true,
// color: 0xffffff
// });
// cloudsMat.map=THREE.ImageUtils.loadTexture("images/clouds.png");
// var meshClouds = new THREE.Mesh( cloudGeo, cloudsMat );
// meshClouds.scale.set(1.015, 1.015, 1.015 );
// scene.add(meshClouds);
//Add lines
var root = new THREE.Object3D();
for (var i = 0; i < dataset.length; i++) {
endLon=dataset[i].lng;
endLat=dataset[i].lat;
makeCurve(startLat,startLon,endLat,endLon);
root.add(line);
};
scene.add(root)
function makeCurve(startLon,startLat,endLon,endLat){
console.log("makeCurve",startLon,startLat,endLon,endLat);
var phiFrom = startLon * Math.PI / 180;
var thetaFrom = (startLat+90) * Math.PI / 180;
//calculates "from" point
var xF = radious * Math.cos(phiFrom) * Math.sin(thetaFrom);
var yF = radious * Math.sin(phiFrom);
var zF = radious * Math.cos(phiFrom) * Math.cos(thetaFrom);
phiFrom = endLon * Math.PI / 180;
thetaFrom = (endLat+90) * Math.PI / 180;
//calculates "from" point
var xT = radious * Math.cos(phiFrom) * Math.sin(thetaFrom);
var yT = radious * Math.sin(phiFrom);
var zT = radious * Math.cos(phiFrom) * Math.cos(thetaFrom);
//Sets up vectors
var vF = new THREE.Vector3(xF, yF, zF);
var vT = new THREE.Vector3(xT, yT, zT);
var dist = vF.distanceTo(vT);
// here we are creating the control points for the first ones.
// the 'c' in front stands for control.
var cvT = vT.clone();
var cvF = vF.clone();
// get the half point of the vectors points.
var xC = ( 0.5 * (vF.x + vT.x) );
var yC = ( 0.5 * (vF.y + vT.y) );
var zC = ( 0.5 * (vF.z + vT.z) );
// then we create a vector for the midpoints.
var subdivisions = 100;
var geometry = new THREE.Geometry();
var curve = new THREE.QuadraticBezierCurve3();
curve.v0 = new THREE.Vector3(xF, yF, zF);
curve.v1 = new THREE.Vector3(xT, yT, zT);
curve.v2 = new THREE.Vector3(xC, yC, zC);
for (var i = 0; i < subdivisions; i++) {
geometry.vertices.push( curve.getPoint(i / subdivisions) )
}
var material = new THREE.LineBasicMaterial( { color: 0xf2c0a4, linewidth: 2 } );
line = new THREE.Line(geometry, material);
}
// controls = new THREE.OrbitControls( camera );
render();
function render() {
var timer = Date.now() * 0.0001;
// camera.position.x=(Math.cos(timer)*1800);
// camera.position.z=(Math.sin(timer)*1800);
camera.lookAt( scene.position );
// light.position.x = (Math.cos(timer)*2000);
// light.position.z = (Math.sin(timer)*2000);
light.lookAt(scene.position);
//earthObject.rotation.y += 0.0014;
root.rotation.y += 0.0014;
//meshClouds.rotation.y += 0.001;
renderer.render(scene,camera);
requestAnimationFrame(render );
}
});
data sample is as follows and is loaded elsewhere
ftlabel,imfcode,lat,lng
Afghanistan,512,33,66
Albania,914,41,20
Algeria,612,28,3
Angola,614,-12.5,18.5
Argentina,213,-34,-64
Armenia,911,40,45
Aruba,314,12.5,-69.97
Australia,193,-25,135
Austria,122,47.33,13.33
Azerbaijan,912,40.5,47.5
Bahamas,313,24,-76
Bahrain,419,26,50.5
Bangladesh,513,24,90
Barbados,316,13.17,-59.53
Belarus,913,53,28
Belgium,124,50.83,4
Belize,339,17.25,-88.75
Benin,638,9.5,2.25
Bermuda,319,32.33,-64.75
Bolivia,218,-17,-65
Bosnia and Herzegovina,963,44.25,17.83
Brazil,223,-10,-55
Brunei,516,4.5,114.67
Bulgaria,918,43,25
Burkina Faso,748,13,-2
Burundi,618,-3.5,30
Cambodia,522,13,105
Cameroon,622,6,12
Canada,156,60,-96
Cape Verde,624,16,-24
Central African Republic,626,7,21
Chad,628,15,19
Chile,228,-30,-71
China,924,35,105
Colombia,233,4,-72
Comoros,632,-12.17,44.25
Costa Rica,238,10,-84
Cote d'Ivoire,662,8,-5
Croatia,960,45.17,15.5
Cuba,928,22,-79.5
Cleaned up the maths a bit but basically I was passing the lon and lat to the function which was receiving them the wrong way round. Code now working and looks like this
import oHoverable from 'o-hoverable';
import d3 from 'd3';
import oHeader from 'o-header';
import THREE from 'three.js';
oHoverable.init(); // makes hover effects work on touch devices
document.addEventListener('DOMContentLoaded', function () {
oHoverable.init(); // makes hover effects work on touch devices
var dataset=spreadsheet.data
//console.log(dataset)
var startLat=38
var startLon=-100
var endLon=0
var endLat=0
var posX = 200;
var posY = 600;
var posZ = 1800;
var width = document.getElementById('main').getBoundingClientRect().width;
var height = 600;
var FOV = 45;
var NEAR = 2;
var FAR = 4000;
var controls;
// some global variables and initialization code
// simple basic renderer
var renderer = new THREE.WebGLRenderer();
renderer.setSize(width,height);
renderer.setClearColor( 0x000000, 1);
// add it to the target element
var globeDiv = document.getElementById("globeDiv");
globeDiv.appendChild(renderer.domElement);
// setup a camera that points to the center
var camera = new THREE.PerspectiveCamera(FOV,width/height,NEAR,FAR);
camera.position.set(posX,posY, posZ);
camera.lookAt(new THREE.Vector3(0,0,0));
// create a basic scene and add the camera
var scene = new THREE.Scene();
scene.add(camera);
//spotlight set up in the same location as the camera
var light = new THREE.DirectionalLight(0xffffff, 1.0, 200 );
light.position.set(posX,posY,posZ);
scene.add(light);
//Add Earth
var radius=600
var curveObject
var earthGeo=new THREE.SphereGeometry(radius,100,100);
var earthMat=new THREE.MeshPhongMaterial();
earthMat.map=THREE.ImageUtils.loadTexture("images/world.jpg");
earthMat.bumpMap=THREE.ImageUtils.loadTexture("images/bumpmap.jpg");
earthMat.bumpScale=8;
earthMat.shininess=10;
var earthObject = new THREE.Mesh(earthGeo,earthMat);
scene.add(earthObject);
//Add clouds
var cloudGeo=new THREE.SphereGeometry(radius,60,60);
var cloudsMat=new THREE.MeshPhongMaterial({
opacity: 0.5,
transparent: true,
color: 0xffffff
});
cloudsMat.map=THREE.ImageUtils.loadTexture("images/clouds.png");
var meshClouds = new THREE.Mesh( cloudGeo, cloudsMat );
meshClouds.scale.set(1.015, 1.015, 1.015 );
scene.add(meshClouds);
//Add lines
var lineObject = new THREE.Object3D();
for (var i = 0; i < dataset.length; i++) {
endLon=dataset[i].lng;
endLat=dataset[i].lat;
makeCurve(startLon,startLat,endLon,endLat,i);
lineObject.add(curveObject);
};
scene.add(lineObject)
// takes lon lat and a radius and turns that into vector
function to3DVector(lon, lat, radius) {
var phi = lat * Math.PI / 180;
var theta = (lon + 90) * Math.PI / 180;
var xF = radius * Math.cos(phi) * Math.sin(theta);
var yF = radius * Math.sin(phi);
var zF = radius * Math.cos(phi) * Math.cos(theta);
return new THREE.Vector3(xF, yF, zF);
}
function makeCurve(startLon,startLat,endLon,endLat,i){
var widthScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) { return d.imfcode; })])
.range([1, 12]);
var vF = to3DVector(startLon, startLat, radius);
var vT = to3DVector(endLon, endLat, radius);
// then you get the half point of the vectors points.
var xC = ( 0.5 * (vF.x + vT.x) );
var yC = ( 0.5 * (vF.y + vT.y) );
var zC = ( 0.5 * (vF.z + vT.z) );
// then we create a vector for the midpoints.
var mid = new THREE.Vector3(xC, yC, zC);
var dist = vF.distanceTo(vT);
// here we are creating the control points for the first ones.
// the 'c' in front stands for control.
var cvT = vT.clone();
var cvF = vF.clone();
var smoothDist = map(dist, 0, 10, 0, 15/dist );
console.log(smoothDist);
mid.setLength( radius * smoothDist );
cvT.add(mid);
cvF.add(mid);
cvT.setLength( radius * smoothDist );
cvF.setLength( radius * smoothDist );
var curve = new THREE.CubicBezierCurve3( vF, cvF, cvT, vT );
var geometry2 = new THREE.Geometry();
geometry2.vertices = curve.getPoints( 50 );
var material2 = new THREE.LineBasicMaterial( { transparent: true,opacity :0.6, color : 0xff0000,linewidth:widthScale(dataset[i].imfcode)} );
// Create the final Object3d to add to the scene
curveObject = new THREE.Line( geometry2, material2 );
function map(value, low1, high1, low2, high2) {
return low2 + (high2 - low2) * (value - low1) / (high1 - low1);
}
}
render();
function render() {
var timer = Date.now() * 0.0001;
// camera.position.x=(Math.cos(timer)*1800);
// camera.position.z=(Math.sin(timer)*1800);
camera.lookAt( scene.position );
// light.position.x = (Math.cos(timer)*2000);
// light.position.z = (Math.sin(timer)*2000);
light.lookAt(scene.position);
earthObject.rotation.y += 0.0005;
lineObject.rotation.y += 0.0005;
meshClouds.rotation.y += 0.0012;
renderer.render(scene,camera);
requestAnimationFrame(render );
}
});