mapping of image on sphere using three.js - javascript

I'm trying to UV map a cube-map texture onto a sphere. I have tried to Map a cube-map onto a cube and it was pretty easy. I had this image which was mapped onto the cube as follows:
Click here to open image
This is the final output (with a different image, of same type)[Click here to open the Output][2]
This is how I carried out the UV mapping for the aforementioned task:
var geometry = new THREE.CubeGeometry( 10, 10, 10);
var material = new THREE.MeshPhongMaterial( { map:THREE.ImageUtils.loadTexture('images/texture-atlas.jpg') } );
There are few things that I found in OpenGL, but almost nothing for Three.js
If you can come up with anything that'll help me in executing this, it'll be a great help. If possible, please show me how to map one image onto corresponding part of the sphere and I'll do it for the rest of them.

A SphereGeometry does not have vertices in the correct locations to achieve the mapping you want. However, you can easily create a suitable geometry by morphing BoxGeometry into a sphere.
// geometry
var geometry = new THREE.BoxGeometry( 10, 10, 10, 8, 8, 8 );
// morph box into a sphere
for ( var i = 0; i < geometry.vertices.length; i ++ ) {
geometry.vertices[ i ].normalize().multiplyScalar( 10 ); // or whatever size you want
}
// texture is a collage; set offset/repeat per material index
var repeat = new THREE.Vector2( 1/3, 1/2 );
var offsets = [
new THREE.Vector2( 0, 0 ),
new THREE.Vector2( 0, 1/2 ),
new THREE.Vector2( 1/3, 0 ),
new THREE.Vector2( 1/3, 1/2 ),
new THREE.Vector2( 2/3, 0 ),
new THREE.Vector2( 2/3, 1/2 )
];
// redefine vertex normals consistent with a sphere; reset UVs
for ( var i = 0; i < geometry.faces.length; i ++ ) {
var face = geometry.faces[ i ];
face.vertexNormals[ 0 ].copy( geometry.vertices[ face.a ] ).normalize();
face.vertexNormals[ 1 ].copy( geometry.vertices[ face.b ] ).normalize();
face.vertexNormals[ 2 ].copy( geometry.vertices[ face.c ] ).normalize();
var uvs = geometry.faceVertexUvs[ 0 ];
for ( var j = 0; j < 3; j ++ ) {
uvs[ i ][ j ].multiply( repeat ).add( offsets[ face.materialIndex ] );
}
// face.normal - will not be used; don't worry about it
}
var loader = new THREE.TextureLoader();
var texture = loader.load( 'texture.jpg' );
// mesh
var mesh = new THREE.Mesh( geometry, new THREE.MeshPhongMaterial( { map: texture } ) );
scene.add( mesh );
three.js r.77

Related

threejs - morphTargets shrink other meshes in scene

Description of the problem
I have a simple box mesh with a morphTarget and a slider which adjusts the morphTargetInfluence. The morph affects the mesh as it should but it also shrinks any other mesh in the scene.
I've put together a jsfiddle (modified from the threejs morph target) example below to demo the issue. In this demo moving the slider correctly morphs the corner of the red cube but incorrectly shrinks the size of the blue cube. The cube meshes are entirely unrelated so I don't see how the morph could affect the blue cube.
https://jsfiddle.net/djmm7vv2/
// JS code from jsfiddle
var container, stats;
var camera, scene, renderer;
var geometry, objects;
var mesh, mesh2;
init();
animate();
function init() {
// Create cameras, lights, scene, ect.
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 15000 );
camera.position.z = 500;
scene = new THREE.Scene();
scene.background = new THREE.Color( 0x222222 );
scene.fog = new THREE.Fog( 0x000000, 1, 15000 );
var light1 = new THREE.PointLight( 'white', 1);
light1.position.set( 100, 100, 100 );
var light2 = new THREE.PointLight( 'white', 1);
light2.position.set( -100, 100, 100 );
scene.add( light1 );
scene.add( light2 );
// Create first mesh (red cube)
var geometry = new THREE.BoxGeometry( 100, 100, 100 );
var material = new THREE.MeshLambertMaterial( { color: 'red', morphTargets: true } );
// Create 1 blend shape
for ( var i = 0; i < 1; i ++ ) {
var vertices = [];
for ( var v = 0; v < geometry.vertices.length; v ++ ) {
vertices.push( geometry.vertices[ v ].clone() );
if ( v === i ) {
vertices[ vertices.length - 1 ].x *= 2;
vertices[ vertices.length - 1 ].y *= 2;
vertices[ vertices.length - 1 ].z *= 2;
}
}
geometry.morphTargets.push( { name: "target" + i, vertices: vertices } );
}
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
// Add second mesh (blue cube)
var blue = new THREE.MeshLambertMaterial( { color: 'blue', morphTargets: true } );
var box2 = new THREE.BoxGeometry( 100, 100, 100 );
mesh2 = new THREE.Mesh( box2, blue );
scene.add( mesh2 );
mesh2.position.x = 150
// Create simple GUI slider to change morph influence on mesh (red cube)
var params = {
influence1: 0,
};
var gui = new dat.GUI();
var folder = gui.addFolder( 'Morph Targets' );
folder.add( params, 'influence1', 0, 1 ).step( 0.01 ).onChange( function( value ) { mesh.morphTargetInfluences[ 0 ] = value; } );
folder.open();
// Render it all
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
mesh.rotation.y += 0.01;
renderer.render( scene, camera );
}
...And here's the answer https://github.com/mrdoob/three.js/issues/12691#issuecomment-345416590. My blue mesh material had morphTargets set to true. Changing it to false corrected the issue.
var blue = new THREE.MeshLambertMaterial( { color: 'blue', morphTargets: false } );
If anyone knows why this is the case I would love to know more. The documentation on MeshLambertMaterials is sparse. Maybe this has to do with keeping material textures from freaking out when the morph is applied?

