Video Texture - how to keep video aspect in new material

Hello,
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: https://jsfiddle.net/xiaoming110/3yL5pfk6/37/;
  2. CSS3DObject is more flexible;
2 Likes

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.

2 Likes

Hi!
Based on @xiaomingTang’s fiddle:
https://jsfiddle.net/prisoner849/zmg4ybkq/

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.

4 Likes

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(child.name == "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

Can this be done so that the texture always fills the mesh viewport and keep a correct ratio basically the texture will be cropped?