Reputation: 984
I'm trying 'fit-to-scale' a mapped image texture on a single plane. kinda simulating the behaviour of object-fit:cover
.
So the image map has to scale up or down propotionally to cover the whole plane completely.
I tried playing around with the repeat and offset of the texture but was unsuccessful. (commented out in my code)
As you can see what i have so far in the snippet below, still has the image stretch to fit. Any help is appreciated!
var renderer = new THREE.WebGLRenderer({ canvas : document.getElementById('canvas'), antialias:true});
renderer.setClearColor(0x7b7b7b);
// use device aspect ratio //
renderer.setPixelRatio(window.devicePixelRatio);
// set size of canvas within window
renderer.setSize(window.innerWidth, window.innerHeight);
// SCENE
var scene = new THREE.Scene();
// CAMERA
var camera = new THREE.PerspectiveCamera( 45, window.innerWidth/window.innerHeight, 0.1, 1000 );
camera.position.z = 5;
// MESH 0
// texture
var texture_0 = new THREE. TextureLoader().load("https://i.imgur.com/YO0ygMx.jpg");
texture_0.wrapS = THREE.ClampToEdgeWrapping;
texture_0.wrapT = THREE.RepeatWrapping;
// var tileWidth = 2;
// var tileHeight = 1;
// repeatX = tileWidth * 1024 / (tileHeight * 2048);
// repeatY = 1;
// texture_0.repeat.set(repeatX, repeatY);
var geometry_0 = new THREE.PlaneGeometry(1.3,1,32);
var material_0 = new THREE.MeshBasicMaterial({
color: 0xd8d0d1,
side: THREE.DoubleSide,
map: texture_0
});
var mesh_0 = new THREE.Mesh(geometry_0, material_0);
scene.add(mesh_0);
mesh_0.position.x = -0.7
// MESH 1
var texture_1 = new THREE.TextureLoader().load("https://i.imgur.com/YO0ygMx.jpg");
texture_1.wrapS = THREE.ClampToEdgeWrapping;
texture_1.wrapT = THREE.RepeatWrapping;
var geometry_1 = new THREE.PlaneGeometry(1,3,32);
var material_1 = new THREE.MeshBasicMaterial({
color: 0xd8d0d1,
side: THREE.DoubleSide,
map: texture_1
});
var mesh_1 = new THREE.Mesh(geometry_1, material_1);
scene.add(mesh_1);
mesh_1.position.x = 0.7
// RENDER + ANIMATE
function animate() {
/* render scene and camera */
renderer.render(scene,camera);
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
// RESIZE EVENTS
window.addEventListener('resize', onResize);
function onResize() {
width = window.innerWidth;
height = window.innerHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r120/three.min.js"></script>
<canvas id="canvas"></canvas>
Upvotes: 1
Views: 2466
Reputation:
There are a lot of possible aspects. There's the aspect of the texture itself, the size of the plane, the size the plane is displayed (like if you scaled it), etc..
Anyway, at a minimum you need to know the aspect of the image and the aspect of the plane. Unless you hard code the image size into your app you won't know the image aspect until after it's loaded. At that point you can compute the correct offset and repeat settings as detailed in this article
// Set the repeat and offset properties of the background texture
// to keep the image's aspect correct.
const planeAspect = planeWidth / planeHeight;
const imageAspect = texture.image.width / texture.image.height;
const aspect = imageAspect / planeAspect;
texture.offset.x = aspect > 1 ? (1 - 1 / aspect) / 2 : 0;
texture.repeat.x = aspect > 1 ? 1 / aspect : 1;
texture.offset.y = aspect > 1 ? 0 : (1 - aspect) / 2;
texture.repeat.y = aspect > 1 ? 1 : aspect;
var renderer = new THREE.WebGLRenderer({ canvas : document.getElementById('canvas'), antialias:true});
renderer.setClearColor(0x7b7b7b);
// use device aspect ratio //
renderer.setPixelRatio(window.devicePixelRatio);
// set size of canvas within window
renderer.setSize(window.innerWidth, window.innerHeight);
// SCENE
var scene = new THREE.Scene();
// CAMERA
var camera = new THREE.PerspectiveCamera( 45, window.innerWidth/window.innerHeight, 0.1, 1000 );
camera.position.z = 5;
// Set the repeat and offset properties of the background texture
// to keep the image's aspect correct.
function fixTexture(planeWidth, planeHeight) {
return function(texture) {
const planeAspect = planeWidth / planeHeight;
const imageAspect = texture.image.width / texture.image.height;
const aspect = imageAspect / planeAspect;
texture.offset.x = aspect > 1 ? (1 - 1 / aspect) / 2 : 0;
texture.repeat.x = aspect > 1 ? 1 / aspect : 1;
texture.offset.y = aspect > 1 ? 0 : (1 - aspect) / 2;
texture.repeat.y = aspect > 1 ? 1 : aspect;
}
}
// MESH 0
// texture
var texture_0 = new THREE. TextureLoader().load("https://i.imgur.com/YO0ygMx.jpg", fixTexture(1.3, 1));
texture_0.wrapS = THREE.ClampToEdgeWrapping;
texture_0.wrapT = THREE.RepeatWrapping;
// var tileWidth = 2;
// var tileHeight = 1;
// repeatX = tileWidth * 1024 / (tileHeight * 2048);
// repeatY = 1;
// texture_0.repeat.set(repeatX, repeatY);
var geometry_0 = new THREE.PlaneGeometry(1.3,1,32);
var material_0 = new THREE.MeshBasicMaterial({
color: 0xd8d0d1,
side: THREE.DoubleSide,
map: texture_0
});
var mesh_0 = new THREE.Mesh(geometry_0, material_0);
scene.add(mesh_0);
mesh_0.position.x = -0.7
// MESH 1
var texture_1 = new THREE.TextureLoader().load("https://i.imgur.com/YO0ygMx.jpg", fixTexture(1,3));
texture_1.wrapS = THREE.ClampToEdgeWrapping;
texture_1.wrapT = THREE.RepeatWrapping;
var geometry_1 = new THREE.PlaneGeometry(1,3,32);
var material_1 = new THREE.MeshBasicMaterial({
color: 0xd8d0d1,
side: THREE.DoubleSide,
map: texture_1
});
var mesh_1 = new THREE.Mesh(geometry_1, material_1);
scene.add(mesh_1);
mesh_1.position.x = 0.7
// RENDER + ANIMATE
function animate() {
/* render scene and camera */
renderer.render(scene,camera);
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
// RESIZE EVENTS
window.addEventListener('resize', onResize);
function onResize() {
width = window.innerWidth;
height = window.innerHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r120/three.min.js"></script>
<canvas id="canvas"></canvas>
Upvotes: 5