Michaël
Michaël

Reputation: 609

Three.js : Modify the UV of my texture inside a custom ShaderMaterial

I've a plane geometry and I'm creating a CustomShader material related to it. It will receive some textures as uniforms. I'd like the textures to perfectly cover my plane (like the background-size:cover css property)

I managed to do it with an utility function when I used my textures with a MeshBasicMaterial :

cover( texture, aspect ) {

    var imageAspect = texture.image.width / texture.image.height;

    if ( aspect < imageAspect ) {
        texture.matrix.setUvTransform( 0, 0, aspect / imageAspect, 1, 0, 0.5, 0.5 );

    } else {
        texture.matrix.setUvTransform( 0, 0, 1, imageAspect / aspect, 0, 0.5, 0.5 );

    }

}

But unfortunately since I'm using the ShaderMaterial, my "cover" function doesn't apply anymore. Am I force to do it inside my fragment shader? If so how can I manage to reproduce this behavior ?

Here's my code :

const vertexShader = `
    precision highp float;
    uniform mat3 uUvTransform;
    varying vec2 vUv;
    void main() {
        vUv = ( uUvTransform * vec3( uv, 1 ) ).xy;
        gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}`;

const fragmentShader = `
    precision highp float;
    uniform sampler2D uText1;
    varying vec2 vUv;


    void main() {
        vec2 xy = vUv;

        vec4 color = texture2D(uText1,xy);
        gl_FragColor = color;
}`;

And here's my current result :

enter image description here

Thanks a lot

Upvotes: 1

Views: 1665

Answers (2)

soju
soju

Reputation: 25322

You could simply use a custom uniform, e.g. :

uniform sampler2D uText1;
uniform vec2 uUvScale;
varying vec2 vUv;
void main() {
    vec2 uv = (vUv - 0.5) * uUvScale + 0.5;
    gl_FragColor = texture2D(uText1, uv);
}

And :

var imageAspect = texture.image.width / texture.image.height;
if ( aspect < imageAspect ) {
    material.uniforms.uUvScale.value.set(aspect / imageAspect, 1)
} else {
    material.uniforms.uUvScale.value.set(1, imageAspect / aspect)
}

Upvotes: 3

M -
M -

Reputation: 28497

The way Three.js handles texture transformations like .offset, .repeat, .rotation, .center is via a Matrix3 that gets passed as a uniform into the vertex shader. The vertex shader performs the matrix multiplication, then passes the modified UVs as a varying to the fragment shader.

You could copy those lines of GLSL code to your ShaderMaterial's vertex shader, and I think the texture properties will come through in the Matrix3 automatically. However, if for some reason it doesn't, you could recreate the Matrix3 by copying it from the source and passing it as a uniform manually. I don't know what your utility function looks like, so it's hard to tell how you're achieving the desired scaling.

Upvotes: 0

Related Questions