Alexander Mikhalchenko
Alexander Mikhalchenko

Reputation: 4565

Babylonjs VideoTexture aspect ratio

I have a script in BabylonJS that creates a background layer with the video feed. The problem is that I can not lock it to a certain aspect ratio.

 const background = new BABYLON.Layer("back", null, scene);
 BABYLON.VideoTexture.CreateFromWebCam(scene, function (videoTexture) {
    background.texture = videoTexture;
    background.texture.level = 0;
    background.texture.wAng = Math.PI;
 }, { maxWidth: window.innerWidth, maxHeight: window.innerHeight});

Code above properly sets the background to the webcam feed, but scales it with the window.

Calling background.texture.scale(20); has no effect since .canRescale for that texture returns false.

Trying to rescale the layer didn't help either.

This is the link to the playground.

I feel like the solution must be simple but I haven't managed to google it. Thanks.

Upvotes: 1

Views: 853

Answers (1)

Hugo McPhee
Hugo McPhee

Reputation: 11

I was looking for a simmilar way to set an image background, and ended up using a plane with a fixed aspect ratio and an orthographic camera

(code is using react-babylonjs, but the same properties should work in normal babylonjs)

first make an orthographic camera

<targetCamera
   name="camera1"
   position={new Vector3(0, 0, -2)}
   rotation={new Vector3(toRadians(0), toRadians(0), 0)}
   mode={Camera.ORTHOGRAPHIC_CAMERA}
   ref={scenePlaneCameraRef}
 />

then make a plane with size = 1

// the real size will be stretched with scale, 
// this works because 1 unit on the orthographic camera is the size of 1 pixel
<plane
  ref={planeRef}
  name="backdropPlane"
  size={1}
  billboardMode={AbstractMesh.BILLBOARDMODE_ALL}
/>

These are some functions to get the size the plane should be

export function defaultSize(engine) {
  return { width: 0, height: 0 };
}

export function getViewSize(engine) {
  if (!engine) return defaultSize();
  return {
    width: engine.getRenderWidth(),
    height: engine.getRenderHeight(),
  };
}

// this gets the wanted plane size so it fills the screen but keeps the aspect ratio
export function getPlaneSize(engine) {
  const viewSize = getViewSize(engine);

  const viewAspectRatio = viewSize.width / viewSize.height;
  const planeAspectRatio = 16 / 9;

  const viewIsThinner = viewAspectRatio < planeAspectRatio;

  if (viewIsThinner) {
    return {
      width: viewSize.height * planeAspectRatio,
      height: viewSize.height,
    };
  } else {
    return {
      width: viewSize.width,
      height: viewSize.width * (1.0 / planeAspectRatio),
    };
  }
}

export default function fitScenePlaneToScreen(planeMesh, engine) {
  const planeSize = getPlaneSize(engine);
  planeMesh.scaling.y = planeSize.height;
  planeMesh.scaling.x = planeSize.width;
}

Then resize the plane whenever the screen size changes

fitScenePlaneToScreen(scenePlane, engine);
engine.onResizeObservable.add(() => {
  fitScenePlaneToScreen(scenePlane, engine);
});

Then set the materials texture for that plane as the video texture

And one way to use it as a background is to have two scenes on top of eachother, where the behind scene uses the orthographic camera

Upvotes: 1

Related Questions