【问题标题】:How to preserve threejs texture scale while applying texture rotation应用纹理旋转时如何保留三个js纹理比例
【发布时间】:2020-06-01 19:08:36
【问题描述】:

我希望用户能够在矩形上旋转纹理,同时保持纹理图像的纵横比不变。我正在矩形表面上旋转 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 度:

0 到 90 度之间的某个角度:

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

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

【问题讨论】:

  • 这是您需要的吗? github.com/mrdoob/three.js/issues/1847
  • @gaitat 感谢您的建议,我已经阅读过,所有文章问题显示的是要更改哪个属性来编辑比例和偏移量。我已经知道可以通过编辑这些属性来完成。但问题是,是否有一种方法可以在不扭曲原始图像比例的情况下更改这些属性。
  • 这看起来像一个三角问题。如果你可以设置一个 JSfiddle 或类似的,我会研究它......
  • 我不完全确定您完成的结果应该是什么样子。您是否尝试在保持宽矩形的同时应用旋转而不获得拉伸的“菱形”外观?

标签: three.js


【解决方案1】:

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

这是在旋转之间为您提供那些水平倾斜的菱形:

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

对于复杂的模型,您可以在 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>

【讨论】:

  • 哇,感谢详细的回答,谢谢。会试试这个。
【解决方案2】:

首先,我同意@Marquizzo 为您的问题提供的解决方案。将 UV 显式设置为几何体应该是解决问题的最简单方法。

但@Marquizzo 没有回答为什么更改纹理矩阵(设置repeatXrepeatY)不起作用。

我们都知道二维旋转矩阵R

cos  -sin
sin  cos

在着色器中使用变换矩阵 T 计算 UV,这是您问题中的纹理矩阵。

T * UV = new UV

为了简化问题,我们只考虑轮换。并假设我们有另一个额外的矩阵X 用于计算新的 UV。然后我们有

X * R * UV = new UV

现在的问题是whether we can find a solution ofX, so that with any rotation, new UV of any points in your question can be calculated correctly。如果有X的解决方案,那么我们可以简单地使用

var X = new Matrix3();
//X.set(x,y,z,...)
texture.matrix.premultiply(X);

否则,我们无法找到您期望的方法。

让我们创建几个方程来计算X

在下图中,ABCD 是几何体的一个面,透明的绿色是纹理。 A 点的 UV 为 (0,1),B 点的 UV 为 (0,0),C 和 D 分别为 (1,0)、(1,1)。

第一个方程来自考虑,没有任何旋转,原始 UV 永远不应该改变(A 的 UV 总是 (0,1))。所以我们应该有

X * I * (0, 1) = (0, 1) // I is the identity matrix

从这里我们可以看到X也应该是一个单位矩阵。

那我们看看单位矩阵X能否满足第二个方程。第二个等式是什么?再次简化,以B 为旋转中心(原点)并将纹理旋转 90 度(逆时针)。尽管我们旋转 90 度,但我们使用 -90 来计算 UV。

将纹理旋转 90 度后点 A 的新 UV 应该是 E 的当前值。 E 的值为 (a/b, 0)。然后我们有 从这个方程我们可以看到X 不应该是一个单位矩阵,这意味着,我们无法找到X 的解决方案来解决您的问题

X * R * UV = new UV

当然,您可以更改计算新 UV 的着色器,但它比 @Marquizzo 提供的方式更难。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-06-07
    • 2014-09-05
    • 2017-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-19
    相关资源
    最近更新 更多