three.js - 如何在应用纹理旋转时保留 threejs 纹理比例

标签 three.js

我想让用户能够在矩形上旋转纹理,同时保持纹理图像的纵横比不变。我正在矩形表面上旋转 1:1 纵横比图像(比如 width: 2length: 1)

重现步骤: 在下面的纹理旋转示例中

https://threejs.org/examples/?q=rotation#webgl_materials_texture_rotation

如果我们像下面这样改变几何体的一个面:

https://github.com/mrdoob/three.js/blob/master/examples/webgl_materials_texture_rotation.html#L57

var geometry = new THREE.BoxBufferGeometry( 20, 10, 10 );

然后您可以看到,当您使用旋转控件时,图像纵横比发生了扭曲。 (把正方形变成奇怪的形状)

在 0 度: At 0 Degree

在 0 到 90 之间的某个角度: enter image description here

我知道通过改变 repeatX 和 repeatY 因子我可以控制它。也很容易看出 0 度、90 度旋转时的值。

但我正在努力想出 repeatX 和 repeatY 的公式,该公式适用于给定矩形面的 lengthwidth 的任何纹理旋转。

最佳答案

不幸的是,当像那样拉伸(stretch)几何体时,您会在 3D 空间而非 UV 空间中得到扭曲。在此示例中,一个 UV.x 单元占用的 3D 空间是一个 UV.y 单元的两倍: enter image description here

这会在旋转之间为您提供那些水平倾斜的菱形: enter image description here

遗憾的是,没有办法通过纹理矩阵变换来解决这个问题。水平拉伸(stretch)将在 3D 空间中纹理变换后应用,因此 texture.repeat 无法帮助您避免这种情况。解决这个问题的唯一方法是修改 UV,使 UV.x 单位占用与 UV.y 单位一样多的 3D 空间: enter image description here

对于复杂的模型,您可以在 3D 编辑器中进行这种“均衡”,但由于几何结构足够简单,我们可以通过代码来完成。请参见下面的示例。我在我的 UV.y 重新映射中使用了一个宽度/高度比率变量,这样 UV 转换就会匹配,无论它有多宽。

//////// Boilerplate Three setup
const renderer = new THREE.WebGLRenderer({canvas: document.querySelector("canvas")});
const camera = new THREE.PerspectiveCamera(50, 1, 1, 100);
camera.position.z = 3;
const scene = new THREE.Scene();

/////////////////// CREATE GEOM & MATERIAL
const width = 2;
const height = 1;
const ratio= width / height;  // <- magic number that will help with UV remapping
const geometry = new THREE.BoxBufferGeometry(width, height, width);

let uvY;
const uvArray = geometry.getAttribute("uv").array;

// Re-map UVs to avoid distortion
for (let i2 = 0; i2 < uvArray.length; i2 += 2){
  uvY = uvArray[i2 + 1];  // Extract Y value, 
  uvY -= 0.5;             // center around 0
  uvY /= ratio;           // divide by w/h ratio
  uvY += 0.5;             // remove center around 0
  uvArray[i2 + 1] = uvY;
}
geometry.getAttribute("uv").needsUpdate = true;

const uvMap = new THREE.TextureLoader().load("https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/uv_grid_opengl.jpg");

// Now we can apply texture transformations as expected
uvMap.center.set(0.5, 0.5);
uvMap.repeat.set(0.25, 0.5);
uvMap.anisotropy = 16;

const material = new THREE.MeshBasicMaterial({map: uvMap});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

window.addEventListener("mousemove", onMouseMove);
window.addEventListener("resize", resize);

// Add rotation on mousemove
function onMouseMove(ev) {
	uvMap.rotation = (ev.clientX / window.innerWidth) * Math.PI * 2;
}

function resize() {
	const width = window.innerWidth;
	const height = window.innerHeight;
	renderer.setSize(width, height);
	camera.aspect = width / height;
	camera.updateProjectionMatrix();
}

function animate(time) {
  mesh.rotation.y = Math.cos(time/ 3000) * 2;
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}

resize();
requestAnimationFrame(animate);
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://threejs.org/build/three.js"></script>
<canvas></canvas>

关于three.js - 如何在应用纹理旋转时保留 threejs 纹理比例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60269433/

相关文章:

javascript - 球体的三个js buffergeometry

javascript - 将动画时序与数据同步(ThreeJS)

javascript - Three.js 从子对象获取父对象(一组)

Three.js - 平滑的阴影导致奇怪的边缘

javascript - 使用 Three.js 在立方体中制作图像库

javascript - 如何让我的网格物体离开屏幕?

javascript - Threejs ColorsNeedUpdate 不触发更新

javascript - Three.js 带摄像头选择对象,无鼠标

typescript - 计算三角形网格的 SDF 的最有效方法

three.js - 为什么带着色器的粒子系统不起作用?三.js