I'm new to Three.js, I'm just having fun with it. I'm trying to achieve a simple dynamic background fullscreen on a page, you get the example here:
function createHexagon( vertices, color ) {
var geometry = new THREE.BufferGeometry();
geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
var material = new THREE.LineBasicMaterial( { color: color, opacity: Math.min((Math.random() / 5), 0.1), transparent: true } );
var hexagon = new THREE.Line( geometry, material );
return hexagon;
}
function initMatrix() {
var color = defaultColor.getHex();
var vertices;
var x = ( width / -2 ) - 90;
var y = height / -2;
var deltaX = 120;
var deltaY = 60;
var time = 5.0;
while( y < height / 2 ) {
while( x < width / 2 ) {
vertices = new Float32Array([
0, 30, 0,
20, 0, 0
]);
var hexagon = createHexagon( vertices, color );
scene.add( hexagon );
hexagon.position.set( x, y, 0 );
vertices = new Float32Array([
20, 0, 0,
60, 0, 0
]);
var hexagon = createHexagon( vertices, color );
scene.add( hexagon );
hexagon.position.set( x, y, 0 );
vertices = new Float32Array([
60, 0, 0,
80, 30, 0
]);
var hexagon = createHexagon( vertices, color );
scene.add( hexagon );
hexagon.position.set( x, y, 0 );
x += deltaX;
}
x = ( width / -2 ) - 90;
y += deltaY;
}
x = ( width / -2 ) - 30;
y = ( height / -2 ) - 30;
deltaX = 120;
deltaY = 60;
while( y < height / 2 ) {
while( x < width / 2 ) {
vertices = new Float32Array([
0, 30, 0,
20, 0, 0
]);
var hexagon = createHexagon( vertices, color );
scene.add( hexagon );
hexagon.position.set( x, y, 0 );
vertices = new Float32Array([
20, 0, 0,
60, 0, 0
]);
var hexagon = createHexagon( vertices, color );
scene.add( hexagon );
hexagon.position.set( x, y, 0 );
vertices = new Float32Array([
60, 0, 0,
80, 30, 0
]);
var hexagon = createHexagon( vertices, color );
scene.add( hexagon );
hexagon.position.set( x, y, 0 );
x += deltaX;
}
x = ( width / -2 ) - 30;
y += deltaY;
}
}
Those are single bufferGeometry lines (as you can see in the above functions to create the background) that randomly rotate and change opacity on mouse hover with raycaster and TweenLite. It works pretty fine. What you can notice is that CPU usage goes to almost 100%.
I know that if I group lines into the same geometry it'll be better on performance, but then I'm not able to animate single lines with raycaster, especially the opacity.
I searched a lot of discussions and tried so many things. The best result is this way, rendering single lines separately. Can you suggest some tips about it?
(Posted on behalf of the question author).
I found a solution. I made a whole geometry and I indexed vertices and respective colors. The only problem is that I can't mange opacity this way, but it works fine and CPU is around 20%.
document.addEventListener( 'DOMContentLoaded', main );
var width = screen.width;
var height = screen.height;
var camera, scene, renderer, raycaster, mouse;
var rayColor = new THREE.Color( 0x0640C2 );
var rayColor2 = new THREE.Color( 0xCC311B );
var colors;
var linesPositions, originalPositions;
var linesMesh;
var geometry;
var intersects;
var stats;
function initMatrix() {
var AlinesPositions = [ ];
var x = ( width / - 2 ) - 80;
var y = ( height / - 2 ) - 60;
var deltaX = 120;
var deltaY = 60;
while( y <= ( height / 2 ) ) {
while( x <= ( width / 2 ) ) {
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
x += deltaX;
}
x = ( width / -2 ) - 80;
y += deltaY;
}
x = ( width / - 2 );
y = ( height / - 2 ) - 60;
while( y <= ( height / 2 ) ) {
while( x <= ( width / 2 ) ) {
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
x += deltaX;
}
x = ( width / -2 );
y += deltaY;
}
linesPositions = new Float32Array( AlinesPositions );
var Acolors = [ ];
for( var i = 0; i < AlinesPositions.length; i++ ) {
var fact = Math.random() * 20.0;
var ran = Math.min( Math.max( ( Math.random() / fact ), 0.01 ), 0.5 );
Acolors[i] = ran;
Acolors[i+1] = ran;
Acolors[i+2] = ran;
i += 2;
}
colors = new Float32Array( Acolors );
geometry = new THREE.BufferGeometry();
geometry.addAttribute( 'position', new THREE.BufferAttribute( linesPositions, 3 ).setDynamic( true ) );
geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ).setDynamic( true ) );
var material = new THREE.LineBasicMaterial( {
vertexColors: THREE.VertexColors,
blending: THREE.AdditiveBlending,
transparent: true
} );
linesMesh = new THREE.LineSegments( geometry, material );
scene.add( linesMesh );
originalPositions = new THREE.BufferAttribute( linesPositions, 3 );
originalPositions.copy( linesMesh.geometry.attributes.position );
}
function init() {
scene = new THREE.Scene();
//camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 100 );
camera = new THREE.PerspectiveCamera( 120, width / height, 1, 1000 );
camera.position.set( 0, 0, 200 );
camera.lookAt( 0, 0, 0 );
// Create matrix
initMatrix();
raycaster = new THREE.Raycaster();
raycaster.linePrecision = 20;
mouse = new THREE.Vector2();
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize( width, height );
document.body.appendChild( renderer.domElement );
}
function main() {
init();
animate();
}
function animate() {
requestAnimationFrame( animate );
render();
}
var intersected = [];
function removeX( i ) { intersected.splice( i, 1 ); }
function alterate( index ) {
var randColor = ( Math.random() <= 0.49 ) ? rayColor : rayColor2;
// Change color
var currentC = new THREE.Color( linesMesh.geometry.attributes.color.getX( index ), linesMesh.geometry.attributes.color.getY( index ), linesMesh.geometry.attributes.color.getZ( index ));
var tweenC = TweenLite.to( currentC, 1.0, {
r: randColor.r,
g: randColor.g,
b: randColor.b,
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.color.setXYZ( index, currentC.r, currentC.g, currentC.b );
linesMesh.geometry.attributes.color.needsUpdate = true;
},
onUpdateParams: [ index, currentC ]
});
// Change coordinates
var currentXY = { x: originalPositions.getX( index ), y: originalPositions.getY( index ), z: originalPositions.getZ( index ) };
var goal = [ ( currentXY.x + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ), ( currentXY.y + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ), ( currentXY.z + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ) ];
var tweenXY = TweenLite.to( currentXY, 1.0, {
x: goal[ 0 ],
y: goal[ 1 ],
z: goal[ 2 ],
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.position.setXYZ( index, currentXY.x, currentXY.y, currentXY.z );
linesMesh.geometry.attributes.position.needsUpdate = true;
},
onUpdateParams: [ index, currentXY ]
});
}
function render() {
// update the picking ray with the camera and mouse position
raycaster.setFromCamera( mouse, camera );
intersects = raycaster.intersectObject( linesMesh );
if( intersects.length ) {
if( !intersected.includes( intersects[ 0 ].index ) ) {
for( var x = 0; x < intersects.length; x++ ) {
if( !intersected.includes( intersects[ x ].index ) ) {
var index = intersects[ x ].index;
// Save index
intersected.push( index );
alterate( index );
}
}
var present = 0;
for( var y = 0; y < intersected.length; y++ ) {
for( var j = 0; j < intersects.length; j++ ) {
if( intersects[ j ].index == intersected[ y ] ) { present = 1; break; }
}
if( !present ) {
( function( y ) {
var randC = Math.min( Math.max( ( Math.random() / ( Math.random() * 20.0 ) ), 0.01 ), 0.5 );
// Current item old coordinates
let index = intersected[ y ];
var indexX = originalPositions.getX( index );
var indexY = originalPositions.getY( index );
var indexZ = originalPositions.getZ( index );
removeX( y );
// Reset color
var currentCreset = new THREE.Color( linesMesh.geometry.attributes.color.getX( index ), linesMesh.geometry.attributes.color.getY( index ), linesMesh.geometry.attributes.color.getZ( index ));
var tweenCreset = TweenLite.to( currentCreset, 1.5, {
r: randC,
g: randC,
b: randC,
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.color.setXYZ( index, currentCreset.r, currentCreset.g, currentCreset.b );
linesMesh.geometry.attributes.color.needsUpdate = true;
},
onUpdateParams: [ index, currentCreset ],
onComplete: function() { }
} );
// Reset coordinates
var currentXYreset = { x: linesMesh.geometry.attributes.position.getX( index ), y: linesMesh.geometry.attributes.position.getY( index ), z: linesMesh.geometry.attributes.position.getZ( index ) };
var tweenXYreset = TweenLite.to( currentXYreset, 1.5, {
x: indexX,
y: indexY,
z: indexZ,
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.position.setXYZ( index, currentXYreset.x, currentXYreset.y, currentXYreset.z );
linesMesh.geometry.attributes.position.needsUpdate = true;
},
onUpdateParams: [ index, currentXYreset ]
});
}) ( y );
}
}
}
}
renderer.render( scene, camera );
}
function onMouseMove( event ) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
var normX = event.clientX - ( window.innerWidth / 2 );
var normY = event.clientY - ( window.innerHeight / 2 );
camera.position.set( 0 + ( normY / 50 ), 0 + ( normX / 50 ), 200 );
camera.lookAt( 0 + ( normY / 50 ), 0 + ( normX / 50 ), 0 );
}
window.addEventListener( 'click', function() {
if( intersected.length ) {
for( var x = 0; x < intersected.length; x++ ) {
var index = intersected[ x ];
alterate( index );
}
}
});
window.addEventListener( 'mousemove', onMouseMove, false );
// Set new dimensions for scene when resize window
window.addEventListener( 'resize', function() {
var wWidth = window.innerWidth;
var wHeight = window.innerHeight;
/*camera.left = wWidth / -2;
camera.right = wWidth / 2;
camera.top = wHeight / 2;
camera.bottom = wHeight / -2;*/
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
raycaster.setFromCamera( mouse, camera );
renderer.setSize( window.innerWidth, window.innerHeight );
} );
Here is a Codepen link.
I found a solution. I made a whole geometry and I indexed vertices and respective colors. The only problem is that I can't mange opacity this way, but it works fine and CPU is around 20%.
document.addEventListener( 'DOMContentLoaded', main );
var width = screen.width;
var height = screen.height;
var camera, scene, renderer, raycaster, mouse;
var rayColor = new THREE.Color( 0x0640C2 );
var rayColor2 = new THREE.Color( 0xCC311B );
var colors;
var linesPositions, originalPositions;
var linesMesh;
var geometry;
var intersects;
var stats;
function initMatrix() {
var AlinesPositions = [ ];
var x = ( width / - 2 ) - 80;
var y = ( height / - 2 ) - 60;
var deltaX = 120;
var deltaY = 60;
while( y <= ( height / 2 ) ) {
while( x <= ( width / 2 ) ) {
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
x += deltaX;
}
x = ( width / -2 ) - 80;
y += deltaY;
}
x = ( width / - 2 );
y = ( height / - 2 ) - 60;
while( y <= ( height / 2 ) ) {
while( x <= ( width / 2 ) ) {
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
x += deltaX;
}
x = ( width / -2 );
y += deltaY;
}
linesPositions = new Float32Array( AlinesPositions );
var Acolors = [ ];
for( var i = 0; i < AlinesPositions.length; i++ ) {
var fact = Math.random() * 20.0;
var ran = Math.min( Math.max( ( Math.random() / fact ), 0.01 ), 0.5 );
Acolors[i] = ran;
Acolors[i+1] = ran;
Acolors[i+2] = ran;
i += 2;
}
colors = new Float32Array( Acolors );
geometry = new THREE.BufferGeometry();
geometry.addAttribute( 'position', new THREE.BufferAttribute( linesPositions, 3 ).setDynamic( true ) );
geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ).setDynamic( true ) );
var material = new THREE.LineBasicMaterial( {
vertexColors: THREE.VertexColors,
blending: THREE.AdditiveBlending,
transparent: true
} );
linesMesh = new THREE.LineSegments( geometry, material );
scene.add( linesMesh );
originalPositions = new THREE.BufferAttribute( linesPositions, 3 );
originalPositions.copy( linesMesh.geometry.attributes.position );
}
function init() {
scene = new THREE.Scene();
//camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 100 );
camera = new THREE.PerspectiveCamera( 120, width / height, 1, 1000 );
camera.position.set( 0, 0, 200 );
camera.lookAt( 0, 0, 0 );
// Create matrix
initMatrix();
raycaster = new THREE.Raycaster();
raycaster.linePrecision = 20;
mouse = new THREE.Vector2();
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize( width, height );
document.body.appendChild( renderer.domElement );
}
function main() {
init();
animate();
}
function animate() {
requestAnimationFrame( animate );
render();
}
var intersected = [];
function removeX( i ) { intersected.splice( i, 1 ); }
function alterate( index ) {
var randColor = ( Math.random() <= 0.49 ) ? rayColor : rayColor2;
// Change color
var currentC = new THREE.Color( linesMesh.geometry.attributes.color.getX( index ), linesMesh.geometry.attributes.color.getY( index ), linesMesh.geometry.attributes.color.getZ( index ));
var tweenC = TweenLite.to( currentC, 1.0, {
r: randColor.r,
g: randColor.g,
b: randColor.b,
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.color.setXYZ( index, currentC.r, currentC.g, currentC.b );
linesMesh.geometry.attributes.color.needsUpdate = true;
},
onUpdateParams: [ index, currentC ]
});
// Change coordinates
var currentXY = { x: originalPositions.getX( index ), y: originalPositions.getY( index ), z: originalPositions.getZ( index ) };
var goal = [ ( currentXY.x + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ), ( currentXY.y + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ), ( currentXY.z + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ) ];
var tweenXY = TweenLite.to( currentXY, 1.0, {
x: goal[ 0 ],
y: goal[ 1 ],
z: goal[ 2 ],
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.position.setXYZ( index, currentXY.x, currentXY.y, currentXY.z );
linesMesh.geometry.attributes.position.needsUpdate = true;
},
onUpdateParams: [ index, currentXY ]
});
}
function render() {
// update the picking ray with the camera and mouse position
raycaster.setFromCamera( mouse, camera );
intersects = raycaster.intersectObject( linesMesh );
if( intersects.length ) {
if( !intersected.includes( intersects[ 0 ].index ) ) {
for( var x = 0; x < intersects.length; x++ ) {
if( !intersected.includes( intersects[ x ].index ) ) {
var index = intersects[ x ].index;
// Save index
intersected.push( index );
alterate( index );
}
}
var present = 0;
for( var y = 0; y < intersected.length; y++ ) {
for( var j = 0; j < intersects.length; j++ ) {
if( intersects[ j ].index == intersected[ y ] ) { present = 1; break; }
}
if( !present ) {
( function( y ) {
var randC = Math.min( Math.max( ( Math.random() / ( Math.random() * 20.0 ) ), 0.01 ), 0.5 );
// Current item old coordinates
let index = intersected[ y ];
var indexX = originalPositions.getX( index );
var indexY = originalPositions.getY( index );
var indexZ = originalPositions.getZ( index );
removeX( y );
// Reset color
var currentCreset = new THREE.Color( linesMesh.geometry.attributes.color.getX( index ), linesMesh.geometry.attributes.color.getY( index ), linesMesh.geometry.attributes.color.getZ( index ));
var tweenCreset = TweenLite.to( currentCreset, 1.5, {
r: randC,
g: randC,
b: randC,
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.color.setXYZ( index, currentCreset.r, currentCreset.g, currentCreset.b );
linesMesh.geometry.attributes.color.needsUpdate = true;
},
onUpdateParams: [ index, currentCreset ],
onComplete: function() { }
} );
// Reset coordinates
var currentXYreset = { x: linesMesh.geometry.attributes.position.getX( index ), y: linesMesh.geometry.attributes.position.getY( index ), z: linesMesh.geometry.attributes.position.getZ( index ) };
var tweenXYreset = TweenLite.to( currentXYreset, 1.5, {
x: indexX,
y: indexY,
z: indexZ,
immediateRender: true,
lazy: false,
onUpdate: function() {
linesMesh.geometry.attributes.position.setXYZ( index, currentXYreset.x, currentXYreset.y, currentXYreset.z );
linesMesh.geometry.attributes.position.needsUpdate = true;
},
onUpdateParams: [ index, currentXYreset ]
});
}) ( y );
}
}
}
}
renderer.render( scene, camera );
}
function onMouseMove( event ) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
var normX = event.clientX - ( window.innerWidth / 2 );
var normY = event.clientY - ( window.innerHeight / 2 );
camera.position.set( 0 + ( normY / 50 ), 0 + ( normX / 50 ), 200 );
camera.lookAt( 0 + ( normY / 50 ), 0 + ( normX / 50 ), 0 );
}
window.addEventListener( 'click', function() {
if( intersected.length ) {
for( var x = 0; x < intersected.length; x++ ) {
var index = intersected[ x ];
alterate( index );
}
}
});
window.addEventListener( 'mousemove', onMouseMove, false );
// Set new dimensions for scene when resize window
window.addEventListener( 'resize', function() {
var wWidth = window.innerWidth;
var wHeight = window.innerHeight;
/*camera.left = wWidth / -2;
camera.right = wWidth / 2;
camera.top = wHeight / 2;
camera.bottom = wHeight / -2;*/
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
raycaster.setFromCamera( mouse, camera );
renderer.setSize( window.innerWidth, window.innerHeight );
} );
Here is a Codepen link: Example
I want to make a animated tube with hemisphere ends, the tubular segments is 200. I have the vertices from the first 60 segments to copy the position of a SphereGeometry's upper half part, and the last 60 segments lower half part.
The segments between the upper and lower hemisphere is all copied to the vertices around the sphere's equatorial.
The geometry looks fine, but the spherical environment mapping texture is discontinued at the sphere's equatorial.
I have my code as below, anyone know how to solve the problem?
var camera, scene, renderer;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 28, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 50;
scene = new THREE.Scene();
var nMat = new THREE.MeshNormalMaterial({side:THREE.DoubleSide,});
var tube = new DSTube( 200, 30, 10, nMat);
scene.add( tube.mesh );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
body {
margin: 0px;
background-color: #000000;
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r78/three.js"></script>
<script>
function DSTube( lengthSeg, radiusSeg, radius, material ) {
//for speed
this.framerate = 60;
//center
this.origin = new THREE.Vector3();
this.head = this.origin;
this.tail = this.origin;
//setup
this.lengthSeg = lengthSeg;
this.radiusSeg = radiusSeg;
this.radius = radius;
this.pathPoints = [];
for(var i=0; i<this.lengthSeg; i++) {
this.pathPoints.push( new THREE.Vector3( 0, 0, 0) );
}
// TubeGeometry(path, tubularSegments, radius, radiusSegments, closed)
this.geometry = new THREE.TubeGeometry( new THREE.CatmullRomCurve3(this.pathPoints), this.lengthSeg, this.radius, this.radiusSeg, false );
this.material = material;
this.mesh = new THREE.Mesh( this.geometry, this.material );
this.verticeCount = this.geometry.vertices.length;
//sphere part
//adjust height segment if needed
this.sphereHeightSegments = 60;
//SphereGeometry(radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength)
this.headSphere = new THREE.SphereGeometry( this.radius, this.radiusSeg, this.sphereHeightSegments );
this.tailSphere = new THREE.SphereGeometry( this.radius, this.radiusSeg, this.sphereHeightSegments );
this.sphereVerticeCount = this.headSphere.vertices.length;
//count for tube hemisphere
this.headHemisphereVerticeCount = ( this.sphereHeightSegments / 2 + 1 ) * this.radiusSeg;
//layer
this.tubeLayerCount = this.lengthSeg + 1;
this.headHemisphereLayerCount = this.tailHemisphereLayerCount = this.sphereHeightSegments / 2 + 1;
this.tailHemisphereStartLayer = this.tubeLayerCount - this.sphereHeightSegments / 2;
this.middleLayerCount = this.tubeLayerCount - this.headHemisphereLayerCount * 2;
this.initGeometry();
// return this.mesh;
}
DSTube.prototype.initGeometry = function() {
this.copyHeadSphere();
this.copyInitialTubePart();
this.copyTailSphere();
}
DSTube.prototype.copyHeadSphere = function() {
//copy head hemisphere vertice
for( var y = 0; y < this.headHemisphereLayerCount; y++ ) {
for( var x = 0; x < this.radiusSeg; x++ ) {
if( x == 0 ) {
var sphereVertex = this.headSphere.vertices[ y * (this.radiusSeg + 1) + x ];
this.geometry.vertices[ y * this.radiusSeg + x ].copy( sphereVertex );
} else {
var sphereVertex = this.headSphere.vertices[ y * (this.radiusSeg + 1) + ( this.radiusSeg - x ) ];
this.geometry.vertices[ y * this.radiusSeg + x ].copy( sphereVertex );
}
}
}
this.geometry.computeBoundingSphere();
this.geometry.computeFaceNormals();
this.geometry.computeVertexNormals();
}
DSTube.prototype.copyInitialTubePart = function() {
//copy head hemisphere vertice
for( var y = this.headHemisphereLayerCount - 1; y < this.tubeLayerCount ; y++ ) {
for( var x = 0; x < this.radiusSeg; x++ ) {
var vertex = this.geometry.vertices[ ( this.headHemisphereLayerCount - 1 ) * this.radiusSeg + x ];
this.geometry.vertices[ y * this.radiusSeg + x ].copy( vertex );
}
}
this.geometry.computeBoundingSphere();
this.geometry.computeFaceNormals();
this.geometry.computeVertexNormals();
}
DSTube.prototype.copyTailSphere = function() {
//copy tail hemisphere vertice
for( var y = this.tailHemisphereStartLayer; y < this.tubeLayerCount; y++ ) {
for( var x = 0; x < this.radiusSeg; x++ ) {
if( x == 0 ) {
var sphereVertex = this.tailSphere.vertices[ ( y - this.middleLayerCount - 1 ) * (this.radiusSeg + 1) + x ];
this.geometry.vertices[ y * this.radiusSeg + x ].copy( sphereVertex );
} else {
var sphereVertex = this.tailSphere.vertices[ ( y - this.middleLayerCount - 1 ) * (this.radiusSeg + 1) + ( this.radiusSeg - x ) ];
this.geometry.vertices[ y * this.radiusSeg + x ].copy( sphereVertex );
}
}
}
this.geometry.computeBoundingSphere();
this.geometry.computeFaceNormals();
this.geometry.computeVertexNormals();
}
</script>
So I have this code that will generate the user's desired dimension and display it on a customized new THREE.Geometry() . This may be an off topic to most of you. But hey, I'm just new to Three.js
My problem is that:
I can't find a way to insert the geometry.morphTargets. Or simply, I don't know how to use it properly
So here's my code:
//custom Object height and width
customHeightWidth(customWidth, customHeight);
function customHeightWidth(width, height){
material = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture(uploadedFile),
side: THREE.DoubleSide,
overdraw: true,
wireframe: false
});
//objects
combined = new THREE.PlaneGeometry(width, height, 30, 10);
geometry = new THREE.Geometry();
geometry.name: "target1", vertices.push( new THREE.Vector3( -(width), height, 0 ) );
geometry.name: "target2", vertices.push( new THREE.Vector3( -(width), -(height), 0 ) );
geometry.name: "target3", vertices.push( new THREE.Vector3( width, -(height), 0 ) );
geometry.computeBoundingSphere();
geometry.faces.push( new THREE.Face3( 0, 1, 2 ) );
geometry1 = new THREE.Geometry();
geometry1.vertices.push( new THREE.Vector3( width, height, 0 ) );
geometry1.vertices.push( new THREE.Vector3( -(width), height, 0 ) );
geometry1.vertices.push( new THREE.Vector3( width, -(height), 0 ) );
geometry1.computeBoundingSphere();
geometry1.faces.push( new THREE.Face3( 0, 1, 2 ) );
// 1st box
var mesh1 = new THREE.Mesh(geometry);
var mesh2 = new THREE.Mesh(geometry1);
//activating the meshs
THREE.GeometryUtils.merge(combined, mesh1);
THREE.GeometryUtils.merge(combined, mesh2);
mesh = new THREE.Mesh(combined, material);
this.scene.add(mesh);
};
}
var animate = function() {
requestAnimationFrame(animate);
//mesh.rotation.x += 0.01;
//mesh.rotation.y -= 0.006;
renderer.render(scene, camera);
}
init();
animate();
I recommend reviewing how JSONLoader loads morph targets to the Geometry.morphTargets array in the Geometry class.
function parseMorphing( scale ) {
if ( json.morphTargets !== undefined ) {
var i, l, v, vl, dstVertices, srcVertices;
for ( i = 0, l = json.morphTargets.length; i < l; i ++ ) {
geometry.morphTargets[ i ] = {};
geometry.morphTargets[ i ].name = json.morphTargets[ i ].name;
geometry.morphTargets[ i ].vertices = [];
dstVertices = geometry.morphTargets[ i ].vertices;
srcVertices = json.morphTargets [ i ].vertices;
for( v = 0, vl = srcVertices.length; v < vl; v += 3 ) {
var vertex = new THREE.Vector3();
vertex.x = srcVertices[ v ] * scale;
vertex.y = srcVertices[ v + 1 ] * scale;
vertex.z = srcVertices[ v + 2 ] * scale;
dstVertices.push( vertex );
}
}
}
//Reading morphTargets
for(i=o;i<geometry.morphTargets.length;i++) {
morphTargets = geometry.morphTargets[i].vertices;
return morphTargets;
}
//loading morphTargets
loadMorphTargets = function (morphTargets) {
if (morphTargets !== undefined) {
var i, l, v, vl, dstVertices, srcVertices;
//for (i = 0, l = morphTargets.length; i < l; i++) {
geometry.morphTargets[i] = {};
geometry.morphTargets[i].name = morphTargets.name;
geometry.morphTargets[i].vertices = [];
var dstVertices = geometry.morphTargets[i].vertices;
var srcVertices = morphTargets.vertices;
for (v = 0, vl = srcVertices.length; v < vl; v += 3) {
var vertex = new THREE.Vector3();
vertex.x = srcVertices[ v ];
vertex.y = srcVertices[ v + 1 ];
vertex.z = srcVertices[ v + 2 ];
dstVertices.push(vertex);
}
}
};
I guess it will be useful to you.