Akash K.
Akash K.

Reputation: 637

Three.js making fixed texture irrespective of mesh size

I'm working on a simple animation where I have to animate a plane's height from 100% to 0. The plane has a texture on it. I've checked multiple examples and couldn't find a way to make texture of fixed dimensions irrespective of mesh's dimensions.

I created a basic example of animation here

Can't use this as I might have a different background texture (not black).

After searching around for a couple more hours I found this question (when I was about to post a question myself). Found the interesting setUv() function. I modified this function to make another example. Basically I'm disposing the geometry on each iteration and assigning new texture to new geometry (smaller than previous one).

var group, mesh, geometry, meterial;
var planeHeight, planeWidth;
var UVwr, UVhr;
planeHeight = planeWidth = 3;
UVwr = UVhr = 1.5;
function drawElements(){
  group = new THREE.Group();
  geometry = new THREE.PlaneGeometry( planeWidth, planeHeight );
  var texture = new THREE.Texture( generateTexture() );
  texture.needsUpdate = true;
  texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
  texture.repeat.set(1,1);
  material = new THREE.MeshBasicMaterial( { map: texture, transparent: true } );
  plane = new THREE.Mesh( geometry, material );
  var v = plane.geometry.faceVertexUvs[0];
  setUv( v, 0, UVwr, UVhr );
  group.add( plane );
    scene.add( group );
}

/* Plane Geometry */
function setUv( v, index, wr, hr ) {
    for (var i=index*2; i<(index+1)*2; i++) {
        for (var j=0; j<3; j++) {
            v[i][j].x = v[i][j].x * wr;
            v[i][j].y = v[i][j].y * hr;         
        }
    }
}
function animate(){
  planeHeight -= 0.01;
  UVhr -= 0.005;
  planeHeight = planeHeight < 0.01 ? 3 : planeHeight;
  UVhr = UVhr < 0.005 ? 1.5 : UVhr;
  group.children[0].geometry.dispose();
  group.children[0].geometry = new THREE.PlaneGeometry( planeWidth, planeHeight );
  
  var v = plane.geometry.faceVertexUvs[0];
  setUv( v, 0, UVwr, UVhr );
}

I wanted to know if there is any other better approach to make this animation.

Maybe we could use 2 textures (1. image texture and 2. transparent texture), and apply the second texture (transparent one) with an offset and move that texture upwards from bottom. In this way we wont have to dispose geometry each time. (Just curious to know if thats possible)

Thanks!

Upvotes: 1

Views: 952

Answers (1)

prisoner849
prisoner849

Reputation: 17586

You know the parameters of your geometry, thus you can use it for calculation of uv coordinates:

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 10);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

var planeGeom = new THREE.PlaneBufferGeometry(10, 10);
console.log(planeGeom);
var planeMat = new THREE.MeshBasicMaterial({
  map: new THREE.TextureLoader().load("https://threejs.org/examples/textures/UV_Grid_Sm.jpg")
});
var plane = new THREE.Mesh(planeGeom, planeMat);
scene.add(plane);

var clock = new THREE.Clock();
var y = 0,
  yUv = 0,
  h = planeGeom.parameters.height;

renderer.setAnimationLoop(() => {

  y = Math.sin(clock.getElapsedTime()) * 2.5 + 2.5;
  
  plane.position.y = y;

  planeGeom.attributes.position.setY(0, h * 0.5 - y * 2);
  planeGeom.attributes.position.setY(1, h * 0.5 - y * 2);
  planeGeom.attributes.position.needsUpdate = true;

  yUv = (Math.sin(clock.getElapsedTime()) * h * 0.5 + h * 0.5) / h;

  planeGeom.attributes.uv.setY(0, 1 - yUv);
  planeGeom.attributes.uv.setY(1, 1 - yUv);
  planeGeom.attributes.uv.needsUpdate = true;

  renderer.render(scene, camera);
});
body {
  overflow: hidden;
  margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>

Upvotes: 2

Related Questions