Animate object along a path in three.js

I am trying to animate a cube along a path in three.js.
CODE
// Ellipse class, which extends the virtual base class Curve
var curve = new THREE.EllipseCurve(
0, 0, // ax, aY
16, 21.28, // xRadius, yRadius
0, 2 * Math.PI, // aStartAngle, aEndAngle
false, // aClockwise
0 // aRotation
);
//defines the amount of points the path will have
var path = new THREE.Path( curve.getPoints( 100 ) );
var geometrycirc = path.createPointsGeometry( 100 );
var materialcirc = new THREE.LineBasicMaterial( {
color : 0xff0000
} );
// Create the final object to add to the scene
var ellipse = new THREE.Line( geometrycirc, materialcirc );
ellipse.position.set(0,1,0);
this.scene.add( ellipse );
// add the box to the scene
this.scene.add(this.box);
I have being doing some research into how this could be done and came across this fiddle animate on path This method uses a the THREE.SplineCurve3 method to create the points for the box to use.
My question is do I need to convert my path to use the THREE.SplineCurve3 method.
Or can I use the path as it is?
Any help or pointers would be appreciated.
many thanks
Object Animating on path
Code
// GLOBALS - ALLOCATE THESE OUTSIDE OF THE RENDER LOOP - CHANGED
var cubes = [], marker, spline;
var matrix = new THREE.Matrix4();
var up = new THREE.Vector3( 0, 1, 0 );
var axis = new THREE.Vector3( );
var pt, radians, axis, tangent, path;
// the getPoint starting variable - !important - You get me ;)
var t = 0;
//This function generates the cube and chooses a random color for it
//on initial load.
function getCube(){
// cube mats and cube
var mats = [];
for (var i = 0; i < 6; i ++) {
mats.push(new
THREE.MeshBasicMaterial({color:Math.random()*0xffffff}));
}
var cube = new THREE.Mesh(
new THREE.CubeGeometry(2, 2, 2),
new THREE.MeshFaceMaterial( mats )
);
return cube
}
// Ellipse class, which extends the virtual base class Curve
function Ellipse( xRadius, yRadius ) {
THREE.Curve.call( this );
// add radius as a property
this.xRadius = xRadius;
this.yRadius = yRadius;
}
Ellipse.prototype = Object.create( THREE.Curve.prototype );
Ellipse.prototype.constructor = Ellipse;
// define the getPoint function for the subClass
Ellipse.prototype.getPoint = function ( t ) {
var radians = 2 * Math.PI * t;
return new THREE.Vector3( this.xRadius * Math.cos( radians ),
this.yRadius * Math.sin( radians ),
0 );
};
//
var mesh, renderer, scene, camera, controls;
function init() {
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
// scene
scene = new THREE.Scene();
// camera
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 20, 20, 20 );
// controls
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.addEventListener( 'change', render ); // use if there is no animation loop
controls.minDistance = 10;
controls.maxDistance = 50;
// light
var light = new THREE.PointLight( 0xffffff, 0.7 );
camera.add( light );
scene.add( camera ); // add to scene only because the camera has a child
// axes
scene.add( new THREE.AxisHelper( 20 ) );
////////////////////////////////////////
// Create the cube //
////////////////////////////////////////
marker = getCube();
marker.position.set(0,0,0);
scene.add(marker);
////////////////////////////////////////
// Create an Extruded shape //
////////////////////////////////////////
// path
path = new Ellipse( 5, 10 );
// params
var pathSegments = 64;
var tubeRadius = 0.5;
var radiusSegments = 16;
var closed = true;
var geometry = new THREE.TubeBufferGeometry( path, pathSegments, tubeRadius, radiusSegments, closed );
// material
var material = new THREE.MeshPhongMaterial( {
color: 0x0080ff,
} );
// mesh
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
//////////////////////////////////////////////////////////////////////////
// Create the path which is based on our shape above //
//////////////////////////////////////////////////////////////////////////
//Please note that this red ellipse was only created has a guide so that I could be certain that the square is true to the tangent and positioning.
// Ellipse class, which extends the virtual base class Curve
var curve = new THREE.EllipseCurve(
0, 0, // ax, aY
6, 11, // xRadius, yRadius
0, 2 * Math.PI, // aStartAngle, aEndAngle
false, // aClockwise
0 // aRotation
);
//defines the amount of points the path will have
var path2 = new THREE.Path( curve.getPoints( 100 ) );
geometrycirc = path2.createPointsGeometry( 100 );
var materialcirc = new THREE.LineBasicMaterial( {
color : 0xff0000
} );
// Create the final object to add to the scene
var ellipse = new THREE.Line( geometrycirc, materialcirc );
ellipse.position.set(0,0,0);
scene.add( ellipse );
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
// set the marker position
pt = path.getPoint( t );
// set the marker position
marker.position.set( pt.x, pt.y, pt.z );
// get the tangent to the curve
tangent = path.getTangent( t ).normalize();
// calculate the axis to rotate around
axis.crossVectors( up, tangent ).normalize();
// calcluate the angle between the up vector and the tangent
radians = Math.acos( up.dot( tangent ) );
// set the quaternion
marker.quaternion.setFromAxisAngle( axis, radians );
t = (t >= 1) ? 0 : t += 0.002;
renderer.render( scene, camera );
}
init();
animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/82/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
Conclusion
So I was very Fortunate to stumble upon the answer.
In my case it was the creation of a subclass to my object which allowed me to use it's data as points so that an object could use it as a guide.
Yes I am aware that you are thinking 'What is this guy talking about' so I have created a fiddle for you to look at and study.
Fiddle: Object Animating on path

