我希望我的纹理具有与“ background-size:cover” css属性相同的行为。
我想使用uvs坐标。 我看着这个答案,开始着手解决方案:Three.js Efficiently Mapping Uvs to Plane
我尝试使尺寸/位置平面与DOM的div相同。 这就是我想要的:
这是我通过这段代码得到的结果:尺寸和位置都很好,我的纹理比例也很好,但是似乎存在缩放问题:
let w = domElmt.clientWidth / window.innerHeight;
let h = domElmt.clientHeight / window.innerHeight;
geometry = new THREE.PlaneGeometry(w, h);
var uvs = geometry.faceVertexUvs[ 0 ];
uvs[ 0 ][ 0 ].set( 0, h );
uvs[ 0 ][ 1 ].set( 0, 0 );
uvs[ 0 ][ 2 ].set( w, h );
uvs[ 1 ][ 0 ].set( 0, 0 );
uvs[ 1 ][ 1 ].set( w, 0 );
uvs[ 1 ][ 2 ].set( w, h );
tex = new THREE.TextureLoader().load('image.jpg'));
tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
material = new THREE.MeshBasicMaterial( { map: tex } );
mesh = new THREE.Mesh( geometry, material );
我应该使用纹理的repeat属性,还是可以使用uvs完全实现这种行为?谢谢
答案 0 :(得分:2)
https://en.wikipedia.org/wiki/UV_mapping
UV映射值的范围从0
到1
(包括端值),代表整个纹理图像的百分比映射。
您使用的是div大小与窗口大小的比率,该比率可能比1
小得多,并且会导致您看到“放大”效果。
例如,如果w
和h
的值为0.5
,则映射纹理的最右上角将是图像的确切中心。 / p>
在不拉伸图像的情况下尽可能放大图像。如果图像的比例与元素不同,则可以垂直或水平裁剪图像,以确保没有空白空间。
换句话说,它将根据短边的大小缩放图像,并裁剪其余部分。因此,假设您有一个不错的128x512
图像和一个64x64
空间。 cover
会将128
的宽度缩小到64
(缩放因子0.5
),因此将512
乘以0.5
得到新高度(128
)。现在您的w
仍为1
,但您的h
将为128 / 512 = 0.25
。现在,您的纹理将适合宽度,并裁剪高度。
您需要针对每个图像与容器的尺寸关系执行此计算,以找到合适的UV,同时请记住缩放比例始终与短边相关。
答案 1 :(得分:0)
您不需要生成UV,只需使用texture.repeat和texture.offset
const aspectOfPlane = planeWidth / planeHeight;
const aspectOfImage = image.width / image.height;
let yScale = 1;
let xScale = aspectOfPlane / aspectOfImage;
if (xScale > 1) { // it doesn't cover so based on x instead
xScale = 1;
yScale = aspectOfImage / aspectOfPlane;
}
texture.repeat.set(xScale, yScale);
texture.offset.set((1 - xScale) / 2, (1 - yScale) / 2);
'use strict';
/* global THREE */
async function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 50;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 4;
const scene = new THREE.Scene();
const loader = new THREE.TextureLoader();
function loadTexture(url) {
return new Promise((resolve, reject) => {
loader.load(url, resolve, undefined, reject);
});
}
const textures = await Promise.all([
"https://i.imgur.com/AyOufBk.jpg",
"https://i.imgur.com/ZKMnXce.png",
"https://i.imgur.com/TSiyiJv.jpg",
"https://i.imgur.com/v38pV.jpg",
].map(loadTexture));
const geometry = new THREE.PlaneBufferGeometry(1, 1);
const material = new THREE.MeshBasicMaterial({map: textures[0]});
const planeMesh = new THREE.Mesh(geometry, material);
scene.add(planeMesh);
let texIndex = 0;
function setTexture() {
const texture = textures[texIndex];
texIndex = (texIndex + 1) % textures.length;
// pick and random width and height for plane
const planeWidth = rand(1, 4);
const planeHeight = rand(1, 4);
planeMesh.scale.set(planeWidth, planeHeight, 1);
const image = texture.image;
const aspectOfPlane = planeWidth / planeHeight;
const aspectOfImage = image.width / image.height;
let yScale = 1;
let xScale = aspectOfPlane / aspectOfImage;
if (xScale > 1) { // it doesn't cover so based on x instead
xScale = 1;
yScale = aspectOfImage / aspectOfPlane;
}
texture.repeat.set(xScale, yScale);
texture.offset.set((1 - xScale) / 2, (1 - yScale) / 2);
material.map = texture;
}
setTexture();
setInterval(setTexture, 1000);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
function rand(min, max) {
if (max === undefined) {
max = min;
min = 0;
}
return Math.random() * (max - min) + min;
}
main();
body {
margin: 0;
}
#c {
width: 100vw;
height: 100vh;
display: block;
}
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r105/three.min.js"></script>
texture.repeat和texture.offset实际上仅应用于UV,因此,如果您真的想要UV,则
newU = u * repeat.x + offset.x;
newV = v * repeat.y + offset.y;
所以使用上面的代码
offsetX = (1 - xScale) / 2;
offsetY = (1 - yScale) / 2;
u0 = offsetX;
v0 = offsetY;
u1 = offsetX + xScale;
v1 = offsetY + yScale;
如此
var uvs = geometry.faceVertexUvs[ 0 ];
uvs[ 0 ][ 0 ].set( u0, v1 );
uvs[ 0 ][ 1 ].set( u0, v0 );
uvs[ 0 ][ 2 ].set( u1, v1 );
uvs[ 1 ][ 0 ].set( u0, v0 );
uvs[ 1 ][ 1 ].set( u1, v0 );
uvs[ 1 ][ 2 ].set( u1, v1 );