Sprite scaled and distorted after loading - Three.js - 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;

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.

Three.js marker on mesh

I'm working on a visualization that uses a height map to create a 3d terrain, and want to add some markers for locations of cities on the map. I'm guessing some sort of raycaster and intersectObject might do it, but I'm lost as to how to place a marker directly on the surface of the terrain. For instance, I know that a city is at (640, 480) in the pixel units of my texture, so I'm doing something like:
// This is the terrain, loaded from a PNG of the digital elevation model
var planeGeo = new THREE.PlaneGeometry( bumpTexture.image.width, bumpTexture.image.height, 600, 450 );
var plane = new THREE.Mesh( planeGeo, customMaterial );
plane.rotation.x = -Math.PI / 2;
plane.position.y = 0;
scene.add( plane );
// At (640, 480) of the image, there should be a marker directly on top of the mesh.
var cube = new THREE.Mesh( new THREE.CubeGeometry( 20, 20, 20 ), new THREE.MeshNormalMaterial() );
cube.position.x = 640;
cube.position.y = 0;
cube.position.z = 480;
scene.add(cube);
raycaster.setFromCamera(cube, camera );
var intersects = raycaster.intersectObject(plane);
console.log(raycaster, intersects);
// no intersects???
Any suggestions, Threejs wizards?

Three.js Texture Mapping with No Scaling

I'm mapping a texture of dimensions 300 x 250 to a Mesh that is 600 x 20. The texture are being scaled down, distorting the image. Anyway to prevent this?
Ideally, I want to map the texture at 300 X 20, "cutting" away at 20 and above
My code is as follows.
var floorTx = THREE.ImageUtils.loadTexture( './walltile/floorTx.jpg' );
floorTx.wrapS = floorTx.wrapT = THREE.RepeatWrapping;
floorTx.repeat.set(2, 2);
var floorMat = new THREE.MeshPhongMaterial( { map: floorTx, specular: 0x050505, shininess: 100});
var floor = new THREE.Mesh(cube, floorMat );
floor.castShadow = true;
floor.receiveShadow = true;
floor.name = "floor";
floor.dynamic = true;
scene.add( floor );
The key is to set the texture's repeat property according to the size of the mesh.
floorTx.repeat.set(meshWidth / textureWidth, meshHeight / textureHeight);

Texturing down plane (WebGL, 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

ThreeJS Texture is pixelated when seen from distance

I was playing with webGL and ThreeJS, then I've got the following issue:
Textures with large images gets pixelated when seen from distance.
Check the example: http://jsfiddle.net/4qTR3/1/
Below is the code:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 10, 7000);
var light = new THREE.PointLight(0xffffff);
light.position.set(0, 150, 100);
scene.add(light);
var light2 = new THREE.AmbientLight(0x444444);
scene.add(light2);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var geometry = new THREE.PlaneGeometry(500, 500, 10, 10);
//I use different textures in my project
var texture = new THREE.ImageUtils.loadTexture(TEST_IMAGE);
var textureBack = new THREE.ImageUtils.loadTexture(TEST_IMAGE);
textureBack.anisotropy = renderer.getMaxAnisotropy();
texture.anisotropy = renderer.getMaxAnisotropy();
//Filters
texture.magFilter = THREE.NearestFilter;
texture.minFilter = THREE.LinearMipMapLinearFilter;
textureBack.magFilter = THREE.NearestFilter;
textureBack.minFilter = THREE.LinearMipMapLinearFilter;
var materials = [
new THREE.MeshLambertMaterial({
transparent: true,
map: texture,
side: THREE.FrontSide
}),
new THREE.MeshLambertMaterial({
transparent: true,
map: textureBack,
side: THREE.BackSide
})];
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));
}
planeObject = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials));
planeObject.overdraw = true;
planeObject.position.z = -5000;
scene.add(planeObject);
camera.position.z = 1000;
(function render() {
requestAnimationFrame(render);
planeObject.rotation.y += 0.02;
renderer.render(scene, camera);
})();
If the image of the texture has got text in it, the text becomes very pixelated with poor quality.
How can I fix this?
In order to not get pixelated you need to use mips but WebGL can't generate mips for non-power-of-2 textures. Your texture is 800x533, neither of those is a power of 2.
a couple of options
1) Scale the picture offline to powers of 2 like 512x512 or 1024x512
2) Scale the picture at runtime before making a texture.
Load the image yourself, once loaded make a canvas that is power-of-2. call drawImage(img, 0, 0, canvas.width, canvas.height) to scale the image into the canvas. Then load the canvas into a texture.
You also probably want to change your mag filtering from NearestFilter to LinearFilter.
Note: (1) is the better option. (2) takes time on the user's machine, uses more memory, and you have no guarantee what the quality of the scaling will be.
Example here.

Resources