Reusing Modified Three.js Geometry

I'm using Three.js to display the line of a particle following a random walk. Since geometries cannot be enlarged dynamically, I'm removing the line from the scene, altering the geometry and adding a new line to the scene. Here's the relevant code:
var step = .5;
var material = new THREE.LineBasicMaterial({ color: 0x0077ff });
var geometry = new THREE.Geometry();
geometry.vertices.push( new THREE.Vector3(0,0,0) );
scene.add( new THREE.Line( geometry, material ) );
var render = function () {
requestAnimationFrame( render );
renderer.render(scene, camera);
controls.update();
}
function addStep() {
scene.remove( scene.children[1] );
last = geometry.vertices[ geometry.vertices.length - 1 ];
geometry.vertices.push( new THREE.Vector3( last.x + (2*Math.random()-1)*step,
last.y + (2*Math.random()-1)*step, last.z + (2*Math.random()-1)*step ));
scene.add( new THREE.Line( geometry.clone(), material ) );
}
setInterval( addStep, 500);
render();
This codes works, but only if the geometry is cloned at each step. If the last line of addStep() is changed to
scene.add( new THREE.Line( geometry, material ) );
then it can be called as many times as desired before rendering starts to add line segments, but if called after rendering starts then nothing appears.
Presumably I'm missing something simple about graphics buffers or variable assignments, but I would appreciate if someone could explain why the modified geometry cannot be reused without cloning.
#PaulMasson Here is a jsFiddle with an adaptation of your code:
http://jsfiddle.net/theo/u6y4n67q/
function addStep() {
vertices = geometry.vertices;
last = vertices[ vertices.length - 1 ];
vertices.push(
new THREE.Vector3(
last.x + ( 2 * Math.random() - 1 ) * step,
last.y + ( 2 * Math.random() - 1 ) * step,
last.z + ( 2 * Math.random() - 1 ) * step )
);
geometry = new THREE.Geometry();
geometry.vertices = vertices;
scene.remove( line );
line = new THREE.Line( geometry, material )
scene.add( line );
}
The only real difference is that this snippet uses new THREE.Geometry() instead of clone.
My guess is that Three.js geometry is tightly associated with with its representation in buffer geometry. Thus the only way to edit the geometry is to clone it or new it.

