Non-radial texture mapping over a ring geometry in WebGL using Three.js - opengl-es

I am trying to simulate image deformation effects using textures over 2D geomtries using the ThreeJS library. I want to apply a texture image over a hollow circle (basically, a ring built by the THREE.RingGeometry function) and obtain the results shown at this image:
Following I show the results I am obtaining in my scene both for the solid ring and its wireframed version:
The problem is that, as you see, the texture is been applied in a radial way, from the center of the ring to the outside. However, what I really need is to apply the texture image on a concentric circle way, as shown in the first image of this question.
The idea is to produce a deformed version of the original texture over a ring shape. I would like to know how this effect can be programmatically achieved through Three.js in such a way that the destination shape can be any arbitrary 2D geometry .
Following, there is the relevant code I am using to draw my scene:
var texture = THREE.ImageUtils.loadTexture('./images/texture.png');
var wireRing = new THREE.Mesh(new THREE.RingGeometry(10, 20, 50, 5, 0, Math.PI * 2), new THREE.MeshBasicMaterial({map: texture, wireframe: true}));
wireRing.position.set(-25, 50, 0);
scene.add(wireRing);
var ring = new THREE.Mesh(new THREE.RingGeometry(10, 20, 50, 5, 0, Math.PI * 2), new THREE.MeshBasicMaterial({map: texture}));
ring.position.set(25, 50, 0);
scene.add(ring);

You just need to change the UV mapping in RingGeometry like so:
uvs.push( new THREE.Vector2( o / thetaSegments, i / phiSegments ) );
Also, if you want to rotate the texture around the ring, you instantiate the RingGeometry by varying the thetaStart parameter:
var geometry = new THREE.RingGeometry( 10, 20, 50, 5, thetaStart, Math.PI * 2 );
three.js r.67

Related

Map Texture to Plane without distortion

In Three.js, how can I change the way in which a texture gets mapped onto a plane?
Let's assume we have a 1x1 plane and a 16:9 image. How can I control the way in which that image gets mapped onto the plane?
By default, the image gets "squished". I would like it to maintain its aspect ratio and have any overlap get "cut off". Is there a way to configure the material or texture to do this, or would I use a shader? If so, what would it need to look like?
const planeMesh = new THREE.Mesh(
new THREE.PlaneBufferGeometry(1, 1),
new THREE.MeshBasicMaterial({
map: texture,
})
);
PS: In future, I would also like to be able to zoom into and out of the image on mouse hover without affecting the size of the plane, so would think a shader might be better?
A Texture already has several properties built-in that can do what you're looking for.
const texture = textureLoader.load("whatever.png");
const planeMesh = new THREE.Mesh(
new THREE.PlaneBufferGeometry(1, 1),
new THREE.MeshBasicMaterial({
map: texture,
})
);
// Sets the pivot point to the center of the texture
texture.center.set(0.5, 0.5);
// Make the texture repeat 0.5625 times in the x-axis to match 16:9 ratio
let ratio = 9 / 16;
texture.repeat.set(ratio, 1);
// Scale texture up to "zoom" into it
let zoom = 0.5;
texture.repeat.set(ratio * zoom, 1 * zoom);
You can read more about the .repeat .center and even .rotation properties in the Texture docs. Just keep in mind that repeating a texture is a bit counter-intuitive because you're doing the inverse of scaling a texture. So to scale a texture by 2, you have to tell it to repeat 1/2 times.

How to move an object in particular direction with specific distance in three.js

