Gif not playing over canvas animation on Firefox - javascript

I'm having this weird bug on Firefox where a gif used as backgound image in not playing when there's a canvas animation. Like on this example. If you check on Webkit, it's totally fine.
const separation = 100, amountX = 70, amountY = 50;
let container, camera, scene, renderer, particles;
let count = 0, windowHalfX = window.innerWidth / 2, windowHalfY = window.innerHeight / 2, cameraPosition = 80;
const init = () => {
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.x = cameraPosition;
camera.position.y = 1000;
camera.position.z = -550;
camera.zoom = 1.2;
scene = new THREE.Scene();
const numParticles = amountX * amountY;
const positions = new Float32Array(numParticles * 3);
const scales = new Float32Array(numParticles);
let i = 0, j = 0;
for(let ix = 0; ix < amountX; ix++) {
for(let iy = 0; iy < amountY; iy++) {
positions[i] = ix * separation - ((amountX * separation ) / 2);
positions[i + 1] = 0;
positions[i + 2] = iy * separation - ((amountY * separation ) / 2);
scales[j] = 1;
i += 3;
j ++;
}
}
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('scale', new THREE.BufferAttribute(scales, 1));
const material = new THREE.ShaderMaterial({
uniforms: {
color: {value: new THREE.Color(0xeae6c3)},
},
vertexShader: document.getElementById('vertexshader').textContent,
fragmentShader: document.getElementById('fragmentshader').textContent,
});
particles = new THREE.Points(geometry, material);
scene.add(particles);
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setClearColor(0x192735, 1);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
container.style.touchAction = 'none';
window.addEventListener( 'resize', onWindowResize );
};
const onWindowResize = () => {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
};
const render = () => {
camera.lookAt(scene.position);
const positions = particles.geometry.attributes.position.array;
const scales = particles.geometry.attributes.scale.array;
let i = 0, j = 0;
for (let ix = 0; ix < amountX; ix ++) {
for (let iy = 0; iy < amountY; iy ++) {
positions[i + 1] = (Math.sin((ix + count) * 0.3) * 50) + (Math.sin((iy + count) * 0.5) * 50);
scales[j] = (Math.sin((ix + count) * 0.3) + 1) * 7 + (Math.sin((iy + count) * 0.5) + 1) * 7;
i += 3;
j ++;
}
}
particles.geometry.attributes.position.needsUpdate = true;
particles.geometry.attributes.scale.needsUpdate = true;
renderer.render(scene, camera);
count += 0.011;
};
const animate = () => {
requestAnimationFrame(animate);
render();
};
init();
animate();
const moveCamera = () => {
cameraPosition = 150;
};
body::after {
content: '';
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1000;
background-image: url(https://res.cloudinary.com/axiol/image/upload/v1612477975/CodePen/noise.gif);
opacity: 0.05;
pointer-events: none;
}
canvas {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: none;
z-index: -1;
}
<script src="https://unpkg.com/three#0.125.2/build/three.min.js"></script>
<script type="x-shader/x-vertex" id="vertexshader">
attribute float scale;
void main() {
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
gl_PointSize = scale * (300.0 / - mvPosition.z);
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script type="x-shader/x-fragment" id="fragmentshader">
uniform vec3 color;
void main() {
if (length(gl_PointCoord - vec2(0.5, 0.5)) > 0.475 ) discard;
gl_FragColor = vec4(color, 1.0);
}
</script>
And I really have no idea where this could come from. On another test I have the same issue. But on this second one the canvas is reacting to the mouse movements. And when the mouse moves, the gif is animated. But once it stop the gif stops.
Anybody already saw that?

This is indeed a recent (one week) regression, apparently caused by this commit.
I did open https://bugzilla.mozilla.org/1692736, let's hope they can fix it before it lands on stable branch.
You can workaround this bug either by disabling WebRender in your own Firefox by going to about:config and then toggle gfx.webrender.force-disabled to false, or by forcing a re-rendering of the element where the gif is rendered, e.g through a minimal opacity variation in a CSS animation:
const separation = 100, amountX = 70, amountY = 50;
let container, camera, scene, renderer, particles;
let count = 0, windowHalfX = window.innerWidth / 2, windowHalfY = window.innerHeight / 2, cameraPosition = 80;
const init = () => {
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.x = cameraPosition;
camera.position.y = 1000;
camera.position.z = -550;
camera.zoom = 1.2;
scene = new THREE.Scene();
const numParticles = amountX * amountY;
const positions = new Float32Array(numParticles * 3);
const scales = new Float32Array(numParticles);
let i = 0, j = 0;
for(let ix = 0; ix < amountX; ix++) {
for(let iy = 0; iy < amountY; iy++) {
positions[i] = ix * separation - ((amountX * separation ) / 2);
positions[i + 1] = 0;
positions[i + 2] = iy * separation - ((amountY * separation ) / 2);
scales[j] = 1;
i += 3;
j ++;
}
}
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('scale', new THREE.BufferAttribute(scales, 1));
const material = new THREE.ShaderMaterial({
uniforms: {
color: {value: new THREE.Color(0xeae6c3)},
},
vertexShader: document.getElementById('vertexshader').textContent,
fragmentShader: document.getElementById('fragmentshader').textContent,
});
particles = new THREE.Points(geometry, material);
scene.add(particles);
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setClearColor(0x192735, 1);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
container.style.touchAction = 'none';
window.addEventListener( 'resize', onWindowResize );
};
const onWindowResize = () => {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
};
const render = () => {
camera.lookAt(scene.position);
const positions = particles.geometry.attributes.position.array;
const scales = particles.geometry.attributes.scale.array;
let i = 0, j = 0;
for (let ix = 0; ix < amountX; ix ++) {
for (let iy = 0; iy < amountY; iy ++) {
positions[i + 1] = (Math.sin((ix + count) * 0.3) * 50) + (Math.sin((iy + count) * 0.5) * 50);
scales[j] = (Math.sin((ix + count) * 0.3) + 1) * 7 + (Math.sin((iy + count) * 0.5) + 1) * 7;
i += 3;
j ++;
}
}
particles.geometry.attributes.position.needsUpdate = true;
particles.geometry.attributes.scale.needsUpdate = true;
renderer.render(scene, camera);
count += 0.011;
};
const animate = () => {
requestAnimationFrame(animate);
render();
};
init();
animate();
const moveCamera = () => {
cameraPosition = 150;
};
/*
we animate a very small opacity variation
to force rerendering of the gif image
*/
#keyframes bug1692736 { to { opacity: 0.051; } }
body::after {
animation: bug1692736 10s infinite;
content: '';
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1000;
background-image: url(https://res.cloudinary.com/axiol/image/upload/v1612477975/CodePen/noise.gif);
opacity: 0.05;
pointer-events: none;
}
canvas {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: none;
z-index: -1;
}
<script src="https://unpkg.com/three#0.125.2/build/three.min.js"></script>
<script type="x-shader/x-vertex" id="vertexshader">
attribute float scale;
void main() {
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
gl_PointSize = scale * (300.0 / - mvPosition.z);
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script type="x-shader/x-fragment" id="fragmentshader">
uniform vec3 color;
void main() {
if (length(gl_PointCoord - vec2(0.5, 0.5)) > 0.475 ) discard;
gl_FragColor = vec4(color, 1.0);
}
</script>

Related

Trying to simulate a 3D effect via Three.js

I'm trying to achieve something akin to this amazing effect : https://www.cobosrl.co/
Here's what I have so far : https://codepen.io/routsou/pen/ZEGWJgR?editors=0010
/*--------------------
Setup
--------------------*/
console.clear();
const canvas = document.querySelector('#bubble');
//wobble
let mouseDown = false;
let howMuch = 0;
let howMuchLimit = 0.25;
//ripple
let rippleAmount = 0;
let rippleRatio = 5;
let step = 0;
let sphereVerticesArray = [];
let sphereVerticesNormArray = [];
//raycaster
let raycaster;
let INTERSECTED = null;
let width = canvas.offsetWidth,
height = canvas.offsetHeight;
const renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true,
alpha: true
});
const scene = new THREE.Scene();
const setup = () => {
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize(width, height);
renderer.setClearColor(0xebebeb, 0);
renderer.shadowMap.enabled = true;
renderer.shadowMapSoft = true;
scene.fog = new THREE.Fog(0x000000, 10, 950);
const aspectRatio = width / height;
const fieldOfView = 100;
const nearPlane = 0.1;
const farPlane = 10000;
camera = new THREE.PerspectiveCamera(
fieldOfView,
aspectRatio,
nearPlane,
farPlane
);
raycaster = new THREE.Raycaster();
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 300;
}
setup();
/*--------------------
Lights
--------------------*/
let hemispshereLight, shadowLight, light2;
const createLights = () => {
hemisphereLight = new THREE.HemisphereLight(0xffffff,0x000000, .5)
shadowLight = new THREE.DirectionalLight(0x666666, .4);
shadowLight.position.set(0, 450, 350);
shadowLight.castShadow = true;
shadowLight.shadow.camera.left = -650;
shadowLight.shadow.camera.right = 650;
shadowLight.shadow.camera.top = 650;
shadowLight.shadow.camera.bottom = -650;
shadowLight.shadow.camera.near = 1;
shadowLight.shadow.camera.far = 1000;
shadowLight.shadow.mapSize.width = 4096;
shadowLight.shadow.mapSize.height = 4096;
light2 = new THREE.DirectionalLight(0x666666, .25);
light2.position.set(-600, 350, 350);
light3 = new THREE.DirectionalLight(0x666666, .15);
light3.position.set(0, -250, 300);
scene.add(hemisphereLight);
scene.add(shadowLight);
scene.add(light2);
scene.add(light3);
}
createLights();
/*--------------------
Bubble
--------------------*/
const vertex = width > 575 ? 80 : 40;
const bubbleGeometry = new THREE.SphereGeometry( 150, vertex, vertex );
let bubble;
const createBubble = () => {
for(let i = 0; i < bubbleGeometry.vertices.length; i++) {
let vector = bubbleGeometry.vertices[i];
vector.original = vector.clone();
}
const bubbleMaterial = new THREE.MeshStandardMaterial({
emissive: 0x91176b,
emissiveIntensity: 0.85,
roughness: 0.55,
metalness: 0.51,
side: THREE.FrontSide,
});
// save points for later calculation
for (var i = 0; i < bubbleGeometry.vertices.length; i += 1) {
var vertex = bubbleGeometry.vertices[i];
var vec = new THREE.Vector3(vertex.x, vertex.y, vertex.z);
sphereVerticesArray.push(vec);
var mag = vec.x * vec.x + vec.y * vec.y + vec.z * vec.z;
mag = Math.sqrt(mag);
var norm = new THREE.Vector3(vertex.x / mag, vertex.y / mag, vertex.z / mag);
sphereVerticesNormArray.push(norm);
}
bubble = new THREE.Mesh(bubbleGeometry, bubbleMaterial);
bubble.castShadow = true;
bubble.receiveShadow = false;
bubble.rotation.y = -90;
scene.add(bubble);
}
createBubble();
/*--------------------
Plane
--------------------*/
const createPlane = () => {
const planeGeometry = new THREE.PlaneBufferGeometry( 2000, 2000 );
const planeMaterial = new THREE.ShadowMaterial({
opacity: 0.15
});
const plane = new THREE.Mesh( planeGeometry, planeMaterial );
plane.position.y = -150;
plane.position.x = 0;
plane.position.z = 0;
plane.rotation.x = Math.PI / 180 * -90;
plane.receiveShadow = true;
scene.add(plane);
}
createPlane();
/*--------------------
Map
--------------------*/
const map = (num, in_min, in_max, out_min, out_max) => {
return (num - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
/*--------------------
Distance
--------------------*/
const distance = (a, b) => {
const dx = a.x - b.x;
const dy = a.y - b.y;
const d = Math.sqrt( dx * dx + dy * dy );
return d;
}
/*--------------------
Mouse
--------------------*/
let mouse = new THREE.Vector2(0, 0);
const onMouseMove = (e) => {
TweenMax.to(mouse, 0.8, {
x : ( e.clientX / window.innerWidth ) * 2 - 1,
y: - ( e.clientY / window.innerHeight ) * 2 + 1,
ease: Power2.easeOut
});
raycaster.setFromCamera( mouse, camera );
let intersects = raycaster.intersectObjects( scene.children );
try{
if ( intersects.length > 0 ) {
if ( INTERSECTED != intersects[ 0 ].object ) {
if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
INTERSECTED = intersects[ 0 ].object;
INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
INTERSECTED.material.emissive.setHex( 0x000000 );
document.body.style.cursor = 'pointer';
}
} else {
if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
INTERSECTED = null;
document.body.style.cursor = 'auto';
}
}catch(e){
}
};
['mousemove', 'touchmove'].forEach(event => {
window.addEventListener(event, onMouseMove);
});
/*--------------------
Spring
--------------------*/
let spring = {
scale: 1
};
const clicking = {
down: () => {
mouseDown = true;
},
up: () => {
mouseDown = false;
}
};
['mousedown', 'touchstart'].forEach(event => {
window.addEventListener(event, clicking.down);
});
['mouseup', 'touchend'].forEach(event => {
window.addEventListener(event, clicking.up);
});
/*--------------------
Resize
--------------------*/
const onResize = () => {
canvas.style.width = '';
canvas.style.height = '';
width = canvas.offsetWidth;
height = canvas.offsetHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
maxDist = distance(mouse, {x: width / 2, y: height / 2});
renderer.setSize(width, height);
}
let resizeTm;
window.addEventListener('resize', function(){
resizeTm = clearTimeout(resizeTm);
resizeTm = setTimeout(onResize, 200);
});
/*--------------------
Noise
--------------------*/
let dist = new THREE.Vector2(0, 0);
let maxDist = distance(mouse, {x: width / 2, y: height / 2});
const updateVertices = (time) => {
dist = distance(mouse, {x: width / 2, y: height / 2});
dist /= maxDist;
dist = map(dist, 1, 0, 0, 1);
for(let i = 0; i < bubbleGeometry.vertices.length; i++) {
let vector = bubbleGeometry.vertices[i];
vector.copy(vector.original);
let perlin = noise.simplex3(
(vector.x * 0.006) + (time * 0.0005),
(vector.y * 0.006) + (time * 0.0005),
(vector.z * 0.006)
);
let ratio = ((perlin * 0.3 * (howMuch + 0.1)) + 0.9);
vector.multiplyScalar(ratio);
}
bubbleGeometry.verticesNeedUpdate = true;
}
/*--------------------
Animate
--------------------*/
const render = (a) => {
step +=1;
requestAnimationFrame(render);
//bubble.scale.set(spring.scale, spring.scale, spring.scale);
updateVertices(a);
renderer.clear();
renderer.render(scene, camera);
//Activate on mouse down
if(mouseDown && howMuch < howMuchLimit)
howMuch += 0.01;
else if (howMuch > 0)
howMuch -= 0.01;
if(INTERSECTED){
if(rippleAmount < 10)
rippleAmount += 0.05;
}else if(rippleAmount > 0)
rippleAmount -= 0.05;
doRipple();
}
requestAnimationFrame(render);
renderer.render(scene, camera);
/*--------------------
Helpers
--------------------*/
function fbm(p) {
var result = noise.simplex3(p._x, p._y, p._z);
return result;
}
function addPoint(arr) {
var r = new Point(0, 0, 0);
var len = arr.length;
for (var i = 0; i < len; i += 1) {
r._x += arr[i]._x;
r._y += arr[i]._y;
r._z += arr[i]._z;
}
return r;
}
function Point(_x=0, _y=0, _z=0) {
this._x = _x;
this._y = _y;
this._z = _z;
}
function ripple(p) {
var q = new Point(fbm(addPoint([p, new Point(0, 0, 0)])),
fbm(addPoint([p, new Point(0, 1, 0)])),
fbm(addPoint([p, new Point(0, 0, 1)])));
return fbm(addPoint([p, new Point(0.5 * q._x, 0.5 * q._y, 0.5 * q._z)]));
}
function doRipple(){
//ripple
for (var i = 0; i < bubbleGeometry.vertices.length; i += 1) {
var vertex = bubbleGeometry.vertices[i];
// var value = pn.noise((vertex.x + step)/ 10, vertex.y / 10, vertex.z / 10);
var value = ripple(new Point((vertex.x + step) / 100.0), vertex.y / 100.0, vertex.z / 100.0);
vertex.x = sphereVerticesArray[i].x + sphereVerticesNormArray[i].x * value * rippleAmount;
vertex.y = sphereVerticesArray[i].y + sphereVerticesNormArray[i].y * value * rippleAmount;
vertex.z = sphereVerticesArray[i].z + sphereVerticesNormArray[i].z * value * rippleAmount;
}
bubbleGeometry.computeFaceNormals();
bubbleGeometry.computeVertexNormals();
bubbleGeometry.verticesNeedUpdate = true;
bubbleGeometry.normalsNeedUpdate = true;
}
Any help, particularly about the mouse pointer "sculpting the geometry", and the waves being more natural and from the pointer?
Thank you very much in advance
I've investigated and found you're intersecting with all children (6) in the scene, including the bubble shadow and the lights. The shadow seems to also intersect with the mouse triggering a false contact.
About "sculpting the geometry" I noticed you hardcode the ripple effect from one specific point of the bubble during initial construction and that's why the sculpting effect is always from that same point. This is my recommendation:
Remove the hard-coded sphereVerticesArray and sphereVerticesNormArray.
After computing the intersection with the mouse, find out the face of the bubble getting hit: intersections[0].point provides the point of intersection, in world coordinates. Use this to find out the face of contact.
During ripple effect use the normal of the contact face as starting point and orientation of the ripple.
This is the code to fix the shadow intersection issue including some comments:
/*--------------------
Setup
--------------------*/
console.clear();
const canvas = document.querySelector('#bubble');
//wobble
let mouseDown = false;
let howMuch = 0;
let howMuchLimit = 0.25;
//ripple
let rippleAmount = 0;
let rippleRatio = 5;
let step = 0;
let sphereVerticesArray = [];
let sphereVerticesNormArray = [];
//raycaster
let raycaster;
let isIntersectingWithBubble = false;
let width = canvas.offsetWidth,
height = canvas.offsetHeight;
const renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true,
alpha: true
});
const scene = new THREE.Scene();
const setup = () => {
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize(width, height);
renderer.setClearColor(0xebebeb, 0);
renderer.shadowMap.enabled = true;
renderer.shadowMapSoft = true;
scene.fog = new THREE.Fog(0x000000, 10, 950);
const aspectRatio = width / height;
const fieldOfView = 100;
const nearPlane = 0.1;
const farPlane = 10000;
camera = new THREE.PerspectiveCamera(
fieldOfView,
aspectRatio,
nearPlane,
farPlane
);
raycaster = new THREE.Raycaster();
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 300;
}
setup();
/*--------------------
Lights
--------------------*/
let hemispshereLight, shadowLight, light2;
const createLights = () => {
hemisphereLight = new THREE.HemisphereLight(0xffffff,0x000000, .5)
shadowLight = new THREE.DirectionalLight(0x666666, .4);
shadowLight.position.set(0, 450, 350);
shadowLight.castShadow = true;
shadowLight.shadow.camera.left = -650;
shadowLight.shadow.camera.right = 650;
shadowLight.shadow.camera.top = 650;
shadowLight.shadow.camera.bottom = -650;
shadowLight.shadow.camera.near = 1;
shadowLight.shadow.camera.far = 1000;
shadowLight.shadow.mapSize.width = 4096;
shadowLight.shadow.mapSize.height = 4096;
light2 = new THREE.DirectionalLight(0x666666, .25);
light2.position.set(-600, 350, 350);
light3 = new THREE.DirectionalLight(0x666666, .15);
light3.position.set(0, -250, 300);
scene.add(hemisphereLight);
scene.add(shadowLight);
scene.add(light2);
scene.add(light3);
}
createLights();
/*--------------------
Bubble
--------------------*/
const vertex = width > 575 ? 80 : 40;
const bubbleGeometry = new THREE.SphereGeometry( 150, vertex, vertex );
const bubbleEmissive = 0x91176b;
const bubbleEmissiveOnContact = 0x000000;
const createBubble = () => {
for(let i = 0; i < bubbleGeometry.vertices.length; i++) {
let vector = bubbleGeometry.vertices[i];
vector.original = vector.clone();
}
const bubbleMaterial = new THREE.MeshStandardMaterial({
emissive: bubbleEmissive,
emissiveIntensity: 0.85,
roughness: 0.55,
metalness: 0.51,
side: THREE.FrontSide,
});
// save points for later calculation
for (var i = 0; i < bubbleGeometry.vertices.length; i += 1) {
var vertex = bubbleGeometry.vertices[i];
var vec = new THREE.Vector3(vertex.x, vertex.y, vertex.z);
sphereVerticesArray.push(vec);
var mag = vec.x * vec.x + vec.y * vec.y + vec.z * vec.z;
mag = Math.sqrt(mag);
var norm = new THREE.Vector3(vertex.x / mag, vertex.y / mag, vertex.z / mag);
sphereVerticesNormArray.push(norm);
}
const _bubble = new THREE.Mesh(bubbleGeometry, bubbleMaterial);
_bubble.castShadow = true;
_bubble.receiveShadow = false;
_bubble.rotation.y = -90;
scene.add(_bubble);
return _bubble;
}
const bubble = createBubble();
/*--------------------
Plane
--------------------*/
const createPlane = () => {
const planeGeometry = new THREE.PlaneBufferGeometry( 2000, 2000 );
const planeMaterial = new THREE.ShadowMaterial({
opacity: 0.15
});
const plane = new THREE.Mesh( planeGeometry, planeMaterial );
plane.position.y = -150;
plane.position.x = 0;
plane.position.z = 0;
plane.rotation.x = Math.PI / 180 * -90;
plane.receiveShadow = true;
scene.add(plane);
}
createPlane();
/*--------------------
Map
--------------------*/
const map = (num, in_min, in_max, out_min, out_max) => {
return (num - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
/*--------------------
Distance
--------------------*/
const distance = (a, b) => {
const dx = a.x - b.x;
const dy = a.y - b.y;
const d = Math.sqrt( dx * dx + dy * dy );
return d;
}
/*--------------------
Mouse
--------------------*/
let mouse = new THREE.Vector2(0, 0);
const onMouseMove = (e) => {
TweenMax.to(mouse, 0.8, {
x : ( e.clientX / window.innerWidth ) * 2 - 1,
y: - ( e.clientY / window.innerHeight ) * 2 + 1,
ease: Power2.easeOut
});
raycaster.setFromCamera( mouse, camera );
isIntersectingWithBubble = raycaster.intersectObject( bubble ).length > 0; // we are only interested in intersections with the bubble object
try {
if (isIntersectingWithBubble) {
// is intersecting: change color, change pointer, change point of contact
bubble.material.emissive.setHex(bubbleEmissiveOnContact);
document.body.style.cursor = 'pointer';
} else {
// is not intersecting: restore color, restore pointer, remove point of contact
bubble.material.emissive.setHex(bubbleEmissive);
document.body.style.cursor = 'auto';
}
} catch (e) {
}
};
['mousemove', 'touchmove'].forEach(event => {
window.addEventListener(event, onMouseMove);
});
/*--------------------
Spring
--------------------*/
let spring = {
scale: 1
};
const clicking = {
down: () => {
mouseDown = true;
},
up: () => {
mouseDown = false;
}
};
['mousedown', 'touchstart'].forEach(event => {
window.addEventListener(event, clicking.down);
});
['mouseup', 'touchend'].forEach(event => {
window.addEventListener(event, clicking.up);
});
/*--------------------
Resize
--------------------*/
const onResize = () => {
canvas.style.width = '';
canvas.style.height = '';
width = canvas.offsetWidth;
height = canvas.offsetHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
maxDist = distance(mouse, {x: width / 2, y: height / 2});
renderer.setSize(width, height);
}
let resizeTm;
window.addEventListener('resize', function(){
resizeTm = clearTimeout(resizeTm);
resizeTm = setTimeout(onResize, 200);
});
/*--------------------
Noise
--------------------*/
let dist = new THREE.Vector2(0, 0);
let maxDist = distance(mouse, {x: width / 2, y: height / 2});
const updateVertices = (time) => {
dist = distance(mouse, {x: width / 2, y: height / 2});
dist /= maxDist;
dist = map(dist, 1, 0, 0, 1);
for(let i = 0; i < bubbleGeometry.vertices.length; i++) {
let vector = bubbleGeometry.vertices[i];
vector.copy(vector.original);
let perlin = noise.simplex3(
(vector.x * 0.006) + (time * 0.0005),
(vector.y * 0.006) + (time * 0.0005),
(vector.z * 0.006)
);
let ratio = ((perlin * 0.3 * (howMuch + 0.1)) + 0.9);
vector.multiplyScalar(ratio);
}
bubbleGeometry.verticesNeedUpdate = true;
}
/*--------------------
Animate
--------------------*/
const render = (a) => {
step +=1;
requestAnimationFrame(render);
//bubble.scale.set(spring.scale, spring.scale, spring.scale);
updateVertices(a);
renderer.clear();
renderer.render(scene, camera);
//Activate on mouse down
if(mouseDown && howMuch < howMuchLimit)
howMuch += 0.01;
else if (howMuch > 0)
howMuch -= 0.01;
if(isIntersectingWithBubble){
if(rippleAmount < 10)
rippleAmount += 0.05;
}else if(rippleAmount > 0)
rippleAmount -= 0.05;
doRipple();
}
requestAnimationFrame(render);
renderer.render(scene, camera);
/*--------------------
Helpers
--------------------*/
function fbm(p) {
var result = noise.simplex3(p._x, p._y, p._z);
return result;
}
function addPoint(arr) {
var r = new Point(0, 0, 0);
var len = arr.length;
for (var i = 0; i < len; i += 1) {
r._x += arr[i]._x;
r._y += arr[i]._y;
r._z += arr[i]._z;
}
return r;
}
function Point(_x=0, _y=0, _z=0) {
this._x = _x;
this._y = _y;
this._z = _z;
}
function ripple(p) {
var q = new Point(fbm(addPoint([p, new Point(0, 0, 0)])),
fbm(addPoint([p, new Point(0, 1, 0)])),
fbm(addPoint([p, new Point(0, 0, 1)])));
return fbm(addPoint([p, new Point(0.5 * q._x, 0.5 * q._y, 0.5 * q._z)]));
}
function doRipple(){
//ripple
for (var i = 0; i < bubbleGeometry.vertices.length; i += 1) {
var vertex = bubbleGeometry.vertices[i];
// var value = pn.noise((vertex.x + step)/ 10, vertex.y / 10, vertex.z / 10);
var value = ripple(new Point((vertex.x + step) / 100.0), vertex.y / 100.0, vertex.z / 100.0);
vertex.x = sphereVerticesArray[i].x + sphereVerticesNormArray[i].x * value * rippleAmount;
vertex.y = sphereVerticesArray[i].y + sphereVerticesNormArray[i].y * value * rippleAmount;
vertex.z = sphereVerticesArray[i].z + sphereVerticesNormArray[i].z * value * rippleAmount;
}
bubbleGeometry.computeFaceNormals();
bubbleGeometry.computeVertexNormals();
bubbleGeometry.verticesNeedUpdate = true;
bubbleGeometry.normalsNeedUpdate = true;
}

'plus' or 'x' shaped wave in threejs

I have this scene made with THREE.js. (see code snippet attached in the post)
or https://codepen.io/farisk/pen/jOPgKGQ
Currently its a radiating a circular wave based on the formula in the ‘distance()’ function
As such :
return Math.sqrt(Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2));
I’m wondering if its possible to modify the formula so that I can somehow achieve a ‘X’ or ‘Plus sign (+)’ shaped wave instead?
var once = false;
class App {
init() {
this.stats = new Stats();
this.stats.showPanel(0);
document.body.querySelector('.stats').appendChild(this.stats.domElement);
this.backgroundColor = 0x000000;
this.ambientLightColor = 0xffffff;
this.spotLightColor = 0xff9999;
// this.boxColor = 0x1a63ed;
this.boxColor = 0xffffff;
this.angle = 0;
this.gridSize = 30;
this.ratio = 1.3
this.col = this.gridSize*this.ratio;
this.row = this.gridSize;
this.velocity = .05;
this.boxes = [];
this.amplitude = -7;
this.frequency = 0;
this.waveLength = 242;
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(this.backgroundColor);
this.camera = new THREE.PerspectiveCamera(2, window.innerWidth / window.innerHeight, 1, 10000);
this.camera.position.set(0, 800, 0);
this.addRenderer();
document.body.appendChild(this.renderer.domElement);
this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
this.addAmbientLight();
this.addDirectionalLight();
this.addFloor();
this.addBoxes(this.scene);
this.addGUIControls();
this.animate();
window.addEventListener('resize', this.onResize.bind(this));
}
addDirectionalLight() {
this.directionalLight = new THREE.DirectionalLight(0xffffff, 1);
this.directionalLight.castShadow = true;
this.directionalLight.position.set(0, 1, 0);
this.directionalLight.shadow.camera.far = 10000;
this.directionalLight.shadow.camera.near = -100;
this.directionalLight.shadow.camera.left = -40;
this.directionalLight.shadow.camera.right = 40;
this.directionalLight.shadow.camera.top = 20;
this.directionalLight.shadow.camera.bottom = -20;
this.directionalLight.shadow.camera.zoom = 1;
this.directionalLight.shadow.camera.needsUpdate = true;
const targetObject = new THREE.Object3D();
targetObject.position.set(-50, -82, 40);
this.directionalLight.target = targetObject;
this.scene.add(this.directionalLight);
this.scene.add(this.directionalLight.target);
}
addGUIControls() {
this.gui = new dat.GUI();
this.gui.add(this, 'amplitude', -10, 10);
this.gui.add(this, 'velocity', 0, .5);
this.gui.add(this, 'waveLength', 100, 500);
this.controller = this.gui.add(this, 'gridSize', 24, 150);
this.controller.onFinishChange((value) => {
this.gridSize = Math.floor(value);
this.clearScene();
this.col = this.gridSize*this.ratio;
this.row = this.gridSize;
this.addBoxes(this.scene);
});
}
addRenderer() {
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
this.renderer.setSize(window.innerWidth, window.innerHeight);
}
addAmbientLight() {
const light = new THREE.AmbientLight(this.ambientLightColor, .5);
this.scene.add(light);
}
addSpotLight() {
this.spotLight = new THREE.SpotLight(this.spotLightColor);
this.spotLight.position.set(100, 250, 150);
this.spotLight.castShadow = true;
this.scene.add(this.spotLight);
}
clearScene() {
this.scene.remove(this.mesh);
this.boxes = [];
}
addBoxes(scene) {
const size = 0.05;
const height = 20;
const material = new THREE.MeshLambertMaterial({
color: this.boxColor,
});
const geometry = new THREE.BoxBufferGeometry(size, height, size);
geometry.translate( 0, 2.5, 0 );
this.mesh = this.getBox(geometry, material, this.row * this.col);
this.scene.add(this.mesh);
let ii = 0;
for (let i = 0; i < this.col; i++) {
this.boxes[i] = [];
for (let j = 0; j < this.row; j++) {
const pivot = new THREE.Object3D();
this.boxes[i][j] = pivot;
pivot.scale.set(1, 0.001, 1);
// pivot.position.set(i - this.gridSize*this.ratio * .5, height * .5, j - this.gridSize * .5);
pivot.position.set(i - this.gridSize*this.ratio * .5, height * 0, j - this.gridSize * .5);
pivot.updateMatrix();
this.mesh.setMatrixAt(ii++, pivot.matrix);
}
}
this.mesh.instanceMatrix.needsUpdate = true;
}
drawWave() {
let ii= 0;
for (let i = 0; i < this.col; i++) {
for (let j = 0; j < this.row; j++) {
const distance = this.distance(j, i, this.row * .5, this.col * .5);
const offset = this.map(distance, 0, this.waveLength, -100, 100);
const angle = this.angle + offset ;
if (!once) {
console.log(this.boxes)
once = true
}
this.boxes[i][j].scale.y = this.map(Math.sin(angle), -1, -this.amplitude, 0.001, 1);
this.boxes[i][j].rotation.z = this.map(Math.sin(angle), -1, -this.amplitude, 0.001, 1);
this.boxes[i][j].updateMatrix();
this.mesh.setMatrixAt(ii++, this.boxes[i][j].matrix);
}
}
this.mesh.instanceMatrix.needsUpdate = true;
this.angle -= this.velocity;
}
distance(x1, y1, x2, y2) {
// return Math.sin(x1 - x2)
return Math.sqrt(Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2));
}
map(value, start1, stop1, start2, stop2) {
return (value - start1) / (stop1 - start1) * (stop2 - start2) + start2
}
addFloor() {
const planeGeometry = new THREE.PlaneBufferGeometry(10, 500);
const planeMaterial = new THREE.ShadowMaterial({ opacity:1 });
this.floor = new THREE.Mesh(planeGeometry, planeMaterial);
planeGeometry.rotateX(- Math.PI / 2);
this.floor.position.y = 2;
this.floor.receiveShadow = true;
this.scene.add(this.floor);
}
getBox(geometry, material, count) {
const mesh = new THREE.InstancedMesh(geometry, material, count);
mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
mesh.castShadow = true;
mesh.receiveShadow = true;
return mesh;
}
addGrid() {
const size = this.col;
const divisions = size;
const gridHelper = new THREE.GridHelper(size, divisions);
gridHelper.position.set(0, 0, 0);
gridHelper.material.opacity = 0;
gridHelper.material.transparent = true;
this.scene.add(gridHelper);
}
animate() {
this.stats.begin();
this.drawWave();
this.controls.update();
this.renderer.render(this.scene, this.camera);
this.stats.end();
requestAnimationFrame(this.animate.bind(this));
}
onResize() {
const ww = window.innerWidth;
const wh = window.innerHeight;
this.camera.aspect = ww / wh;
this.camera.updateProjectionMatrix();
this.renderer.setSize(ww, wh);
}
}
new App().init();
html {
font-family: sans-serif;
}
* {
box-sizing: border-box;
}
body {
background: black;
color: #fff;
font-family: sans-serif;
overflow: hidden;
cursor: -webkit-grab;
cursor: -moz-grab;
padding: 0;
margin: 0;
}
canvas {
width: 100%;
height: 100%;
}
.stats {
opacity: 1;
z-index: 10;
position: absolute;
}
.dg.ac {
position: absolute;
z-index: 10 !important;
}
<main>
<div class="stats"></div>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.2/dat.gui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>
"Plus" or "X"
The "Plus" is the easiest to implement. You just need the two distances from the center lines (horizontal and vertical).
const yd = j - this.row / 2; // distance from horizontal
const xd = i - this.col / 2; // distance from vertical
The get the minimum of the absolute values of each distance
const distance = Math.min(Math.abs(yd), Math.abs(xd));
You then use that distance as you did in the original.
Implementation
If you change the function draw to the following this will create the plus that I think you are looking for.
drawWave() {
var ii= 0, x, y;
for (x = 0; x < this.col; x++) {
const bRow = this.boxes[x];
for (y = 0; y < this.row; y++) {
const yd = y - this.row / 2;
const xd = x - this.col / 2;
const distance = Math.min(Math.abs(yd), Math.abs(xd));
const angle = this.angle + this.map(distance, 0, this.waveLength, -100, 100);
const size = this.map(Math.sin(angle), -1, -this.amplitude, 0.001, 1);
bRow[y].scale.y = size;
bRow[y].rotation.z = size;
bRow[y].updateMatrix();
this.mesh.setMatrixAt(ii++, bRow[y].matrix);
}
}
this.mesh.instanceMatrix.needsUpdate = true;
this.angle -= this.velocity;
}
The cross
If you want a cross you need only rotate the x and y by 45 deg. The next function does that.
drawWave() {
const xAx = Math.cos(Math.PI / 4); // Axis 45 deg CW
const xAy = Math.sin(Math.PI / 4);
var ii= 0, x, y;
for (x = 0; x < this.col; x++) {
const bRow = this.boxes[x];
for (y = 0; y < this.row; y++) {
const xx = x - this.col / 2;
const yy = y - this.row / 2;
const xd = xx * xAx - yy * xAy; // rotate
const yd = xx * xAy + yy * xAx;
const distance = Math.min(Math.abs(yd), Math.abs(xd));
const angle = this.angle + this.map(distance, 0, this.waveLength, -100, 100);
const size = this.map(Math.sin(angle), -1, -this.amplitude, 0.001, 1);
bRow[y].scale.y = size;
bRow[y].rotation.z = size;
bRow[y].updateMatrix();
this.mesh.setMatrixAt(ii++, bRow[y].matrix);
}
}
this.mesh.instanceMatrix.needsUpdate = true;
this.angle -= this.velocity;
}
It would be far more efficient if you implemented all the above logic in the vertex shader

Create a BoxGeometry 16x16 Grid relative to viewport size with Three.js

The question goes as simple as it can, the title pretty much describes what I'm trying to do.
I'm new to Three.js and WebGL, I more or less understand the basics of creating a Cube but when it comes to what I'm trying to do I'm at loss.
I'm basically using this pen as reference while I learn: https://codepen.io/jackrugile/pen/vOEKzw as it does what I want without it being relative to screen size
var tick = 0,
smallestDimension = Math.min(window.innerWidth, window.innerHeight),
viewportWidth = smallestDimension,
viewportHeight = smallestDimension,
worldWidth = 100,
worldHeight = 100,
rows = 30,
cols = 30,
tileWidth = worldWidth / cols,
tileHeight = worldHeight / rows,
FOV = 90,
scene = new THREE.Scene(),
camera = new THREE.PerspectiveCamera(
FOV,
viewportWidth / viewportHeight,
0.1,
1000
),
renderer = new THREE.WebGLRenderer({
antialias: true
}),
plane = new THREE.Mesh(
new THREE.PlaneBufferGeometry(worldWidth, worldHeight, 1),
new THREE.MeshPhongMaterial({
color: 0x222222
})
),
cubes = new THREE.Object3D(),
spotLight = new THREE.SpotLight(0xffffff),
ambientLight = new THREE.AmbientLight(0x666666);
renderer.setSize(viewportWidth, viewportHeight);
renderer.shadowMapEnabled = true;
renderer.shadowMapType = THREE.PCFSoftShadowMap;
scene.add(plane);
scene.add(cubes);
scene.add(spotLight);
scene.add(ambientLight);
for (var x = 0; x < cols; x++) {
for (var y = 0; y < rows; y++) {
var width = tileWidth,
height = tileHeight,
dx = (cols / 2 - x),
dy = (rows / 2 - y),
depth = 1 + (20 - Math.sqrt(dx * dx + dy * dy)) / 4,
xBase = -worldWidth / 2 + x * tileWidth + tileWidth / 2,
yBase = -worldHeight / 2 + y * tileHeight + tileHeight / 2,
zBase = depth / 2,
cube = new THREE.Mesh(
new THREE.BoxGeometry(width, height, depth),
new THREE.MeshPhongMaterial({
color: 'rgb(' + ~~((y / rows) * 255) + ', ' + ~~((x / cols) * 255) + ', 255)',
shininess: 50
})
);
cube.position.set(
xBase,
yBase,
zBase
);
cube.castShadow = true;
cube.receiveShadow = true;
cube.zBase = zBase;
cube.zScaleTarget = 1;
cubes.add(cube);
}
}
plane.position.set(0, 0, 0);
plane.castShadow = false;
plane.receiveShadow = true;
camera.position.set(0, 0, 100);
spotLight.position.set(0, 0, 100);
spotLight.castShadow = true;
spotLight.shadowCameraNear = 0.1;
spotLight.shadowMapWidth = 2048;
spotLight.shadowMapHeight = 2048;
spotLight.shadowDarkness = 0.1;
function step() {
spotLight.position.x = Math.sin(tick / 100) * (worldWidth / 2);
spotLight.position.y = Math.cos(tick / 100) * (worldHeight / 2);
cubes.traverse(function(cube) {
if (cube instanceof THREE.Mesh) {
if (Math.abs(cube.scale.z - cube.zScaleTarget) > 0.001) {
cube.scale.z += (cube.zScaleTarget - cube.scale.z) * 0.05;
} else {
cube.zScaleTarget = 1 + Math.random() * 10;
}
cube.position.z = cube.geometry.parameters.depth / 2 * cube.scale.z;
}
});
tick++;
}
function render() {
renderer.render(scene, camera);
}
function loop() {
requestAnimationFrame(loop);
step();
render();
}
loop();
document.body.appendChild(renderer.domElement);
body {
background: #000;
overflow: hidden;
}
canvas {
bottom: 0;
display: block;
left: 0;
margin: auto;
position: absolute;
right: 0;
top: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r71/three.min.js"></script>
worldWidth = 100,
worldHeight = 100,
rows = 30,
cols = 30,
tileWidth = worldWidth / cols,
tileHeight = worldHeight / rows,
I see it uses this to set the size of the tiles that he lates uses in the loop:
...
var width = tileWidth,
height = tileHeight,
dx = ( cols / 2 - x ),
dy = ( rows / 2 - y ),
depth = 1 + ( 20 - Math.sqrt( dx * dx + dy * dy ) ) / 4,
xBase = -worldWidth / 2 + x * tileWidth + tileWidth / 2,
yBase = -worldHeight / 2 + y * tileHeight + tileHeight / 2,
...
How would I draw this tiles across the whole viewport so its similar to:
Thank you!
Rather then to take Math.min use this in line number 2 smallestDimension = Math.max( window.innerWidth, window.innerHeight )
It will automatically cover your screen size.
If you want you can change rows = 10 and col = 10

Camera position and device controls using the waves example

I have a website where i need to use threejs. I used the waves example in a landing page and it is working well on desktop/laptop.
However, I need to be able to move the waves (same behaviour as mouse in desktop) but with the device gyroscope/accelerometer and it is working using the devicecontrols that i found in another example. The problem is that it is my first time using three and working with 3d and i am lost. The dots are really small they only seem to move in a very small line and i dont know how or where the camera should look.
For example,
Desired behaviour (https://www.dropbox.com/s/d11srwqww8jtuda/PhoneWanted.png?dl=0)
Current on desktop (https://www.dropbox.com/s/x3rpcnovrfr2v1x/Desktop.png?dl=0)
Current on mobile (https://www.dropbox.com/s/c9c1mojgi5zt55y/IMG_2385.jpeg?dl=0)
On mobile should look the same as on desktop and move the waves but only horizontally...
Is this possible?
Mobile Script
<script>
if (screen && screen.width < 900) {
//alert("Mobile");
if ( WEBGL.isWebGLAvailable() === false ) {
document.body.appendChild( WEBGL.getWebGLErrorMessage() );
}
var SEPARATION = 100, AMOUNTX = 50, AMOUNTY = 50;
var container;
var camera, scene, renderer, controls;
var particles, count = 0;
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
controls = new THREE.DeviceOrientationControls( camera );
camera.position.z = 1000;
scene = new THREE.Scene();
//
var numParticles = AMOUNTX * AMOUNTY;
var positions = new Float32Array( numParticles * 3 );
var scales = new Float32Array( numParticles );
var i = 0, j = 0;
for ( var ix = 0; ix < AMOUNTX; ix ++ ) {
for ( var iy = 0; iy < AMOUNTY; iy ++ ) {
positions[ i ] = ix * SEPARATION - ( ( AMOUNTX * SEPARATION ) / 2 ); // x
positions[ i + 1 ] = 0; // y
positions[ i + 2 ] = iy * SEPARATION - ( ( AMOUNTY * SEPARATION ) / 2 ); // z
scales[ j ] = 1;
i += 3;
j ++;
}
}
var geometry = new THREE.BufferGeometry();
geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
geometry.addAttribute( 'scale', new THREE.BufferAttribute( scales, 1 ) );
var material = new THREE.ShaderMaterial( {
uniforms: {
color: { value: new THREE.Color( 0xffffff ) },
},
vertexShader: document.getElementById( 'vertexshader' ).textContent,
fragmentShader: document.getElementById( 'fragmentshader' ).textContent
} );
particles = new THREE.Points( geometry, material );
scene.add( particles );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
window.addEventListener( 'resize', onWindowResize, false );
}
function animate() {
window.requestAnimationFrame( animate );
controls.update();
renderer.render( scene, camera );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
}
</script>
Solution
console.log("Desktop");
if (WEBGL.isWebGLAvailable() === false) {
document.body.appendChild(WEBGL.getWebGLErrorMessage());
}
var SEPARATION = 100,
AMOUNTX = 50,
AMOUNTY = 50;
var container;
var camera, scene, renderer, controls;
var particles, count = 0;
var mouseX = 0,
mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
// var isMobile = isOSMobile();
var isMobile = true;
init();
animate();
function init() {
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(75, window.innerWidth /
window.innerHeight, 1, 10000);
if (isMobile) {
controls = new THREE.DeviceOrientationControls(camera);
}
camera.position.z = 1000;
scene = new THREE.Scene();
//
var numParticles = AMOUNTX * AMOUNTY;
var positions = new Float32Array(numParticles * 3);
var scales = new Float32Array(numParticles);
var i = 0,
j = 0;
for (var ix = 0; ix < AMOUNTX; ix++) {
for (var iy = 0; iy < AMOUNTY; iy++) {
positions[i] = ix * SEPARATION - ((AMOUNTX *
SEPARATION) / 2); // x
positions[i + 1] = 0; // y
positions[i + 2] = iy * SEPARATION - ((AMOUNTY *
SEPARATION) / 2); // z
scales[j] = 1;
i += 3;
j++;
}
}
var geometry = new THREE.BufferGeometry();
geometry.addAttribute('position', new THREE.BufferAttribute(
positions, 3));
geometry.addAttribute('scale', new THREE.BufferAttribute(scales,
1));
var material = new THREE.ShaderMaterial({
uniforms: {
color: {
value: new THREE.Color(isMobile ? 0xffffff : 0x8D8D8F)
},
},
vertexShader: document.getElementById(
'vertexshader').textContent,
fragmentShader: document.getElementById(
'fragmentshader').textContent
});
particles = new THREE.Points(geometry, material);
scene.add(particles);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
document.addEventListener('mousemove', onDocumentMouseMove,
false);
window.addEventListener('resize', onWindowResize, false);
if (isMobile) {
window.addEventListener("deviceorientation", handleOrientation, true);
}
}
function handleOrientation(e) {
var absolute = e.absolute;
var alpha = e.alpha;// x -90 ... 90
var beta = e.beta;// y 180 ... 0
var gamma = e.gamma;// x -90 ... 90
mouseX = -5 * windowHalfX * (gamma / 90);
//mouseY = -windowHalfY * ((beta - 90) / 90);
// console.log(mouseX.toFixed(2), ' x ', mouseY.toFixed(2));
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
//
function onDocumentMouseMove(event) {
mouseX = event.clientX - windowHalfX;
mouseY = event.clientY - windowHalfY;
}
//
function animate() {
requestAnimationFrame(animate);
render();
if (isMobile) {
controls.update();
}
}
function render() {
camera.position.x += (mouseX - camera.position.x) * .05;
if(isMobile) {
camera.position.y = 550;
} else {
camera.position.y += (-mouseY - camera.position.y) * .05;
}
camera.lookAt(scene.position);
var positions = particles.geometry.attributes.position.array;
var scales = particles.geometry.attributes.scale.array;
var i = 0,
j = 0;
for (var ix = 0; ix < AMOUNTX; ix++) {
for (var iy = 0; iy < AMOUNTY; iy++) {
positions[i + 1] = (Math.sin((ix + count) * 0.3) * 50) +
(Math.sin((iy + count) * 0.5) * 50);
scales[j] = (Math.sin((ix + count) * 0.3) + 1) * 8 +
(Math.sin((iy + count) * 0.5) + 1) * 8;
i += 3;
j++;
}
}
particles.geometry.attributes.position.needsUpdate = true;
particles.geometry.attributes.scale.needsUpdate = true;
renderer.render(scene, camera);
count += 0.1;
}
function isOSMobile() {
var userAgent = navigator.userAgent || navigator.vendor || window.opera;
if (/android/i.test(userAgent)) {
return true;
}
if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) {
return true;
}
return false;
}
Thank you!
Fixed it. Code works, Y axis movement is disabled.
console.log("Desktop");
if (WEBGL.isWebGLAvailable() === false) {
document.body.appendChild(WEBGL.getWebGLErrorMessage());
}
var SEPARATION = 100,
AMOUNTX = 50,
AMOUNTY = 50;
var container;
var camera, scene, renderer, controls;
var particles, count = 0;
var mouseX = 0,
mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
// var isMobile = isOSMobile();
var isMobile = true;
init();
animate();
function init() {
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(75, window.innerWidth /
window.innerHeight, 1, 10000);
if (isMobile) {
controls = new THREE.DeviceOrientationControls(camera);
}
camera.position.z = 1000;
scene = new THREE.Scene();
//
var numParticles = AMOUNTX * AMOUNTY;
var positions = new Float32Array(numParticles * 3);
var scales = new Float32Array(numParticles);
var i = 0,
j = 0;
for (var ix = 0; ix < AMOUNTX; ix++) {
for (var iy = 0; iy < AMOUNTY; iy++) {
positions[i] = ix * SEPARATION - ((AMOUNTX *
SEPARATION) / 2); // x
positions[i + 1] = 0; // y
positions[i + 2] = iy * SEPARATION - ((AMOUNTY *
SEPARATION) / 2); // z
scales[j] = 1;
i += 3;
j++;
}
}
var geometry = new THREE.BufferGeometry();
geometry.addAttribute('position', new THREE.BufferAttribute(
positions, 3));
geometry.addAttribute('scale', new THREE.BufferAttribute(scales,
1));
var material = new THREE.ShaderMaterial({
uniforms: {
color: {
value: new THREE.Color(isMobile ? 0xffffff : 0x8D8D8F)
},
},
vertexShader: document.getElementById(
'vertexshader').textContent,
fragmentShader: document.getElementById(
'fragmentshader').textContent
});
particles = new THREE.Points(geometry, material);
scene.add(particles);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
document.addEventListener('mousemove', onDocumentMouseMove,
false);
window.addEventListener('resize', onWindowResize, false);
if (isMobile) {
window.addEventListener("deviceorientation", handleOrientation, true);
}
}
function handleOrientation(e) {
var absolute = e.absolute;
var alpha = e.alpha;// x -90 ... 90
var beta = e.beta;// y 180 ... 0
var gamma = e.gamma;// x -90 ... 90
mouseX = -5 * windowHalfX * (gamma / 90);
//mouseY = -windowHalfY * ((beta - 90) / 90);
// console.log(mouseX.toFixed(2), ' x ', mouseY.toFixed(2));
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
//
function onDocumentMouseMove(event) {
mouseX = event.clientX - windowHalfX;
mouseY = event.clientY - windowHalfY;
}
//
function animate() {
requestAnimationFrame(animate);
render();
if (isMobile) {
controls.update();
}
}
function render() {
camera.position.x += (mouseX - camera.position.x) * .05;
if(isMobile) {
camera.position.y = 550;
} else {
camera.position.y += (-mouseY - camera.position.y) * .05;
}
camera.lookAt(scene.position);
var positions = particles.geometry.attributes.position.array;
var scales = particles.geometry.attributes.scale.array;
var i = 0,
j = 0;
for (var ix = 0; ix < AMOUNTX; ix++) {
for (var iy = 0; iy < AMOUNTY; iy++) {
positions[i + 1] = (Math.sin((ix + count) * 0.3) * 50) +
(Math.sin((iy + count) * 0.5) * 50);
scales[j] = (Math.sin((ix + count) * 0.3) + 1) * 8 +
(Math.sin((iy + count) * 0.5) + 1) * 8;
i += 3;
j++;
}
}
particles.geometry.attributes.position.needsUpdate = true;
particles.geometry.attributes.scale.needsUpdate = true;
renderer.render(scene, camera);
count += 0.1;
}
function isOSMobile() {
var userAgent = navigator.userAgent || navigator.vendor || window.opera;
if (/android/i.test(userAgent)) {
return true;
}
if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) {
return true;
}
return false;
}

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>

Categories

Resources