Texturing down plane (WebGL, Three.JS) - three.js

I am using three.js. with webGL. I have a single texture file called support.jpg 100x100.
I am creating planes on the fly, with different heights and widths. I need the support.jpg texture to scale to the width and then repeat down the plane. (as soon in image below)
For Example. If the plane was (height:10, width: 10) it would the texture once fiting. If the plane was (height:100, width: 10) it would have 10 of the textures repeating 10by10. If the plane was (height:100, width: 50) it would have 2 of the textures repeating 50by50.
Question: How Do I Create a plane that will have the correct texture mapping.
Here is what I have so far, but it is only rendering a single texture. (this is a width 200, height 800)
function CreateSupportBeam() {
var mesh, texture, material;
texture = THREE.ImageUtils.loadTexture("images/support.png");
material = new THREE.MeshBasicMaterial({ map: texture, transparent: true });
var uvs = [];
uvs.push(new THREE.Vector2(0,0));
uvs.push(new THREE.Vector2(1,0));
uvs.push(new THREE.Vector2(1,4));
uvs.push(new THREE.Vector2(0,4));
var geo = new THREE.PlaneGeometry(200, 800);
geo.faceVertexUvs[0].push([uvs[0], uvs[1], uvs[2], uvs[3]]);
mesh = new THREE.Mesh(geo, material);
scene.add(mesh);
}
rollercoaster.dickinsonbros.com/ <- This is the project I am working on.

You do not need to change the UVs.
Use a pattern like the following to avoid distortion and ensure that the pattern repeats and starts at the "top".
var geometry = new THREE.PlaneGeometry( length, height );
var scale = height / length;
var offset = Math.floor( scale ) - scale;;
var texture = THREE.ImageUtils.loadTexture( ... );
texture.wrapT = THREE.RepeatWrapping;
texture.wrapS = THREE.ClampToEdgeWrapping;
texture.repeat.set( 1, scale );
texture.offset.set( 0, offset );
If that is not exactly what you are looking for, experiment until you get it the way you want it.
three.js r.66

Related

Same image texture for merged shape

I have made a closed hemisphere by merging geometries of a hemisphere and a circle. I have a 360degree image for texture. I want the image to be applied as the texture to the combined geometry. Currently it is applying the texture twice: to the hemisphere and the circle separately.
I have seen some answers on editing the UV mapping, but I am not sure how to go about it.
Here is the code.
var loader = new THREE.TextureLoader();
loader.setPath(srcPath);
loader.load("./texture.jpg", function(texture) {
var hemiSphereGeom = new THREE.SphereGeometry(radius, radialSegments, Math.round(radialSegments / 4), 0, Math.PI * 2, 0, Math.PI * 0.5);
var objMaterial = new THREE.MeshPhongMaterial({
map: texture,
shading: THREE.FlatShading
});
objMaterial.side = THREE.BackSide;
var capGeom = new THREE.CircleGeometry(radius, radialSegments);
capGeom.rotateX(Math.PI * 0.5);
var singleGeometry = new THREE.Geometry();
var cap = new THREE.Mesh(capGeom);
var hemiSphere = new THREE.Mesh(hemiSphereGeom);
hemiSphere.updateMatrix();
singleGeometry.merge(hemiSphere.geometry, hemiSphere.matrix);
cap.updateMatrix();
singleGeometry.merge(cap.geometry, cap.matrix);
el.setObject3D('hemisphere',new THREE.Mesh(singleGeometry , objMaterial));
});
It appears that the code is seeing the closed hemisphere as the two separate entities still. I would try a 3D modeling program and make the shape there and loading it into the AFrame code. Then load the texture on the back side of the geometry.

Sprite scaled and distorted after loading - Three.js