I need to move an object along directional vector through some distance. I fount translateOnAxis(vector, distance) of Object3D class. But I'm not able to understand how it works.
I've an object- sphere. I'm scaling it to look like ellipse. And setting position and direction. Now I need this object to move in the same direction which I'm setting it to, through some distance. When I apply it, I can't see the object. Can anybody suggest how it can be achieved?
var geometry = new THREE.SphereGeometry( radius, 64, 64, 0, -Math.PI );
geometry.applyMatrix( new THREE.Matrix4().makeScale( 1, 1, zScale ); //scaling it to look like ellipse
var direction = new THREE.Vector3( xDir, yDir, zDir);
var ellipse = new THREE.Mesh( geometry, material );
ellipse.lookAt(direction);
ellipse.position.set( xPos, yPos, zPos);
ellipse.translateOnAxis(direction, distance);
Your pasted code is buggy.
You're missing a ) on your applyMatrix line.
Are you using a debugger and observing console errors/warnings?

Is there ANY way to have the three.js camera lookat being rendered off-center?

Is there a way to setup the Three.js renderer in such a way that the lookat point of the camera is not in the center of the rendered image?
To clarify: image a scene with just one 1x1x1m cube at ( 0, 0, 0 ). The camera is located at ( 0, 0, 10 ) and the lookat point is at the origin, coinciding with the center of the cube. If I render this scene as is, I might end up with something like this:
normal render
However I'd like to be able to render this scene in such a way that the lookat point is in the upper left corner, giving me something like this:
desired render
If the normal image is 800x600, then the result I envision would be as if I rendered a 1600x1200 image with the lookat in the center and then cropped that normal image so that only the lower right part remains.
Of course, I can change the lookat to make the cube go to the upper left corner, but then I view the cube under an angle, giving me an undesired result like this:
test.moobels.com/temp/cube_angle.jpg
I could also actually render the full 1600x1200 image and hide 3/4 of the image, but one would hope there is a more elegant solution. Does anybody know it?
If you want your perspective camera to have an off-center view, the pattern you need to use is:
camera = new THREE.PerspectiveCamera( for, aspect, near, far );
camera.setViewOffset( fullWidth, fullHeight, viewX, viewY, viewWidth, viewHeight );
See the docs: https://threejs.org/docs/#api/cameras/PerspectiveCamera
You can find examples of this usage in this example and this example.
three.js r.73
Here's a simple solution:
Assuming your cube is 4 x 4 x 4, at position 0, 0, 0:
var geometry = new THREE.BoxGeometry( 4, 4, 4 );
var material = new THREE.MeshBasicMaterial( { color: 0x777777 } );
var cube = new THREE.Mesh( geometry, material );
cube.position.set( 0, 0, 0 );
Get cube's position:
var Vx = cube.position.x,
Vy = cube.position.y,
Vz = cube.position.z;
Then deduct by 2 from x position, then add 2 to y and z position, and use the values to create a new Vector3:
var newVx = Vx - 2,
newVy = Vy + 2;
newVz = Vz + 2;
var xyz = new THREE.Vector3(newVx, newVy, newVz)
Then camera lookAt:
camera.lookAt(xyz);
Using console log, it would show that the camera is now looking at -2, 2, 2, which is the upper-left of your cube.
console.log(xyz);

Positioning Objects at the same Elevation

I have an issue with the position of cubes in my application. When I set them all with the same size they are rendered properly on the same Y position as I defined:
Example:
geometry = new THREE.BoxGeometry(50, 50, 50);
material = new THREE.MeshBasicMaterial({ color: 0xff0000 })
mesh = new THREE.Mesh(geometry, material);
mesh.position.set(100, 0, 400); // I always set y as 0 because I want the cubes to be on the same level like buildings in a city
And I do the same for the next cubes, only changing the X and Z positions.
However, when I create cubes with different sizes, which is my objective, as follows,
geometry = new THREE.BoxGeometry(50, 100, 50);
they appear on a different level in the final visualization on the browser, as shows the image:
https://cloud.githubusercontent.com/assets/3678443/8651664/35574c18-2972-11e5-8c75-2612733ea595.png
Any ideas on how to solve this problem? What am I doing wrong?
BoxGeometry is centered on the origin. There are two solutions to translating the box so it sits on the XZ-plane.
Option 1. Translate the geometry so the bottom face of the box passes through the origin. You do that by translating the geometry up by half its height.
geometry = new THREE.BoxGeometry( 50, 50, 50 );
geometry.translate( 0, 50 / 2, 0 );
mesh = new THREE.Mesh( geometry, material );
mesh.position.set( 100, 0, 400 );
Option 2. Translate the mesh by setting its position.
geometry = new THREE.BoxGeometry( 50, 50, 50 );
mesh = new THREE.Mesh( geometry, material );
mesh.position.set( 100, 50 / 2, 400 );
The first option is likely preferable for your use case.
three.js r.92
The Position of the Objects is correct, they are placed where their centerĀ“s are. So your cube with 100 height in geometry extends 50 to the top and 50 to the bottom, its centroid is right in its "middle" at 0.
You could set the y positions of your Cubes to y + cube.geometry.parameters.height / 2 so every cube is aligned at one level (variable y).

Lighting effect looks weird with mesh Lambert material

I have a problem using Mesh Lambert Material with Three.js, I have a plane with different height points, but when trying to apply light the higher points are the ones that look darker, and it should be the other way around, my code looks like the following:
var geometry = new THREE.PlaneBufferGeometry(7500, 7500, worldWidth - 1, worldDepth -1);
geometry.computeFaceNormals();
geometry.applyMatrix(new THREE.Matrix4().makeRotationX(- Math.PI / 2));
texture = THREE.ImageUtils.loadTexture('img/grass.png');
mesh = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({ map: texture }));
scene.add(mesh);
var pointLight = new THREE.PointLight(0xffffff, 100, 0);
pointLight.position.set(5, 50, 5);
scene.add(pointLight);
And the result looks like the following:
Thank you so much in advance.
In your call to
THREE.PointLight(0xffffff, 100, 0);
you are specifying intensity to be 100 when the normal range is 0.0 - 1.0 so your scene comes out over-lit.

Resources