Video Texture - how to keep video aspect in new material

I have got a scene that includes a 3D Object of a TV-Screen (the screen is squared):

The screen gets a new material that has video texture as map.

The video i want to play has an aspect of 16:9. The material is (since the screen is squared) squared too.

How can I play the video on the screen in the correct ratio?

Summary: I want to play a 16:9 video on a squared screen without stretching.

Thank you :slight_smile:

  1. handle uv, for example:;
  2. CSS3DObject is more flexible;

Use a custom GLSL shader that adjusts the UV. Like this example.

See the Pen GLSL Shaders - 49 by Nik Lever (@nik-lever) on CodePen.


Based on @xiaomingTang’s fiddle:

float ratio = 16. / 9.; //480./204.;

you just need to know the actual ratio of a video or an image, thus you can pass it to the shader as a uniform.


Hey and thank you for the answers.

How can I update the material of my object properly? Right now when I change the material the screen no longer gets animated. It happend to me several times before. To fix this I cloned the old material and it worked.

So how can I create a new material with the code from you fiddles and keep the functionality of my object getting animated.

I tried material.skinning = true; but it does not work.

I’m using the GLTF Loader.

    screen.scene.traverse(function (child) {
        if( == "Screen"){

            child.material = new THREE.ShaderMaterial({
                skinning : true,
                uniforms: {
                  texture: {
                    type: "t",
                    value: videoTexture
                side: THREE.DoubleSide,
                vertexShader: `
                  varying vec2 vUv;
                  void main() {
                    vUv = uv;
                    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
                fragmentShader: `
                    varying vec2 vUv;
                    uniform sampler2D texture;
                    void main() {
                        vec2 uv = vUv;
                    float ratio = 16. / 9.; //480./204.;
                    uv.y *= ratio;
                    uv.y -= (0.5 - (1. / ratio) * 0.5) * ratio;
                    vec3 col = texture2D(texture, uv).rgb;
                    col = mix(col, vec3(0), step(0.5, abs(uv.y - 0.5)));
                    gl_FragColor = vec4(col, 1.);

            child.material.needsUpdate = true;

            screenMixer = new THREE.AnimationMixer(screen.scene);

Thank you!

You can use material.onBeforeCompile() to change an existing material. Just search the forum, there are several topics about it.
Not the best one, just the first one I remembered about: FBX + Material.onBeforeCompile()

I wrote a function that manipulates the texture’s uv’s to fit/fill/stretch the media by some aspect ratio.
Now that I found this solution, I can throw it away :stuck_out_tongue: But for anyone interested; here’s the code in a test case:

1 Like