Three js buffergeometry for spheres

Im trying to make a three js document which shows lots of spherical objects, the quickest way to do this is by using buffergeometry. From this post here I learned that I could convert normal geometry into buffergeometry using:
var sphere = new THREE.SphereGeometry( 4, 0.05, 0.025 );
var geometry = THREE.BufferGeometryUtils.fromGeometry( sphere );
But this does not seem to work for me, the rest of the code that creates the object reads:
var positions = new Float32Array( x_GAMA.length * 3 );
for ( var i = 0; i < x_GAMA.length; i += 1 ) {
// positions
positions[ 3*i ] = x_GAMA[i]*10000;
positions[ 3*i + 1 ] = y_GAMA[i]*10000;
positions[ 3*i + 2 ] = z_GAMA[i]*10000;
}
geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
var material = new THREE.PointCloudMaterial( {size:1,color:0x999999} );
geometry.computeBoundingSphere();
particleSystem = new THREE.PointCloud( geometry, material );
scene.add( particleSystem );
It works fine if I use var geometry = new THREE.BufferGeometry(); but this creates squares which I do not want. Anyone have any idea why this does not seem to work? Thanks in advance.
In Three.js r71 you can create Sphere Buffer Geometry like this:
var sphereGeometry = new THREE.SphereGeometry( 4, 3, 2 );
var bufferSphereGeometry = new THREE.BufferGeometry().fromGeometry( sphereGeometry );
In r72 dev you can do ít straightforward like this:
// constructor: radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength
var sphereGeo = new THREE.SphereBufferGeometry( 4, 3, 2 ); //r72

How can I put two different textures on the front and back of a plane?

PRoblem: i'm trying to create (just for fun) a simple poker card (with a card back and a card front).
I have two different images, for back and front.
I easily created a Plane geometry with a single texture for both sides, but i really don't know how to assign a texture for a side and the other texture for the other side...
i tried this (without success :( ):
var textureBack = new THREE.ImageUtils.loadTexture( 'images/cardBack.png' );
var textureFront = new THREE.ImageUtils.loadTexture( 'images/cardFront.png' );
var material1 = new THREE.MeshBasicMaterial( { map: textureBack } );
var material2 = new THREE.MeshBasicMaterial( { map: textureFront } );
var geometry = new THREE.PlaneGeometry( 90, 110, 1, 1 );
geometry.faces[ 0 ].materials.push( material1 );
geometry.faces[ 1 ].materials.push( material2 );
var card = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial());
any help, please? :)
Was searching for solution without duplicating all my geometry.
Here you go ladies and gentlemen...
var materials = [new THREE.MeshBasicMaterial({map: texture, side: THREE.FrontSide}),
new THREE.MeshBasicMaterial({map: textureBack, side: THREE.BackSide})];
var geometry = new THREE.PlaneGeometry(width, height);
for (var i = 0, len = geometry.faces.length; i < len; i++) {
var face = geometry.faces[i].clone();
face.materialIndex = 1;
geometry.faces.push(face);
geometry.faceVertexUvs[0].push(geometry.faceVertexUvs[0][i].slice(0));
}
scene.add(new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials)));
BOOM a Two Faced Plane for ya, the loop will also work with geometries with more faces, replicating each face and applying the BackSide texture to it.
Enjoy!
You need to place two plane geometries back-to-back.
First, create a geometry for the front.
var geometry1 = new THREE.PlaneGeometry( 90, 110, 1, 1 );
Now create another geometry for the back.
var geometry2 = new THREE.PlaneGeometry( 90, 110, 1, 1 );
Spin it 180 degrees.
geometry2.applyMatrix( new THREE.Matrix4().makeRotationY( Math.PI ) );
After you load the materials, create the meshes, and add them as children of a "card" object.
// textures
var textureFront = new THREE.ImageUtils.loadTexture('images/cardFront.png' );
var textureBack = new THREE.ImageUtils.loadTexture('images/cardBack.png' );
// material
var material1 = new THREE.MeshBasicMaterial( { color: 0xffffff, map: textureFront } );
var material2 = new THREE.MeshBasicMaterial( { color: 0xffffff, map: textureBack } );
// card
card = new THREE.Object3D();
scene.add( card );
// mesh
mesh1 = new THREE.Mesh( geometry1, material1 );
card.add( mesh1 );
mesh2 = new THREE.Mesh( geometry2, material2 );
card.add( mesh2 );
You'll have an easier time with this if you use WebGLRenderer.
Fiddle: http://jsfiddle.net/mdAb7/11/
Updated to three.js r.69

Categories