I'm quite new to three.js, building 3d globe with satellite sprite moving around it.
The problem is - sprite image gets distorted after loading - aspect ratio is ignored and initial scale is about 5x of its size. I'm downscaling manually but the image gets distorted as I pick numbers manually for each axis e.g. satelliteSprite.scale.set(0.14, 0.075, 1);
My satellite image:
I have 2 questions here:
How do I load it correctly so at least aspect ratio will be
respected?
When I add AxesHelper to the sprite why its not aligned to the image plane?
original <img> vs downscaled sprite (0.1, 0.1, 0.1):
my code for satellite, earth, camera here:
//.........SPRITE
var satelliteTexture = new THREE.TextureLoader().load('assets/img/sections/section-astronaut/small-satellite.png');
var satelliteSprite = new THREE.Sprite(new THREE.SpriteMaterial({
map: satelliteTexture,
color: 0xffffff,
fog: false
}));
satelliteSprite.scale.set(0.14, 0.075, 1);
satelliteSprite.position.setFromSpherical(new THREE.Spherical().set(0.565, Math.PI * 2 - 0.9, Math.PI * 2 + 0.5));
earth.add(satelliteSprite);
window['satelliteSprite'] = satelliteSprite;
var axesHelperSatelliteSprite = new THREE.AxesHelper(5);
satelliteSprite.add(axesHelperSatelliteSprite);
//..........CAMERA
var camera = window['camera'] = new THREE.PerspectiveCamera(45, width / height, 0.01, 100);
camera.position.z = 1.5;
//..........EARTH
var earth = window['earth'] = new THREE.Mesh(
new THREE.SphereGeometry(earthRadius, earthSegments, earthSegments),
new THREE.MeshPhongMaterial({
map: THREE.ImageUtils.loadTexture('assets/img/sections/section-astronaut/2_no_clouds_4k.jpg'),
bumpMap: THREE.ImageUtils.loadTexture('assets/img/sections/section-astronaut/elev_bump_4k.jpg'),
bumpScale: 0.002,
specularMap: THREE.ImageUtils.loadTexture('assets/img/sections/section-astronaut/water_4k.png'),
specular: new THREE.Color(0x111111),
wireframe: false
})
);
earth.rotation.x = 0.4;
earth.rotation.y = -1.95;
1) create square png texture with transparency so it will not get stretched/distorted and now you can scale equally for each axis e.g. satelliteSprite.scale.set(0.08, 0.08, 0.08)
2) set filters and anisotropy for the texture
satelliteTexture.anisotropy = renderer.capabilities.getMaxAnisotropy();
satelliteTexture.minFilter = satelliteTexture.magFilter = THREE.LinearFilter;
satelliteTexture.minFilter = satelliteTexture.magFilter = THREE.NearestFilter;

three.js sprites seem to ignore parent's scale when using a PerspectiveCamera

I'm trying to have text sprites in the 3d scene with constant size (regardless of camera distance) using a PerspectiveCamera. In order to get non-sprites to have constant size, I make them children of a special "scaled" object which adjusts its scale as the camera distance to origin changes (see the code below). This works well to keep a general object roughly the same visual size, but when I add a sprite to the scaled object, the sprite seems to ignore its parent's scale (so it gets smaller and bigger as you zoom out and in).
Interestingly, when we switch to an orthographic camera (uncomment the appropriate line below), this special scaled object doesn't seem to affect children anymore (i.e., children don't stay a constant size). However, since we have an orthographic camera, sprites no longer scale as the camera distance changes (so they maintain a constant size), but this is independent of the scaled object.
I notice a few other similar questions and answers, including adjust the scale of the sprites themselves (it seems much easier to add all my sprites to a single scaling object), use an orthographic camera overlay to draw sprites (see also this) (but I want my sprites to be inside the 3d perspective scene).
So, my questions are: why do sprites not use scale according to their parent's scale when using a PerspectiveCamera? Also, why does my scaled object not work with the orthographic camera? Are these bugs or features of the cameras?
Thanks!
http://jsfiddle.net/LLbcs/8/
var camera, scene, renderer, geometry, material, mesh, text, controls;
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000); var scenescale=1;
//camera = new THREE.OrthographicCamera( -7,7,7,-7, 1, 20 );
camera.position.z = 10;
scene.add(camera);
scaled=new THREE.Object3D();
scene.add(scaled);
var textmaterial = new THREE.SpriteMaterial( {color: 'red', useScreenCoordinates: true, map: texttexture("hi")});
text = new THREE.Sprite( textmaterial );
text.position.set( 1, 1, 0);
scaled.add(text);
var geometry = new THREE.BoxGeometry( 1, 1,1 );
var material = new THREE.MeshLambertMaterial( { color: 0xffffff } );
mesh = new THREE.Mesh( geometry, material );
mesh.position.set(0,3,0);
scaled.add(mesh);
var light = new THREE.PointLight('green');
light.position.set(10,15,10);
camera.add(light);
light = new THREE.PointLight(0x333333);
light.position.set(-10,-15,-8);
camera.add(light);
renderer = new THREE.WebGLRenderer();
controls = new THREE.OrbitControls( camera, renderer.domElement );
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
var scale = camera.position.length()/10;
scaled.scale.set(scale,scale,scale);
render();
}
function render() {
renderer.render(scene, camera);
}
function texttexture(string) {
var fontFace = "Arial"
var size = "50";
var color = "white"
var squareTexture = true;
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
canvas.height = size;
var font = "Normal " + size + "px " + fontFace;
context.font = font;
var metrics = context.measureText(string);
var textWidth = metrics.width;
canvas.width = textWidth;
if (squareTexture) {
canvas.height = canvas.width;
}
var aspect = canvas.width / canvas.height;
context.textAlign = "center";
context.textBaseline = "middle";
context.fillStyle = color;
// Must set the font again for the fillText call
context.font = font;
context.fillText(string, canvas.width / 2, canvas.height / 2);
var t = new THREE.Texture(canvas);
t.needsUpdate = true;
return t;
}
If you want text to appear over a 3D scene and you don't care if it is static, why not try layering a div over the scene instead?
This will allow you to save graphics bandwidth and memory, improving performance of your scene and give you much better flexibility over what you display. It's also much easier to do and to maintain.

