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
Nik
February 27, 2019, 8:38am
3
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 But for anyone interested; here’s the code in a test case:
FitFillTest.js
import {
Object3D,
Cache,
Texture,
PlaneGeometry,
MeshLambertMaterial,
Mesh,
ClampToEdgeWrapping,
RepeatWrapping,
MirroredRepeatWrapping,
This file has been truncated. show original
fitTexture.js
/**
* @param {Texture} texture - a texture containing a loaded image with a defined width and height
* @param {number} screenAspect - the aspect ratio (width / height) of the model that contains the texture
* @param {"fit"|"fill"|"stretch"} mode - three modes of manipulating the texture offset and scale
* @param {number} [alignH] - optional multiplier to align the texture horizontally - 0: left, 0.5: center, 1: right
* @param {number} [alignV] - optional multiplier to align the texture vertically - 0: bottom, 0.5: middle, 1: top
**/
export function fitTexture(texture, screenAspect, mode, alignH = 0.5, alignV = 0.5) {
const imageAspect = texture.image.width / texture.image.height;
This file has been truncated. show original
1 Like
Tibi
March 31, 2023, 6:00pm
8
Can this be done so that the texture always fills the mesh viewport and keep a correct ratio basically the texture will be cropped?