cannon.js object is flipping end to end - javascript

I'm working on a little three.js scene in which I want to drive a car down a road. The trouble is my car is flipping end to end each frame, instead of rolling on its tires:
Does anyone know how I can make my car roll with cannon.js? Any pointers would be hugely helpful. For the sake of preservation, here's my raw scene:
var carBody,
floorBody,
pressed = {},
rotation = 0,
clock = new THREE.Clock(),
loader = new THREE.TextureLoader(),
container = document.querySelector('body'),
w = container.clientWidth,
h = container.clientHeight,
scene = new THREE.Scene(),
camera = new THREE.PerspectiveCamera(75, w/h, 0.1, 100000),
controls = new THREE.TrackballControls(camera, container),
renderConfig = {antialias: true, alpha: true},
renderer = new THREE.WebGLRenderer(renderConfig);
controls.target = new THREE.Vector3(0, 0, 0.75);
controls.panSpeed = 0.4;
camera.position.set(0,80,-4900);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(w, h);
container.appendChild(renderer.domElement);
window.addEventListener('resize', function() {
w = container.clientWidth;
h = container.clientHeight;
camera.aspect = w/h;
camera.updateProjectionMatrix();
renderer.setSize(w, h);
})
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
if (car && carBody && carBody.position) {
moveCar();
updatePhysics();
moveCamera();
//controls.update();
}
}
function getPlane(img, w, h, wrap) {
var texture = loader.load(img);
if (wrap) {
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(10, 10);
}
var material = new THREE.MeshBasicMaterial({
map: texture,
side: THREE.DoubleSide,
});
var geometry = new THREE.PlaneGeometry(w, h, 10, 10);
return new THREE.Mesh(geometry, material);
}
function getSides() {
var d = 300;
var group = new THREE.Group();
for (var i=0; i<2; i++) {
var plane = getPlane('asphalt.jpg', 10000, 200, true);
plane.position.y = 100;
plane.rotation.set(Math.PI/2, -Math.PI/2, Math.PI/2);
plane.position.x = i == 0 ? -d : d;
group.add(plane);
}
return group;
}
function getSky() {
var directions = ['right', 'left', 'top', 'bottom', 'front', 'back'];
var geometry = new THREE.BoxGeometry(50000, 50000, 50000);
var materialArray = [];
for (var i=0; i<6; i++)
materialArray.push( new THREE.MeshBasicMaterial({
map: loader.load(directions[i] + '.bmp'),
side: THREE.BackSide
}));
return new THREE.Mesh( geometry, materialArray );
}
function getCar() {
var mtlLoader = new THREE.MTLLoader();
mtlLoader.load('car.mtl', function(mat) {
mat.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials(mat);
objLoader.load('car.obj', function(obj) {
window.car = obj;
car.scale.set(0.1, 0.1, 0.1);
obj.position.set(0, 0, -4800);
scene.add(obj);
})
})
}
function getPhysics() {
world = new CANNON.World();
world.gravity.set(0, -100, 0); // earth = -9.82 m/s
world.broadphase = new CANNON.NaiveBroadphase();
world.broadphase.useBoundingBoxes = true;
var solver = new CANNON.GSSolver();
solver.iterations = 7;
solver.tolerance = 0.1;
world.solver = solver;
world.quatNormalizeSkip = 0;
world.quatNormalizeFast = false;
world.defaultContactMaterial.contactEquationStiffness = 1e9;
world.defaultContactMaterial.contactEquationRelaxation = 4;
return world;
}
function addPhysics() {
var m = getPhysicsMaterial();
carBody = new CANNON.Body({
mass: 10,
material: m,
shape: new CANNON.Sphere(30),
linearDamping: 0.6,
angularDamping: 0.8,
position: new CANNON.Vec3(0, 30, -4900)
});
world.addBody(carBody);
// floor
var q = floor.quaternion;
var floorBody = new CANNON.Body({
mass: 0, // mass = 0 makes the body static
material: m,
shape: new CANNON.Plane(),
quaternion: new CANNON.Quaternion(-q._x, q._y, q._z, q._w)
});
world.addBody(floorBody);
}
function getPhysicsMaterial() {
var m = new CANNON.Material('slipperyMaterial');
var c = new CANNON.ContactMaterial(m, m, {
friction: 0.3,
restitution: 0.3,
})
world.addContactMaterial(c);
return m;
}
function moveCar() {
var delta = clock.getDelta(); // seconds
var moveDistance = 2000 * delta; // n pixels per second
// set roll sensitivity
var sensitivity = 1.5;
var rotateAngle = Math.PI / 2 * delta * sensitivity;
// determine the direction to travel
var p = carBody.position;
var dir = new THREE.Vector3(p.x, p.y, p.z);
dir.sub(camera.position).normalize(); // vector b/w camera and car
if (pressed['W'] || pressed['ARROWUP']) {
carBody.velocity.x += moveDistance * dir.x;
carBody.velocity.z += moveDistance * dir.z;
}
if (pressed['S'] || pressed['ARROWDOWN']) {
carBody.velocity.x -= moveDistance * dir.x;
carBody.velocity.z -= moveDistance * dir.z;
}
if (pressed['A'] || pressed['ARROWLEFT']) {
rotation += rotateAngle;
}
if (pressed['D'] || pressed['ARROWRIGHT']) {
rotation -= rotateAngle;
}
if (pressed[' ']) {
carBody.velocity.y = 10;
}
}
function updatePhysics() {
world.step(1/60);
car.position.copy(carBody.position);
car.quaternion.copy(carBody.quaternion);
}
function moveCamera() {
var rotZ = Math.cos(rotation);
var rotX = Math.sin(rotation);
var distance = 100;
camera.position.x = carBody.position.x - (distance * rotX);
camera.position.y = carBody.position.y + 20;
camera.position.z = carBody.position.z - (distance * rotZ);
camera.lookAt(car.position);
}
window.addEventListener('keydown', function(e) {
pressed[e.key.toUpperCase()] = true;
})
window.addEventListener('keyup', function(e) {
pressed[e.key.toUpperCase()] = false;
})
/**
* Add elems
**/
var light = new THREE.HemisphereLight(0xffffbb, 0x080820, 1);
scene.add(light);
var geometry = new THREE.PlaneGeometry(10000, 10000);
var material = new THREE.MeshBasicMaterial();
var floor = new THREE.Mesh(geometry, material);
floor.rotation.x = Math.PI / 2;
scene.add(floor);
var street = getPlane('asphalt.jpg', 10000, 1000, true);
street.rotation.x = Math.PI/2;
street.rotation.z = Math.PI/2;
scene.add(street);
var sides = getSides();
scene.add(sides);
var sky = getSky();
scene.add(sky);
var car = getCar();
var world = getPhysics();
addPhysics();
render();
html,
body {
width: 100%;
height: 100%;
background: #aaa;
}
body {
margin: 0;
overflow: hidden;
}
canvas {
width: 100%;
height: 100%;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/cannon.js/0.6.2/cannon.js'></script>
<script src='https://raw.githack.com/mrdoob/three.js/master/examples/js/loaders/ObjLoader.js'></script>
<script src='https://raw.githack.com/mrdoob/three.js/master/examples/js/loaders/MTLLoader.js'></script>
<script src='https://raw.githack.com/mrdoob/three.js/master/examples/js/controls/TrackballControls.js'></script>

Related

Three.js TimeLineLite animation not working

I'm trying to animate the TextGeometries to form a shape of box {delay} sphere {delay} and then a cone. Also, change the colors while the transition. I tried the below code for trying to animate the initial state to a box:
var pointsInsideShape = shapes[0].points.attributes.position.array;
for (i = 0; i < MAX_PARTICLES; i++) {
var tl = new TimelineLite();
tl.from(particleGroup.children[i].position, 2, {
x: pointsInsideShape[ index ++ ],
y: pointsInsideShape[ index ++ ],
z: pointsInsideShape[ index ++ ]
})
}
The console throws the error below:
particleGroup.children[i] is undefined
I checked the particleGroup has objects, however I'm getting this error. I'm not sure why this isn't working.
Currently all the particles are in the center, I'm trying to animate them to form a shape of a sphere, then a delay before the next shape animation and so on. Repeat the loop at the end. If someone could point me in the right direction, it'd be much appreciated. Thank you!
Below is the code:
var renderer, camera, scene, light, shapes, triggers, particleCount, particleGroup,
defaultAnimationSpeed, morphAnimationSpeed, colorToStartWith, textGeometries, loader, typeface,
animationVars;
const MAX_PARTICLES = 100,
PARTICLE_SIZE = .65;
var sts = {
config: function() {
shapes = [{
"geoCode": new THREE.BoxBufferGeometry(13, 13, 13),
"textMat": new THREE.MeshPhongMaterial({
color: 0x029894
}),
"color": 0x029894,
"size": [50, 50, 50]
},
{
"geoCode": new THREE.SphereBufferGeometry(25, 33, 33),
"textMat": new THREE.MeshPhongMaterial({
color: 0x8F3985
}),
"color": 0x8F3985,
"size": [25, 33, 33]
},
{
"geoCode": new THREE.ConeBufferGeometry(25, 50, 30),
"textMat": new THREE.MeshPhongMaterial({
color: 0x11659C
}),
"color": 0x11659C,
"size": [25, 50, 30]
}
];
triggers = document.getElementsByTagName('span');
particleGroup = new THREE.Group();
defaultAnimationSpeed = 1;
morphAnimationSpeed = 18;
normalSpeed = (defaultAnimationSpeed / 100)
fullSpeed = (morphAnimationSpeed / 100)
colorToStartWith = '#8F3985';
textGeometries = new Array();
},
initScene: function() {
//Renderer
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setClearColor(0xffffff, 1);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Scene
scene = new THREE.Scene();
// Camera and position
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.y = -25;
camera.position.z = 45;
camera.rotation.x = -.45;
// Lighting
light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(1, 1, 1).normalize();
scene.add(light);
var light2 = new THREE.DirectionalLight(0xffffff, 1.5);
light2.position.set(0, -1, 0);
scene.add(light2);
// Texts
loader = new THREE.FontLoader();
typeface = 'https://raw.githubusercontent.com/7dir/json-fonts/master/fonts/cyrillic/roboto/Roboto_Regular.json';
//particleGroup
particleGroup = new THREE.Group();
particleGroup.position.x = 0;
particleGroup.position.y = -45;
particleGroup.position.z = 0;
scene.add(particleGroup);
},
fullScreen: function() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
},
attachEvents: function() {
window.addEventListener('resize', this.fullScreen, false);
},
addShapes: function() {
for (idx = 0; idx < shapes.length; idx++) {
shapes[idx].geometry = shapes[idx].geoCode.toNonIndexed();
m = new THREE.MeshLambertMaterial({
color: shapes[idx].color,
opacity: 0,
transparent: true,
wireframe: true
});
shapes[idx].geometry.center();
shape = new THREE.Mesh(shapes[idx].geometry, m);
scene.add(shape);
}
},
computePointsInside: function(idx) {
shapes[idx].points = this.fillWithPoints(shapes[idx].geometry, MAX_PARTICLES);
},
addText: function() {
loader.load(typeface, (font) => {
//var x, y, z, index1, index2;
//x = y = z = index1 = index2 = 0;
var m = new THREE.MeshPhongMaterial({
color: 0x8F3985,
opacity: .8,
specular: 0xffffff,
shininess: 100
});
for (i = 0; i < MAX_PARTICLES; i++) {
g = new THREE.TextGeometry(Math.random() < .5 ? '6' : '9', {
font: font,
size: PARTICLE_SIZE,
height: 0.1
});
var text = new THREE.Mesh(g, m);
text.position.x = 0;
text.position.y = 0;
text.position.z = 0;
text.rotation.x = Math.random() * 2 * Math.PI;
text.rotation.y = Math.random() * 2 * Math.PI;
text.rotation.z = Math.random() * 2 * Math.PI;
text.scale.x = (Math.random() * (0.95 - 0.1) + 0.95).toFixed(4);
text.scale.y = (Math.random() * (0.95 - 0.1) + 0.95).toFixed(4);
text.scale.z = (Math.random() * (0.95 - 0.1) + 0.95).toFixed(4);
//var pointsInsideShape = shapes[0].points.attributes.position.array;
//text.position.x = pointsInsideShape[ index2 ++ ];
//text.position.y = pointsInsideShape[ index2 ++ ];
//text.position.z = pointsInsideShape[ index2 ++ ];
particleGroup.add(text);
}
});
},
animationSequence: function() {
var x, y, z, index;
x = y = z = index = 0;
var pointsInsideShape = shapes[0].points.attributes.position.array;
for (i = 0; i < MAX_PARTICLES; i++) {
var tl = new TimelineLite();
tl.from(particleGroup.children[i].position, 2, {
x: pointsInsideShape[index++],
y: pointsInsideShape[index++],
z: pointsInsideShape[index++]
})
}
},
animate: function() {
window.requestAnimationFrame(sts.animate);
particleGroup.rotation.y += 0.005;
renderer.render(scene, camera)
},
fillWithPoints: function(geometry, count) {
var ray = new THREE.Ray()
var size = new THREE.Vector3();
geometry.computeBoundingBox();
var bbox = geometry.boundingBox;
var points = [];
var dir = new THREE.Vector3(1, 1, 1).normalize();
for (var i = 0; i < count; i++) {
var p = setRandomVector(bbox.min, bbox.max);
points.push(p);
}
function setRandomVector(min, max) {
var v = new THREE.Vector3(
THREE.Math.randFloat(min.x, max.x),
THREE.Math.randFloat(min.y, max.y),
THREE.Math.randFloat(min.z, max.z)
);
if (!isInside(v)) {
return setRandomVector(min, max);
}
return v;
}
function isInside(v) {
ray.set(v, dir);
var counter = 0;
var pos = geometry.attributes.position;
var faces = pos.count / 3;
var vA = new THREE.Vector3(),
vB = new THREE.Vector3(),
vC = new THREE.Vector3();
for (var i = 0; i < faces; i++) {
vA.fromBufferAttribute(pos, i * 3 + 0);
vB.fromBufferAttribute(pos, i * 3 + 1);
vC.fromBufferAttribute(pos, i * 3 + 2);
if (ray.intersectTriangle(vA, vB, vC)) counter++;
}
return counter % 2 == 1;
}
return new THREE.BufferGeometry().setFromPoints(points);
},
init: function() {
this.config();
this.initScene();
this.attachEvents();
this.addShapes();
this.computePointsInside(0);
this.computePointsInside(1);
this.computePointsInside(2);
this.addText();
this.animate();
this.animationSequence();
}
}
sts.init();
body {
margin: 0;
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js" type="text/javascript"></script>
I added a slight delay to animationSequence
setTimeout(() => this.animationSequence(), 1000);
This is not the best approach and ideally you'd wait until particleGroup.children is loaded with data
After bit of investigation I found the culprit.
addText: function() {
loader.load(typeface, (font) => {
Wrap that in Promise
addText: function() {
return new Promise((resolve, reject) => {
loader.load(typeface, (font) => {
Use put rest of logic in then callback
this.addText().then(_ => {
this.animate();
this.animationSequence();
})
Working example
var renderer, camera, scene, light, shapes, triggers, particleCount, particleGroup,
defaultAnimationSpeed, morphAnimationSpeed, colorToStartWith, textGeometries, loader, typeface,
animationVars;
const MAX_PARTICLES = 100, PARTICLE_SIZE = .65;
var sts = {
config: function() {
shapes = [
{
"geoCode": new THREE.BoxBufferGeometry(13, 13, 13),
"textMat": new THREE.MeshPhongMaterial({ color: 0x029894 }),
"color": 0x029894,
"size": [50, 50, 50]
},
{
"geoCode": new THREE.SphereBufferGeometry(25, 33, 33),
"textMat": new THREE.MeshPhongMaterial({ color: 0x8F3985 }),
"color": 0x8F3985,
"size": [25, 33, 33]
},
{
"geoCode": new THREE.ConeBufferGeometry(25, 50, 30),
"textMat": new THREE.MeshPhongMaterial({ color: 0x11659C }),
"color": 0x11659C,
"size": [25, 50, 30]
}
];
triggers = document.getElementsByTagName('span');
particleGroup = new THREE.Group();
defaultAnimationSpeed = 1;
morphAnimationSpeed = 18;
normalSpeed = (defaultAnimationSpeed / 100),
fullSpeed = (morphAnimationSpeed / 100)
colorToStartWith = '#8F3985';
textGeometries = new Array();
},
initScene: function() {
//Renderer
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setClearColor(0xffffff, 1);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Scene
scene = new THREE.Scene();
// Camera and position
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.y = -25;
camera.position.z = 45;
camera.rotation.x = -.45;
// Lighting
light = new THREE.DirectionalLight( 0xffffff, 1 );
light.position.set( 1, 1, 1 ).normalize();
scene.add(light);
var light2 = new THREE.DirectionalLight( 0xffffff, 1.5 );
light2.position.set( 0, - 1, 0 );
scene.add( light2 );
// Texts
loader = new THREE.FontLoader();
typeface = 'https://raw.githubusercontent.com/7dir/json-fonts/master/fonts/cyrillic/roboto/Roboto_Regular.json';
//particleGroup
particleGroup = new THREE.Group();
particleGroup.position.x = 0;
particleGroup.position.y = -45;
particleGroup.position.z = 0;
scene.add(particleGroup);
},
fullScreen: function() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
},
attachEvents: function() {
window.addEventListener('resize', this.fullScreen, false);
},
addShapes: function() {
for (idx = 0; idx < shapes.length; idx++) {
shapes[idx].geometry = shapes[idx].geoCode.toNonIndexed();
m = new THREE.MeshLambertMaterial({
color: shapes[idx].color,
opacity: 0,
transparent: true,
wireframe: true
});
shapes[idx].geometry.center();
shape = new THREE.Mesh(shapes[idx].geometry, m);
scene.add(shape);
}
},
computePointsInside: function(idx) {
shapes[idx].points = this.fillWithPoints(shapes[idx].geometry, MAX_PARTICLES);
},
addText: function() {
return new Promise((resolve, reject) => {
loader.load(typeface, (font) => {
//var x, y, z, index1, index2;
//x = y = z = index1 = index2 = 0;
var m = new THREE.MeshPhongMaterial({color: 0x8F3985, opacity: .8, specular: 0xffffff, shininess: 100});
for (i = 0; i < MAX_PARTICLES; i++) {
g = new THREE.TextGeometry( Math.random() < .5 ? '6' : '9', {
font: font,
size: PARTICLE_SIZE,
height: 0.1
});
var text = new THREE.Mesh(g, m);
text.position.x = 0;
text.position.y = 0;
text.position.z = 0;
text.rotation.x = Math.random() * 2 * Math.PI;
text.rotation.y = Math.random() * 2 * Math.PI;
text.rotation.z = Math.random() * 2 * Math.PI;
text.scale.x = (Math.random() * (0.95 - 0.1) + 0.95).toFixed(4);
text.scale.y = (Math.random() * (0.95 - 0.1) + 0.95).toFixed(4);
text.scale.z = (Math.random() * (0.95 - 0.1) + 0.95).toFixed(4);
//var pointsInsideShape = shapes[0].points.attributes.position.array;
//text.position.x = pointsInsideShape[ index2 ++ ];
//text.position.y = pointsInsideShape[ index2 ++ ];
//text.position.z = pointsInsideShape[ index2 ++ ];
particleGroup.add(text);
}
resolve();
});
})
},
animationSequence: function() {
var x, y, z, index;
x = y = z = index = 0;
const children = particleGroup.children;
var pointsInsideShape = shapes[0].points.attributes.position.array;
for (i = 0; i < MAX_PARTICLES; i++) {
var tl = new TimelineLite();
const child = children[i];
tl.from(child.position, 2, {
x: pointsInsideShape[ index ++ ],
y: pointsInsideShape[ index ++ ],
z: pointsInsideShape[ index ++ ]
})
}
},
animate: function() {
window.requestAnimationFrame(sts.animate);
particleGroup.rotation.y +=0.005;
renderer.render(scene, camera)
},
fillWithPoints: function(geometry, count) {
var ray = new THREE.Ray()
var size = new THREE.Vector3();
geometry.computeBoundingBox();
var bbox = geometry.boundingBox;
var points = [];
var dir = new THREE.Vector3(1, 1, 1).normalize();
for (var i = 0; i < count; i++) {
var p = setRandomVector(bbox.min, bbox.max);
points.push(p);
}
function setRandomVector(min, max){
var v = new THREE.Vector3(
THREE.Math.randFloat(min.x, max.x),
THREE.Math.randFloat(min.y, max.y),
THREE.Math.randFloat(min.z, max.z)
);
if (!isInside(v)){return setRandomVector(min, max);}
return v;
}
function isInside(v){
ray.set(v, dir);
var counter = 0;
var pos = geometry.attributes.position;
var faces = pos.count / 3;
var vA = new THREE.Vector3(), vB = new THREE.Vector3(), vC = new THREE.Vector3();
for(var i = 0; i < faces; i++){
vA.fromBufferAttribute(pos, i * 3 + 0);
vB.fromBufferAttribute(pos, i * 3 + 1);
vC.fromBufferAttribute(pos, i * 3 + 2);
if (ray.intersectTriangle(vA, vB, vC)) counter++;
}
return counter % 2 == 1;
}
return new THREE.BufferGeometry().setFromPoints(points);
},
init: function() {
this.config();
this.initScene();
this.attachEvents();
this.addShapes();
this.computePointsInside(0);
this.computePointsInside(1);
this.computePointsInside(2);
this.addText().then(_ => {
this.animate();
this.animationSequence();
})
}
}
sts.init();
body {
margin: 0;
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js" type="text/javascript"></script>

Threejs / EffectComposer - Interactively masking an UnrealBloomPass

So, I am trying to dynamically mask an UnrealBloomPass with the EffectComposer and I am getting unexpected results. I am not sure if I am missing a key concept here, or if I should be trying to achieve this in a different way. Any input would be appreciated.
The composer is set up into these layers:
hexgradientPass (Main content)
maskingPass (I am trying to move this dynamically with the mouse)
bloomPass (Which I am trying to mask with ^)
clearMaskPass (Clearing the mask)
effectCopyPass (Applying the effects)
I've been following tutorials and examples as closely as possible and still no dice. Thanks in advance.
<!DOCTYPE html>
<html lang="en">
<head>
<title>BloomShader Mask</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
color: #fff;
font-family:Monospace;
font-size:13px;
text-align:center;
background-color: #fff;
margin: 0px;
overflow: hidden;
}
#info {
position: absolute;
top: 0px;
width: 100%;
padding: 5px;
}
#info p {
max-width: 600px;
margin-left: auto;
margin-right: auto;
padding: 0 2em;
}
a {
color: #2983ff;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="./build/three.js"></script>
<script src="js/libs/stats.min.js"></script>
<script src="js/libs/dat.gui.min.js"></script>
<script src="js/controls/OrbitControls.js"></script>
<script src="js/loaders/GLTFLoader.js"></script>
<script src="js/postprocessing/EffectComposer.js"></script>
<script src="js/postprocessing/RenderPass.js"></script>
<script src="js/postprocessing/ShaderPass.js"></script>
<script src="js/postprocessing/MaskPass.js "></script>
<script src="js/shaders/CopyShader.js"></script>
<script src="js/shaders/LuminosityHighPassShader.js"></script>
<script src="js/postprocessing/UnrealBloomPass.js"></script>
<script id="gradientShader" type="shader-code">
uniform vec2 resolution;
uniform vec2 mousePosition;
void main() {
vec2 pos = (gl_FragCoord.xy - mousePosition.xy * 1.2) / resolution.xy;
gl_FragColor = vec4(1.0, pos.x, pos.y, 1.0);
}
</script>
<script>
var clock = new THREE.Clock();
var stats;
var gui;
var sceneHexGradient;
var sceneMask;
var camera;
var orbitControls;
var pointLight;
var composer;
var mixer;
var maskerMesh;
var bloomPass;
var width = 0;
var height = 0;
var cameraZoom = 120;
var mousePositionY = 0;
var params = {
exposure: 2,
bloomStrength: .81,
bloomThreshold: 0,
bloomRadius: .05,
cameraZoom: cameraZoom
};
function init() {
// Init vars (DOM needed)
gui = initGui();
stats = initStats();
width = window.innerWidth;
height = window.innerHeight;
pixelRatio = window.devicePixelRatio;
// Renderer
var webGLRenderer = new THREE.WebGLRenderer({ antialias: true });
webGLRenderer.setClearColor(new THREE.Color(0x000000, 1.0));
webGLRenderer.setPixelRatio(pixelRatio);
webGLRenderer.setSize(width , height);
webGLRenderer.toneMapping = THREE.ReinhardToneMapping;
webGLRenderer.shadowMap.enabled = true;
appendChild(webGLRenderer.domElement);
// Scene
sceneHexGradient = new THREE.Scene();
sceneMasker = new THREE.Scene();
// Camera
camera = new THREE.OrthographicCamera(-width, width, height, -height, -1000, 1000);
camera.zoom = cameraZoom;
camera.lookAt(new THREE.Vector3(0, 0, 0));
camera.updateProjectionMatrix();
sceneHexGradient.add(camera);
sceneMasker.add(camera);
// Orbit
orbitControls = new THREE.OrbitControls(camera, webGLRenderer.domElement);
orbitControls.maxPolarAngle = Math.PI * 0.5;
orbitControls.minDistance = 1;
orbitControls.maxDistance = 10;
// Draw onto Scenes
drawGradientToScene(sceneHexGradient);
drawHexToScene(sceneHexGradient);
drawMaskerToScene(sceneMasker);
// ShaderPass
var hexgradientPass = new THREE.RenderPass(sceneHexGradient, camera);
hexgradientPass.clear = false;
var effectCopyPass = new THREE.ShaderPass(THREE.CopyShader);
effectCopyPass.renderToScreen = true;
var maskingPass = new THREE.MaskPass(sceneMasker, camera);
maskingPass.clear = true;
// var maskingPass = new THREE.RenderPass(sceneMasker, camera);
// maskingPass.renderToScreen = true;
bloomPass = new THREE.UnrealBloomPass(new THREE.Vector2(width, height), 1.5, 0.4, 0.85);
bloomPass.renderToScreen = true;
bloomPass.threshold = params.bloomThreshold;
bloomPass.strength = params.bloomStrength;
bloomPass.radius = params.bloomRadius;
var clearMaskPass = new THREE.ClearMaskPass();
composer = new THREE.EffectComposer(webGLRenderer);
composer.renderTarget1.stencilBuffer = true;
composer.renderTarget2.stencilBuffer = true;
composer.setSize(width, height);
composer.addPass(hexgradientPass);
composer.addPass(maskingPass);
// composer.addPass(maskingVerticalBlurPass);
composer.addPass(bloomPass);
composer.addPass(clearMaskPass);
composer.addPass(effectCopyPass);
render();
function initStats() { // Debug only
var stats = new Stats();
stats.setMode(0);
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
appendChild(stats.dom);
return stats;
}
function initGui() { // Debug only
gui = new dat.GUI();
gui.add(params, 'exposure', 0.1, 2 ).onChange((value) => {
webGLRenderer.toneMappingExposure = Math.pow(value, 4.0);
});
gui.add(params, 'bloomThreshold', 0.0, 1.0).onChange((value) => {
bloomPass.threshold = Number(value);
});
gui.add(params, 'bloomStrength', 0.0, 3.0).onChange((value) => {
bloomPass.strength = Number(value);
});
gui.add(params, 'bloomRadius', 0.0, 1.0).step(0.01).onChange((value) => {
bloomPass.radius = Number(value);
});
gui.add(params, 'cameraZoom', 100, 150).onChange((value) => {
camera.zoom = Number(value);
camera.updateProjectionMatrix();
});
return gui;
}
function appendChild(domElement) {
var container = document.getElementById('container');
if (container) {
return container.appendChild(domElement);
}
return false;
}
function drawGradientToScene(scene) {
var gradientUniforms = {};
gradientUniforms["resolution"] = { type:'v2', value:new THREE.Vector2(width, height)};
gradientUniforms['mousePosition'] = { type:'v2', value:new THREE.Vector2(0, 0) };
var shaderCode = document.getElementById('gradientShader').innerHTML;
var material = new THREE.ShaderMaterial({ uniforms:gradientUniforms, fragmentShader:shaderCode });
var geometry = new THREE.PlaneBufferGeometry(width, height);
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
}
function drawHexToScene(scene) {
var hexes = [];
var colCount = 100;
var rowCount = 100;
var hexDiameter = 1;
var xStart = -(colCount) * hexDiameter * 0.5;
var rowSpace = Math.sqrt(3) * (hexDiameter * 0.5);
var yStart = (rowCount - 1) * (rowSpace * 0.5);
var hexGeom = new THREE.CylinderGeometry(hexDiameter * .55, hexDiameter * .55, 0.0625, 6, 1);
hexGeom.rotateX(Math.PI * 0.5);
for (let j = 0; j < rowCount; j++) {
for (let i = 0; i < colCount + (j % 2 === 0 ? 0 : 1); i++) {
let hex = new THREE.Mesh(hexGeom, new THREE.MeshBasicMaterial({
color: 0x000000,
wireframe: false,
}));
var x = (xStart + i * hexDiameter + (j % 2 === 0 ? 0.5 * hexDiameter : 0));
var y = (yStart - j * rowSpace);
hex.position.set(x, y, 0);
hexes.push(hex);
scene.add(hex);
}
}
}
function drawMaskerToScene(scene) {
var boxGeometry = new THREE.BoxGeometry(width, height, 10);
var basicMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff });
maskerMesh = new THREE.Mesh(boxGeometry, basicMaterial);
maskerMesh.position.y = -height;
scene.add(maskerMesh);
}
function onResze() {
camera.position.set(0, 0, -15);
camera.zoom = cameraZoom;
camera.updateProjectionMatrix();
composer.setSize(width, height);
}
function onMouseMove(event) {
mousePositionY = event.clientY;
}
function render() {
webGLRenderer.autoClear = false;
stats.update();
var delta = clock.getDelta();
// orbitControls.update(delta);
requestAnimationFrame(render);
maskerMesh.position.y = mousePositionY;
// composer.setSize(window.innerWidth, window.innerHeight);
composer.render();
}
window.addEventListener('mousemove', onMouseMove);
} // End - init
// Listeners
window.onload = init;
window.onresize = init.onResze;
</script>
</body>
</html>
It was actually a combination of issues. The main issue was setting the flags on the passes, to clear or to render to screen. This wasn't an easy thing to debug:
<!DOCTYPE html>
<html lang="en">
<head>
<title>BloomShader Mask</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no,
minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
color: #fff;
font-family:Monospace;
font-size:13px;
text-align:center;
background-color: #fff;
margin: 0px;
overflow: hidden;
}
#info {
position: absolute;
top: 0px;
width: 100%;
padding: 5px;
}
#info p {
max-width: 600px;
margin-left: auto;
margin-right: auto;
padding: 0 2em;
}
a {
color: #2983ff;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="./build/three.js"></script>
<script src="js/libs/stats.min.js"></script>
<script src="js/libs/dat.gui.min.js"></script>
<script src="js/controls/OrbitControls.js"></script>
<script src="js/loaders/GLTFLoader.js"></script>
<script src="js/postprocessing/EffectComposer.js"></script>
<script src="js/postprocessing/ClearPass.js"></script>
<script src="js/postprocessing/RenderPass.js"></script>
<script src="js/postprocessing/ShaderPass.js"></script>
<script src="js/postprocessing/MaskPass.js "></script>
<script src="js/shaders/CopyShader.js"></script>
<script src="js/shaders/LuminosityHighPassShader.js"></script>
<script src="js/postprocessing/UnrealBloomPass.js"></script>
<script id="gradientShader" type="shader-code">
uniform vec2 resolution;
uniform vec2 mousePosition;
void main() {
vec2 pos = ((gl_FragCoord.xy - mousePosition.xy * 1.3) / resolution.xy);
gl_FragColor = vec4(1.0, pos.x, pos.y, 1.0);
}
</script>
<script>
var clock = new THREE.Clock();
var stats;
var gui;
var sceneHexGradient;
var sceneBloomMasker;
var sceneMasker;
var camera;
var orbitControls;
var pointLight;
var composer;
var mixer;
var maskerMesh;
var bloomPass;
var width = 0;
var height = 0;
var cameraZoom = 120;
var mousePositionY = 0;
var params = {
exposure: 2, //1.1
bloomStrength: 1.75,
bloomThreshold: 0,
bloomRadius: .16,
cameraZoom: cameraZoom
};
function init() {
// Init vars (DOM needed)
gui = initGui();
stats = initStats();
width = window.innerWidth;
height = window.innerHeight;
pixelRatio = window.devicePixelRatio;
// Camera
camera = new THREE.OrthographicCamera(-width, width, height, -height, -1000, 1000);
camera.zoom = cameraZoom;
camera.lookAt(new THREE.Vector3(0, 0, 0));
camera.updateProjectionMatrix();
// Renderer
var webGLRenderer = new THREE.WebGLRenderer({ antialias: true });
webGLRenderer.setClearColor(new THREE.Color(0x000000, 1.0));
webGLRenderer.setPixelRatio(pixelRatio);
webGLRenderer.setSize(width , height);
webGLRenderer.toneMapping = THREE.ReinhardToneMapping;
webGLRenderer.shadowMap.enabled = true;
webGLRenderer.autoClear = false;
appendChild(webGLRenderer.domElement);
// Scene
sceneHexGradient = new THREE.Scene();
sceneBloom = new THREE.Scene();
sceneMasker = new THREE.Scene();
sceneHexGradient.add(camera);
sceneBloom.add(camera);
sceneMasker.add(camera);
// Lights (are they needed ? )
sceneBloom.add(new THREE.AmbientLight(0xffffff));
// Orbit
orbitControls = new THREE.OrbitControls(camera, webGLRenderer.domElement);
orbitControls.maxPolarAngle = Math.PI * 0.5;
orbitControls.minDistance = 1;
orbitControls.maxDistance = 10;
// Draw onto Scenes
drawGradientToScene(sceneHexGradient);
drawHexToScene(sceneHexGradient);
drawMaskerToScene(sceneMasker);
bloomPass = new THREE.UnrealBloomPass(new THREE.Vector2(width, height), 1.5, 0.4, 0.85);
bloomPass.threshold = params.bloomThreshold;
bloomPass.strength = params.bloomStrength;
bloomPass.radius = params.bloomRadius;
// ShaderPasses
var clearPass = new THREE.ClearPass();
var clearMaskPass = new THREE.ClearMaskPass();
var hexgradientPass = new THREE.RenderPass(sceneHexGradient, camera);
var maskingPass = new THREE.MaskPass(sceneMasker, camera);
var outputPass = new THREE.ShaderPass(THREE.CopyShader);
outputPass.renderToScreen = true;
var parameters = {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBFormat,
stencilBuffer: true
};
var renderTarget = new THREE.WebGLRenderTarget(width, height, parameters);
composer = new THREE.EffectComposer(webGLRenderer, renderTarget);
composer.addPass(clearPass);
composer.addPass(hexgradientPass);
composer.addPass(maskingPass);
composer.addPass(bloomPass);
composer.addPass(clearMaskPass);
composer.addPass(outputPass);
function initStats() { // Debug only
var stats = new Stats();
stats.setMode(0);
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
appendChild(stats.dom);
return stats;
}
function initGui() { // Debug only
gui = new dat.GUI();
gui.add(params, 'exposure', 0.1, 2 ).onChange((value) => {
webGLRenderer.toneMappingExposure = Math.pow(value, 4.0);
});
gui.add(params, 'bloomThreshold', 0.0, 1.0).onChange((value) => {
bloomPass.threshold = Number(value);
});
gui.add(params, 'bloomStrength', 0.0, 3.0).onChange((value) => {
bloomPass.strength = Number(value);
});
gui.add(params, 'bloomRadius', 0.0, 1.0).step(0.01).onChange((value) => {
bloomPass.radius = Number(value);
});
gui.add(params, 'cameraZoom', 100, 150).onChange((value) => {
camera.zoom = Number(value);
camera.updateProjectionMatrix();
});
return gui;
}
function appendChild(domElement) {
var container = document.getElementById('container');
if (container) {
return container.appendChild(domElement);
}
return false;
}
function getRandomNumber(min = 1, max = 100) {
return Math.floor(Math.random() * (+max - +min)) + +min;
}
function getArrayOfRandomNumbers(count = 1, min = 1, max = 100) {
const outArr = [];
for( i = 0; i < count; ++i) {
outArr.push(getRandomNumber(min, max));
}
return outArr.sort();
}
function drawBlackoutMask(scene) {
// var x = 0, y = 0;
// var hexDiameter = 2;
// var shapeWidth = 10;
// var facets = getRandomNumber(1, shapeWidth);
// var xValuesArr = getArrayOfRandomNumbers(facets, 0, shapeWidth);
// var yValuesArr = getArrayOfRandomNumbers(facets, 0, shapeWidth);
// var hexGeom = new THREE.CylinderGeometry(hexDiameter * .55, hexDiameter * .55, 0.0625, 6, 1);
// hexGeom.rotateX(Math.PI * 0.5);
// var boxGeometry = new THREE.BoxGeometry(10, 10, 10);
// var mesh = new THREE.Mesh(boxGeometry, new THREE.MeshBasicMaterial({ color: 0xff0000 }));
// mesh.position.x = 0;
// mesh.position.y = 0;
// scene.add(mesh);
// for(var i = 0; i < facets; i++) {
// var hexGeom = new THREE.CylinderGeometry(hexDiameter * .55, hexDiameter * .55, 0.0625, 6, 1);
// var mesh = new THREE.Mesh(hexGeom, new THREE.MeshBasicMaterial({
// color: 0xff0000,
// wireframe: false,
// }));
// mesh.position.x = xValuesArr[i];
// mesh.position.y = yValuesArr[i];
// scene.add(mesh);
// }
}
function drawMaskerToScene(scene) {
var boxGeometry = new THREE.CylinderBufferGeometry(width, height, 10);
// var basicMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff });
boxGeometry.computeBoundingBox();
var material = new THREE.ShaderMaterial({
uniforms: {
color1: {
value: new THREE.Color(0xffffff)
},
color2: {
value: new THREE.Color(0x000000)
},
bboxMin: {
value: boxGeometry.boundingBox.min
},
bboxMax: {
value: boxGeometry.boundingBox.max
}
},
vertexShader: `
uniform vec3 bboxMin;
uniform vec3 bboxMax;
varying vec2 vUv;
void main() {
vUv.y = (position.y - bboxMin.y) / (bboxMax.y - bboxMin.y);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
`,
fragmentShader: `
uniform vec3 color1;
uniform vec3 color2;
varying vec2 vUv;
void main() {
gl_FragColor = vec4(mix(color1, color2, vUv.y), 1.0);
}
`,
wireframe: false
});
maskerMesh = new THREE.Mesh(boxGeometry, material); //basicMaterial);
// maskerMesh.scale.set(1,1,1);
// maskerMesh.position.y = -height;
scene.add(maskerMesh);
}
function drawGradientToScene(scene) {
var gradientUniforms = {};
gradientUniforms["resolution"] = { type:'v2', value:new THREE.Vector2(width, height)};
gradientUniforms['mousePosition'] = { type:'v2', value:new THREE.Vector2(0, 0) };
var shaderCode = document.getElementById('gradientShader').innerHTML;
var material = new THREE.ShaderMaterial({ uniforms:gradientUniforms, fragmentShader:shaderCode });
var geometry = new THREE.PlaneBufferGeometry(width, height);
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
}
function drawHexToScene(scene) {
var hexes = [];
var colCount = 100;
var rowCount = 100;
var hexDiameter = 1;
var xStart = -(colCount) * hexDiameter * 0.5;
var rowSpace = Math.sqrt(3) * (hexDiameter * 0.5);
var yStart = (rowCount - 1) * (rowSpace * 0.5);
var hexGeom = new THREE.CylinderGeometry(hexDiameter * .55, hexDiameter * .55, 0.0625, 6, 1);
hexGeom.rotateX(Math.PI * 0.5);
for (let j = 0; j < rowCount; j++) {
for (let i = 0; i < colCount + (j % 2 === 0 ? 0 : 1); i++) {
let hex = new THREE.Mesh(hexGeom, new THREE.MeshBasicMaterial({
color: 0x000000,
wireframe: false,
}));
var x = (xStart + i * hexDiameter + (j % 2 === 0 ? 0.5 * hexDiameter : 0));
var y = (yStart - j * rowSpace);
hex.position.set(x, y, 0);
hexes.push(hex);
scene.add(hex);
}
}
}
function onResze() {
camera.position.set(0, 0, -15);
camera.zoom = cameraZoom;
camera.updateProjectionMatrix();
composer.setSize(width, height);
}
function onMouseMove(event) {
mousePositionY = event.clientY;
}
function render() {
webGLRenderer.autoClear = false;
stats.update();
var delta = clock.getDelta();
// orbitControls.update(delta);
requestAnimationFrame(render);
maskerMesh.position.y = mousePositionY;
webGLRenderer.clear();
composer.render(delta);
}
window.addEventListener('mousemove', onMouseMove);
render();
} // End - init
// Listeners
window.onload = init;
window.onresize = init.onResze;
</script>
</body>

.points opacity / size within three.js

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>

Rotating an 3D object around it's y-axis in three.js

I just started exploring three.js and have been trying to adapt a project I found.
I would like to know if it would be possible to have the globe object rotate around it's y-axis with minor additions to the code or whether it has to be rewritten from the ground up.
var canvas = document.querySelector('canvas');
var width = canvas.offsetWidth,
height = canvas.offsetHeight;
var colors = [
new THREE.Color(0xac1122),
new THREE.Color(0x96789f),
new THREE.Color(0x535353)];
var renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio > 1 ? 2 : 1);
renderer.setSize(width, height);
renderer.setClearColor(0xffffff);
var scene = new THREE.Scene();
var raycaster = new THREE.Raycaster();
raycaster.params.Points.threshold = 6;
var camera = new THREE.PerspectiveCamera(50, width / height, 0.1, 2000);
camera.position.set(0, 0, 350);
var galaxy = new THREE.Group();
scene.add(galaxy);
// Create dots
var loader = new THREE.TextureLoader();
loader.crossOrigin = "";
var dotTexture = loader.load("img/dotTexture.png");
var dotsAmount = 3000;
var dotsGeometry = new THREE.Geometry();
var positions = new Float32Array(dotsAmount * 3);
var sizes = new Float32Array(dotsAmount);
var colorsAttribute = new Float32Array(dotsAmount * 3);
for (var i = 0; i < dotsAmount; i++) {
var vector = new THREE.Vector3();
vector.color = Math.floor(Math.random() * colors.length);
vector.theta = Math.random() * Math.PI * 2;
vector.phi =
(1 - Math.sqrt(Math.random())) *
Math.PI /
2 *
(Math.random() > 0.5 ? 1 : -1);
vector.x = Math.cos(vector.theta) * Math.cos(vector.phi);
vector.y = Math.sin(vector.phi);
vector.z = Math.sin(vector.theta) * Math.cos(vector.phi);
vector.multiplyScalar(120 + (Math.random() - 0.5) * 5);
vector.scaleX = 5;
if (Math.random() > 0.5) {
moveDot(vector, i);
}
dotsGeometry.vertices.push(vector);
vector.toArray(positions, i * 3);
colors[vector.color].toArray(colorsAttribute, i*3);
sizes[i] = 5;
}
function moveDot(vector, index) {
var tempVector = vector.clone();
tempVector.multiplyScalar((Math.random() - 0.5) * 0.2 + 1);
TweenMax.to(vector, Math.random() * 3 + 3, {
x: tempVector.x,
y: tempVector.y,
z: tempVector.z,
yoyo: true,
repeat: -1,
delay: -Math.random() * 3,
ease: Power0.easeNone,
onUpdate: function () {
attributePositions.array[index*3] = vector.x;
attributePositions.array[index*3+1] = vector.y;
attributePositions.array[index*3+2] = vector.z;
}
});
}
var bufferWrapGeom = new THREE.BufferGeometry();
var attributePositions = new THREE.BufferAttribute(positions, 3);
bufferWrapGeom.addAttribute('position', attributePositions);
var attributeSizes = new THREE.BufferAttribute(sizes, 1);
bufferWrapGeom.addAttribute('size', attributeSizes);
var attributeColors = new THREE.BufferAttribute(colorsAttribute, 3);
bufferWrapGeom.addAttribute('color', attributeColors);
var shaderMaterial = new THREE.ShaderMaterial({
uniforms: {
texture: {
value: dotTexture
}
},
vertexShader: document.getElementById("wrapVertexShader").textContent,
fragmentShader: document.getElementById("wrapFragmentShader").textContent,
transparent:true
});
var wrap = new THREE.Points(bufferWrapGeom, shaderMaterial);
scene.add(wrap);
// Create white segments
var segmentsGeom = new THREE.Geometry();
var segmentsMat = new THREE.LineBasicMaterial({
color: 0xffffff,
transparent: true,
opacity: 0.3,
vertexColors: THREE.VertexColors
});
for (i = dotsGeometry.vertices.length - 1; i >= 0; i--) {
vector = dotsGeometry.vertices[i];
for (var j = dotsGeometry.vertices.length - 1; j >= 0; j--) {
if (i !== j && vector.distanceTo(dotsGeometry.vertices[j]) < 12) {
segmentsGeom.vertices.push(vector);
segmentsGeom.vertices.push(dotsGeometry.vertices[j]);
segmentsGeom.colors.push(colors[vector.color]);
segmentsGeom.colors.push(colors[vector.color]);
}
}
}
var segments = new THREE.LineSegments(segmentsGeom, segmentsMat);
galaxy.add(segments);
var hovered = [];
var prevHovered = [];
function render(a) {
var i;
dotsGeometry.verticesNeedUpdate = true;
segmentsGeom.verticesNeedUpdate = true;
raycaster.setFromCamera( mouse, camera );
var intersections = raycaster.intersectObjects([wrap]);
hovered = [];
if (intersections.length) {
for(i = 0; i < intersections.length; i++) {
var index = intersections[i].index;
hovered.push(index);
if (prevHovered.indexOf(index) === -1) {
onDotHover(index);
}
}
}
for(i = 0; i < prevHovered.length; i++){
if(hovered.indexOf(prevHovered[i]) === -1){
mouseOut(prevHovered[i]);
}
}
prevHovered = hovered.slice(0);
attributeSizes.needsUpdate = true;
attributePositions.needsUpdate = true;
renderer.render(scene, camera);
}
function onDotHover(index) {
dotsGeometry.vertices[index].tl = new TimelineMax();
dotsGeometry.vertices[index].tl.to(dotsGeometry.vertices[index], 1, {
scaleX: 10,
ease: Elastic.easeOut.config(2, 0.2),
onUpdate: function() {
attributeSizes.array[index] = dotsGeometry.vertices[index].scaleX;
}
});
}
function mouseOut(index) {
dotsGeometry.vertices[index].tl.to(dotsGeometry.vertices[index], 0.4, {
scaleX: 5,
ease: Power2.easeOut,
onUpdate: function() {
attributeSizes.array[index] = dotsGeometry.vertices[index].scaleX;
}
});
}
function onResize() {
canvas.style.width = '';
canvas.style.height = '';
width = canvas.offsetWidth;
height = canvas.offsetHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
}
var mouse = new THREE.Vector2(-100,-100);
function onMouseMove(e) {
var canvasBounding = canvas.getBoundingClientRect();
mouse.x = ((e.clientX - canvasBounding.left) / width) * 2 - 1;
mouse.y = -((e.clientY - canvasBounding.top) / height) * 2 + 1;
}
TweenMax.ticker.addEventListener("tick", render);
window.addEventListener("mousemove", onMouseMove);
var resizeTm;
window.addEventListener("resize", function(){
resizeTm = clearTimeout(resizeTm);
resizeTm = setTimeout(onResize, 200);
});
Codepen here - https://codepen.io/quickwaste/pen/PaGPdw
Thanks.
(A stretch goal would be to have the camera move in response to mouse movement)
Simply add galaxy.rotateY(0.005 * Math.PI); to render(), right before renderer.render(scene, camera) call, like this:
// pulled from the CodePen
function render(a) {
// ... omitted for brevity
prevHovered = hovered.slice(0);
attributeSizes.needsUpdate = true;
attributePositions.needsUpdate = true;
galaxy.rotateY(0.005 * Math.PI);
renderer.render(scene, camera);
}
I used a multiplier of 0.005 to give the globe a nice, lazy spin.
The 'galaxy' object is a THREE.Group, a wrapper of sorts for collections of THREE.Object3D objects. The Object3D has all sorts of nifty functions to help rotate, translate, and transform 3D objects. The rotateY() will spin the model around its local y-axis.

THREE.js smooth material color cycle in separate geometries

guys!
Look at the codepen provided. Multiple curves animation.
So i'm trying to reach this smooth hue change at every drawn curve.
The color of every next curve should be shifted in hue a bit.
And i need to control the duration of this shifting.
Right now the colors shift seem random and i cant control it's duration.
Need your help. Thanks.
'use strict';
var camera, scene, renderer, controls;
var params = {P0x: 0, P0y: 0,P1x: 0.6, P1y: 1.7,P2x: -0.1, P2y: 1.1,P3x: 0, P3y: 3,steps: 30};
var controlPoints = [[params.P0x, params.P0y, 0],[params.P1x, params.P1y, 0],[params.P2x, params.P2y, 0],[params.P3x, params.P3y, 0]];
var material = new THREE.LineBasicMaterial( { color: 0xd9e2ec, linewidth: 1 } );
var mat = new THREE.MeshBasicMaterial({wireframe:true,color: 0x4a4a4a, side: THREE.DoubleSide, opacity:0, transparent:true});
var angle1 = 0;
var angle2 = 0;
var color = 0;
var initialCurvesCount = 5;
var initialGroupsCount = 6;
var curveQuality = 500;
var hColor = 1;
var mesh = {};
var axis1 = new THREE.Vector3(0,0.8,1.2);
var axis2 = new THREE.Vector3(0,-0.8,3.2);
var geom = {};
var curveGeometry;
var curves;
var group = {};
var triangle = [[ 0, 0.5, -0.5, 0 ], [ 0.6, -0.5, -0.5, 0.6 ], [ 0, 0, 0, 0 ]];
init();
createCurveGroups();
createHelpers();
animate();
function createHelpers() {
// var gridHelper = new THREE.GridHelper( 4, 8, 0xadd6e8, 0xdddddd );
// var gridHelper2 = new THREE.GridHelper( 4, 8, 0xadd6e8, 0xdddddd );
// gridHelper2.rotation.x = 1.58;
// gridHelper.position.y = 0;
// gridHelper.position.x = 0;
//
// var axisHelper = new THREE.AxisHelper( 1 );
// axisHelper.position.y = 0;
// axisHelper.position.x = 0;
//
// scene.add( gridHelper );
// scene.add( gridHelper2 );
// scene.add( axisHelper );
}
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 0.1, 10000);
camera.position.set(-0,0,2);
camera.rotation.y = -0;
camera.frustumCulled = false;
controls = new THREE.OrbitControls( camera );
controls.addEventListener( 'change', render );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setClearColor( 0xffffff, 1 );
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function createBezierCurveNEW(cpList, steps) {
var N = Math.round(steps)+1 || 20;
var geometry = new THREE.Geometry();
var curve = new THREE.CubicBezierCurve3();
var cp = cpList[0];
curve.v0 = new THREE.Vector3(cp[0], cp[1], cp[2]);
cp = cpList[1];
curve.v1 = new THREE.Vector3(cp[0], cp[1], cp[2]);
cp = cpList[2];
curve.v2 = new THREE.Vector3(cp[0], cp[1], cp[2]);
cp = cpList[3];
curve.v3 = new THREE.Vector3(cp[0], cp[1], cp[2]);
var j, stepSize = 1/(N-1);
for (j = 0; j < N; j++) {
geometry.vertices.push( curve.getPoint(j * stepSize) );
}
return geometry;
};
function createTriangle(number) {
geom[number] = new THREE.Geometry();
geom[number].vertices.push(new THREE.Vector3(0, 0.35, 0));
geom[number].vertices.push(new THREE.Vector3(0.35, -0.35, 0));
geom[number].vertices.push(new THREE.Vector3(-0.35,-0.35, 0));
geom[number].faces.push(new THREE.Face3(0, 1, 2));
geom[number].applyMatrix( new THREE.Matrix4().makeTranslation( 0, 0, 0 ) );
mesh[number] = new THREE.Mesh(geom[number], mat);
};
function createCurveGroups() {
for ( var i = 1; i <= initialGroupsCount; ++i ) {
group[i] = new THREE.Group();
scene.add( group[i] );
group[i].rotation.set( 0, 3.15, i/((initialGroupsCount/6 - initialGroupsCount/130)) );
};
};
function cloneCurvesToGroups() {
for ( var i = 1; i <= (Object.keys(group).length); ++i ) {
var curvesArray = {};
curvesArray[i] = curves.clone();
group[i].add(curvesArray[i]);
}
};
function colorChanger() {
}
function morphTriangle() {
group[1].add( mesh[1] );
mesh[1].rotateOnAxis(axis1,(angle2 + 1));
mesh[1].updateMatrix();
mesh[1].geometry.applyMatrix( mesh[1].matrix );
mesh[1].matrix.identity();
mesh[1].position.set( 0, 0, 0 );
mesh[1].geometry.verticesNeedUpdate = true;
group[1].add( mesh[2] );
mesh[2].rotateOnAxis(axis2,-angle2);
mesh[2].updateMatrix();
mesh[2].geometry.applyMatrix( mesh[2].matrix );
mesh[2].matrix.identity();
mesh[2].position.set( 0, 0, 0 );
mesh[2].geometry.verticesNeedUpdate = true;
};
function changeCreatedCurves() {
angle1 += 0.00450;
angle2 += 0.0020;
createTriangle(1);
createTriangle(2);
morphTriangle();
for ( var i = 1; i <= initialCurvesCount; ++i ) {
controlPoints[0][0] = -0.09 ;
controlPoints[0][1] = 0;
controlPoints[0][2] = -0.035 + i/10000; //optional + Math.sin(angle1)/6;
controlPoints[2][0] = mesh[2].geometry.vertices[0]['x'] + 0.1 - i/55 + Math.cos(angle1)/6;
controlPoints[2][1] = mesh[2].geometry.vertices[0]['y'];
controlPoints[2][2] = mesh[2].geometry.vertices[0]['z'] + i/20 + Math.sin(angle1)/6;
controlPoints[1][0] = mesh[1].geometry.vertices[0]['x'] - i/20 + Math.sin(angle1)/6;
controlPoints[1][1] = mesh[1].geometry.vertices[0]['y'];
controlPoints[1][2] = mesh[1].geometry.vertices[0]['z'] - i/20 + Math.sin(angle1)/6;
controlPoints[3][0] = triangle[0][0] - 0.05 + i/10;
controlPoints[3][1] = triangle[1][0] - 0.05 + i/10;
controlPoints[3][2] = triangle[2][0];
// !!! HERE IS THE PROBLEM !!!
hColor = hColor + i*0.3;
var wow = String("hsl(" + hColor*i + "," + 100 + "%" + "," + 70 + "%" + ")");
// console.log(wow)
// console.log("this is i "+ i);
material = new THREE.LineBasicMaterial( { color: wow, linewidth: 1 } );
// !!! HERE IS THE PROBLEM !!!
curveGeometry = createBezierCurveNEW(controlPoints, (curveQuality/initialGroupsCount));
curves = new THREE.Line(curveGeometry, material);
group[1].add(curves);
// debugger
render();
cloneCurvesToGroups();
}
};
function disposeCurveGeometry() {
for (var i = 0; i <= group[1].children.length; ++i) {
group[1].children[0].geometry.dispose();
group[1].children[0].material.dispose();
for (var j = 1; j <= (Object.keys(group).length); ++j) {
group[j].remove(group[j].children[0]);
};
};
};
function render() {
renderer.render(scene, camera);
};
function animate() {
requestAnimationFrame(animate);
changeCreatedCurves();
disposeCurveGeometry();
onWindowResize();
};
Working example code at codepen
Is something like this what you are looking for?
var vueDifference = 20;
var speed = 0.03;
hColor = hColor + speed;
var wow = String("hsl(" + (hColor + i * vueDifference) + "," + 100 + "%" + "," + 70 + "%" + ")");

Categories

Resources