我正在尝试使用 ThreeJS 库使用 2D 几何结构上的纹理来模拟图像变形效果。我想在空心圆(基本上是由 THREE.RingGeometry 函数构建的环)上应用纹理图像并获得此图像中显示的结果:
下面我展示了我在场景中获得的实心环及其线框版本的结果:
问题在于,正如您所看到的,纹理是从环的中心到外部以放射状方式应用的。然而,我真正需要的是以同心圆的方式应用纹理图像,如本问题的第一张图片所示。
这个想法是在环形上生成原始纹理的变形版本。我想知道如何通过 Three.js 以编程方式实现这种效果,使得目标形状可以是任意 2D 几何图形。
下面是我用来绘制场景的相关代码:
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);
您只需更改
RingGeometry
中的 UV 映射,如下所示:
uvs.push( new THREE.Vector2( o / thetaSegments, i / phiSegments ) );
此外,如果您想围绕环旋转纹理,可以通过改变
RingGeometry
参数来实例化 thetaStart
:
var geometry = new THREE.RingGeometry( 10, 20, 50, 5, thetaStart, Math.PI * 2 );
三.js r.67
对于使用更新版本 Three.js 的任何人,我都能够创建一个正确更新 uv 的 TypeScript 类。标准
RingGeomtry
的主要变化是从 uv
更新 const
定义,然后利用自之前提供正确答案以来已更改的 uv = new THREE.Vector2(j / phiSegments, i / thetaSegments);
。
行星环几何.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));
}
}
RingGeomtry r67的先前版本定义:https://github.com/mrdoob/ Three.js/blob/fa4e4ae77fe5a1f9506a91160cc7a9f4e61518fc/src/extras/geometries/RingGeometry.js
基于RingGeomtryr166更新:https://github.com/mrdoob/ Three.js/blob/1845f6cd0525b5c73b9da33c40e198c360af29f1/src/geometries/RingGeometry.js
土星环纹理应用的视觉示例:
相关代码
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);