Reputation: 621
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);
Upvotes: 2
Views: 2741
Reputation: 9260
For anyone using a more recent version three.js I was able to create a TypeScript class that updates the uv correctly. The main change from the standard RingGeomtry
is updating the uv
definition from a const
and then utilizing uv = new THREE.Vector2(j / phiSegments, i / thetaSegments);
which has changed since the previously correct answer provided.
planet-ring-geomtry.ts
import * as THREE from 'three';
export class PlanetRingGeomtry extends THREE.BufferGeometry {
parameters: {
innerRadius: number;
outerRadius: number;
thetaSegments: number;
phiSegments: number;
thetaStart: number;
thetaLength: number;
};
constructor(
innerRadius = 0.5,
outerRadius = 1,
thetaSegments = 32,
phiSegments = 1,
thetaStart = 0,
thetaLength = Math.PI * 2
) {
super();
this.parameters = {
innerRadius: innerRadius,
outerRadius: outerRadius,
thetaSegments: thetaSegments,
phiSegments: phiSegments,
thetaStart: thetaStart,
thetaLength: thetaLength,
};
thetaSegments = Math.max(3, thetaSegments);
phiSegments = Math.max(1, phiSegments);
// buffers
const indices = [];
const vertices = [];
const normals = [];
const uvs = [];
// some helper variables
let radius = innerRadius;
const radiusStep = (outerRadius - innerRadius) / phiSegments;
const vertex = new THREE.Vector3();
let uv = new THREE.Vector2();
// generate vertices, normals and uvs
for (let j = 0; j <= phiSegments; j++) {
for (let i = 0; i <= thetaSegments; i++) {
// values are generate from the inside of the ring to the outside
const segment = thetaStart + (i / thetaSegments) * thetaLength;
// vertex
vertex.x = radius * Math.cos(segment);
vertex.y = radius * Math.sin(segment);
vertices.push(vertex.x, vertex.y, vertex.z);
// normal
normals.push(0, 0, 1);
// uv
uv = new THREE.Vector2(j / phiSegments, i / thetaSegments);
uvs.push(uv.x, uv.y);
}
// increase the radius for next row of vertices
radius += radiusStep;
}
// indices
for (let j = 0; j < phiSegments; j++) {
const thetaSegmentLevel = j * (thetaSegments + 1);
for (let i = 0; i < thetaSegments; i++) {
const segment = i + thetaSegmentLevel;
const a = segment;
const b = segment + thetaSegments + 1;
const c = segment + thetaSegments + 2;
const d = segment + 1;
// faces
indices.push(a, b, d);
indices.push(b, c, d);
}
}
// build geometry
this.setIndex(indices);
this.setAttribute(
'position',
new THREE.Float32BufferAttribute(vertices, 3)
);
this.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));
this.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
}
}
Previous release definition for RingGeomtry r67: https://github.com/mrdoob/three.js/blob/fa4e4ae77fe5a1f9506a91160cc7a9f4e61518fc/src/extras/geometries/RingGeometry.js
Updated based on RingGeomtry r166: https://github.com/mrdoob/three.js/blob/1845f6cd0525b5c73b9da33c40e198c360af29f1/src/geometries/RingGeometry.js
Visual example of application of texture for Saturn's rings:
relevant code
var thetaSegments = 42;
var geometry = new PlanetRingGeomtry(
innerRadius,
outerRadius,
thetaSegments
);
geometry.rotateX(Math.PI / 2);
var mapTexture = this.textureLoader.load(map);
mapTexture.minFilter = THREE.NearestFilter;
var colorMapTexture = this.textureLoader.load(colorMap);
colorMapTexture.minFilter = THREE.NearestFilter;
var material = new THREE.MeshLambertMaterial({
map: colorMapTexture,
alphaMap: mapTexture,
transparent: true,
opacity: 0.98,
side: THREE.DoubleSide,
});
var ring = new THREE.Mesh(geometry, material);
ring.position.set(0, 0, 0);
Upvotes: 3
Reputation: 104793
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
Upvotes: 3