threejs merge plane geometries or create a texture from multiple images

I have a set of plane geometries, each having a their own texture from a url. At any point during navigation(zoom/pan) the container (THREE.Object3D) contains 26 plane geometries. How will i merge them to a single big plane so that i could apply a heighmap for all tiles in the merge geometry.
Or How could I get all the texture from the 36 images.(currently as a map property for MeshPhongMaterial) in to a single geometry?
EDIT:
Currently I create a Big geometry as suggested by Dainel. and put a texture to the geometry which is a combined texture of a set of images via canvas.
context = canvas.getContext( '2d' );
var img = Image();
img.src = url //url of the texture tile
context.drawImage(img,xpos, ypos);
This is done or every images. Each image have a url, xpos, ypos. Images are preloaded and after loading every images a callback is called which creates the geometry(plane) and add texture from the canvas.
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var material = new THREE.MeshPhongMaterial({ map : texture });
var geom = new THREE.PlaneGeometry(canvas.width, canvas.height, 57, 40);
var mesh = new THREE.Mesh(geom, material);
scene.add( mesh );
also i update the z vertex of geometry with the height values.
u may create one "big" plane and apply a merged texture to it
or u could manually set the vertex Y-values of each plane to match the corresponding part of the height map. so u have a set of separate planes that all share the same "base" height map.
regards,
daniel
Using terrainLoader class for threejs and canvas texture I was able to acheive
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, mywidth / myheight, 0.01, 10000);
camera.position.set(0, 0, 240);
scene.add(new THREE.AmbientLight(0xeeeeee));
renderer = new THREE.WebGLRenderer();
renderer.setSize(mywidth, myheight);
var group = new THREE.Object3D();
var canvas = document.createElement( 'canvas' );
canvas.width = mywidth;
canvas.height = myheight;
var ctx = canvas.getContext('2d');
/* adjust width, height, segments based on height data;*/
/* create a plane geometry */
var geom = new THREE.PlaneGeometry(800, 692, 40, 55);
/*
read height values from .bin file uing terrainLoader
and update the Z values of the plane
*/
heightdata = 'assets/heightmap.bin';
terrainLoader.load(heightdata, function(data) {
/* bin file generated from USGS SRTM tile */
for (var i = 0, l = geom.vertices.length ; i < l; i++) {
geom.vertices[i].z = data[i] / 200; /*simple scale */
}
geom.verticesNeedUpdate = true;
});
/*
create a texture from canvas created above with width and height
of the of the scene. This is to be careful as the size of texture
image may not be the same but as same ratio as height map.
Atleast for my case it was the situation. But geometry width and
texture width are in same ratio. So that i need a small array of
heightmap from bin but a much bigger texture
*/
var texture = new THREE.Texture(canvas);
var material = new THREE.MeshPhongMaterial({ map : texture });
var mesh = new THREE.Mesh(geom, material);
group.add( mesh );
scene.add(group);
Now to draw multiple images to canvas check this page
I will just duplicate the code from HTML5 Canvas Image Loader Tutorial
function loadImages(sources, callback) {
var images = {};
var loadedImages = 0;
var numImages = 0;
// get num of sources
for(var src in sources) {
numImages++;
}
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if(++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
var sources = {
darthVader: 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg',
yoda: 'http://www.html5canvastutorials.com/demos/assets/yoda.jpg'
};
You can call the loadImages function after updating the Plane geometry from heightmap in above code.
loadImages(sources, function(images) {
//context = context of the texture put in threejs texture
context.drawImage(images.darthVader, 100, 30, 200, 137);
context.drawImage(images.yoda, 350, 55, 93, 104);
});

How do you find the size of a particle in Three.js?

How do you find the size of a particle in Three.js?
With geometrys you can use the bounding box/sphere to calculate its size in three.js units. But a particle has no bounding box and I can't see of anyway to calulate its size.
Does anyone have any suggestions?
It depends what meaning of 'particle' you are dealing with. With a WebGLRenderer you would be referring to a vertex in a ParticleSystem, but with a CanvasRenderer you mean Particle (which is Canvas only). In terms of the ParticleSystem the material used to construct it has a size, like in the usual examples:
geometry = new THREE.Geometry();
for ( i = 0; i < 10000; i ++ ) {
var vertex = new THREE.Vector3(); // and set its location
geometry.vertices.push( vertex );
}
material = new THREE.ParticleBasicMaterial( {
size: 35, // This might be the size you are thinking of?
sizeAttenuation: false,
map: // Some THREE.Texture
});
particles = new THREE.ParticleSystem( geometry, material );
Then you can access the size, and modify it simply with
particles.material.size = newSize;
If you are creating a Particle for canvas, then it is very similar:
// Construct the particle with a material
var material = new THREE.ParticleBasicMaterial( {
map: new THREE.Texture( canvas ),
blending: THREE.AdditiveBlending
});
particle = new THREE.Particle( textMaterial );
// Just need to access the material to get the 'size'
var size = particle.material.size

Resources