In a three.js scene, I have an image sprite whose image is constructed out of a html canvas. When I zoom in to the scene ( using mouse scroll ) the sprite appears larger and when I zoom out it appears smaller. But its not the case with other material like PointsMaterial. They appear smaller when I zoom in they appear smaller and when I zoom out they appear larger - which I think is the desired behavior.
Here is the code for my sprite that uses canvas:
var image = new Image();
image.src = canvas.toDataURL("image/png");
var imgLoader = new THREE.TextureLoader();
imgLoader.setCrossOrigin("");
var imgTexture = imgLoader.load(image.src);
imgTexture.minFilter = THREE.LinearFilter;
var spriteMaterial = new THREE.SpriteMaterial({
map: imgTexture,
depthWrite: false,
depthTest: false,
transparent: true,
});
var sprite = new THREE.Sprite(spriteMaterial);
sprite.scale.set(0.3*canvas.width, 0.25*canvas.height, 1);
sprite.position.x = x0; //fixed
sprite.position.y = y0; //fixed
sprite.position.z = z0; //fixed
var cx = (0.17/187)*w; // w is width of text present on canvas.
var cy = 0.35;
sprite.center.set(cx, cy);
Is there any setting that I forgot to get the behavior that I described?
They appear smaller when I zoom in and when I zoom out they appear larger - which I think is the desired behavior.
It sounds to me like you're trying to get rid of the sprite's size attenuation, so it maintains the same size regardless of distance to the camera. To achieve this, you can set the material's .sizeAttenuation property to false:
var spriteMaterial = new THREE.SpriteMaterial({
map: imgTexture,
depthWrite: false,
depthTest: false,
transparent: true,
sizeAttenuation: false
});
Related
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;
I'm using Three.js r69.
I'm drawing several sprites, each with a canvas texture so that I can draw text onto it. I'm following the suggestions found in this question, with a few simple modifications to make it work in r69. When I set the position of the sprite, it doesn't draw it at its position. Here's a screenshot of what's happening:
I'll also include the code I'm using to generate the sprite:
this.text = _text;
this.textColor = new THREE.Color();
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
//...set the font
//...measure the text width
//...get the aspect ratio of width to height of the text
//...set the background color
//...set the border color
var borderThickness = 2;
ctx.lineWidth = borderThickness;
roundRect(ctx,
borderThickness * 0.5, // x
borderThickness * 0.5, // y
this.textWidth + borderThickness, // width
fontsize * 1.4 + borderThickness, // height
6 // corner radius
);
//...set font color
ctx.fillText(this.text, borderThickness, fontsize + borderThickness);
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var spriteMaterial = new THREE.SpriteMaterial(
{map: texture}
);
this.object = new THREE.Sprite(spriteMaterial);
this.object.scale.set(2, 2/this.aspect, 1.0);
Am I doing something wrong when I'm generating the canvas and SpriteMaterial? Or is it something else? I'd like the sprite to pivot around its center, not some point that's completely outside the canvas.
Looks like I wasn't explicitly setting the size of the canvas, so it was defaulting to 300 x 150.
I set the canvas size to that of just the text label, and it positions it correctly.
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.
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
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.