0
votes

I have a classic mesh composed by a THREE.PlaneGeometry and a material. If I add a THREE.MeshNormalMaterial() here's the result I get :

enter image description here

So far, so good. But when I call my THREE.ShaderMaterial(), using an external texture, the dimension of my mesh completely changes :

enter image description here

I always get that weird ratio even if - like in the screenshot - my texture is a square (512x512). I just want my MaterialShader to fit inside my geometry.

Here is the code of my MaterialShader :

var material = new THREE.ShaderMaterial( {
   uniforms: uniforms,
   vertexShader: document.getElementById( 'vertexShader' ).textContent,
   fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
} );

var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );

I don't see what I'm missing. Does anyone has an idea ? Thank you very much.

UPDATE : Here's the fully code of my ShaderMaterial :

material = new THREE.ShaderMaterial({
    uniforms:{
        u_time: { type: "f", value: 1.0 },
        u_resolution: { type: "v2", value: new THREE.Vector2() },
        u_mouse: { type: "v2", value: new THREE.Vector2() },
        texture1: { type: "t", value: texture }
    },
    vertexShader:`
        void main() {
            gl_Position = vec4( position, 1.0 );
        }
    `,
    fragmentShader:`
        #ifdef GL_ES
            precision highp float;
            precision highp int;
        #endif

        uniform vec2 u_resolution;
        uniform vec2 u_mouse;
        uniform float u_time;

        uniform sampler2D texture1;

        void main(){

            float pyt=3.1415926*2./3.;
            float m=-1e10;
            vec4 mv= vec4(0.);

            vec2 xy = gl_FragCoord.xy/u_resolution.xy;

            int ic=0;

            for (int i=0;i<30;i++){
                vec2 np=vec2(xy.x+float(i)/u_resolution.x*sin(3.14/2.)  * 4.,xy.y+float(i)/u_resolution.y*cos(3.14/2.) * 4.);


                float jTime = u_time*1.618;  
                vec4 tk=texture2D(texture1,np);
                float t=tk.r*sin(jTime)+tk.g*sin(jTime+pyt)+tk.b*sin(jTime+2.*pyt)-.01*float(i);

                if (t>m){m=t; mv=tk;ic=i;}
            }
            float sc=float(ic)/30.;
            vec4 tk=texture2D(texture1,xy);
            mv=sc*tk+(1.-sc)*mv;
            gl_FragColor = vec4(mv.r,mv.g,mv.b,1.0);
        }
    `
});

UPDATE2 : I changed my vertex shader but nothing has changed.

I might have a lead : I think this is related to my camera settings. I changed them and I've a better result. Now my texture fits into my square mesh.

Unfortunately, the scale isn't good. Since my texture is a square too, I want it to have exactly the same size than my mesh, for now it's zoomed.

How can I manage the size of my texture ? Should I do it inside my vertexShader ?

Here's my texture settings for now :

texture = new THREE.TextureLoader().load( "test5.jpg");
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;

UPDATE 3 :

I found that code to apply a texture and make it fit to my mesh : https://bl.ocks.org/duhaime/c8375f1c313587ac629e04e0253481f9

It's working but as soon as I change the example fragement shader by mine, I've no errors but the shaders become one unique color. I don't understand what I'm missing...

1
Could you provide the object of uniforms and the code of shaders?prisoner849
Sure, just added it @prisoner849 !Michaël
@prisoner849 I'm still looking for a way to make my square texture fit perfectly to my square mesh, I added several updates to my main post but I still can't manage to do it...Michaël
Just out curiousity, why do you use such a complicated fragment shader for such a simple task like mapping a texture to a plane? I've updated the answer. A square texture to a square plane.prisoner849
@prisoner849, thank you for your answer and your time ! I'm using a fragment shader because I want my texture to get distord in addition to perfectly fit into my square (here the fragment I want to use : shadertoy.com/view/Xt2yzc you can see the result in my screenshot upper). I successfully made the bridge between the vertex shader and the fragment shader using varying vec2 vUv. Unfortunately my issue remains because the two times my texture2D(texture1,xy) function is called, it doesn't use vUv as second parameter.Michaël

1 Answers

1
votes

Try this code of the vertex shader:

void main() {
  gl_Position = projectionMatrix *
                modelViewMatrix *
                vec4(position,1.0);
}

Reference

Simply pass the uv coordinates from the vertex shader to the fragment shader and use them there.

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 5);
camera.lookAt(scene.position);
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x404040);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

var iResolution = new THREE.Vector2();

var planeGeo = new THREE.PlaneBufferGeometry(5, 5);
var planeMat = new THREE.ShaderMaterial({
  uniforms: {
    texture: {
      value: null
    },
    iResolution: {
      value: iResolution
    },
    iTime: {
      value: 0
    }
  },
  vertexShader: `
    varying vec2 vUv;
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix *
                    modelViewMatrix *
                    vec4(position,1.0);
    }
  `,
  fragmentShader: `
    uniform sampler2D texture;
    uniform float iTime;
    uniform vec2 iResolution;
    
    varying vec2 vUv;
    
    void main() {
      float pyt=3.1415926*2./3.;
      float m=-1e10;//very negitive start value for maximisation algorithm.
      vec4 mv= vec4(0.);//lank starting value of max so far

      vec2 xy = vUv;
      int ic=0;//stores smear distance
      for (int i=0;i<30;i++){
          //point offset on a circle
          vec2 np=vec2(xy.x+float(i)/iResolution.x*sin(iTime),xy.y+float(i)/iResolution.y*cos(iTime));
          //colour cycles faster than position
          float jTime = iTime*1.618;  
          //get neerby point
        vec4 tk=texture2D(texture,np);
          // and if its colourfull enough, use that
          float t=tk.r*sin(jTime)+tk.g*sin(jTime+pyt)+tk.b*sin(jTime+2.*pyt)-.01*float(i);
          if (t>m){m=t; mv=tk;ic=i;}
      }
      //mix smeared with background depending ondistance
      float sc=float(ic)/30.;
      vec4 tk=texture2D(texture,xy);
      mv=sc*tk+(1.-sc)*mv;
      gl_FragColor = vec4(mv.rgb,1.0);
    }
  `
});

var textureLoader = new THREE.TextureLoader();
textureLoader.load("https://threejs.org/examples/textures/UV_Grid_Sm.jpg", tex => {
  planeMat.uniforms.texture.value = tex;
  planeMat.uniforms.texture.value.needsUpdate = true;
  iResolution.set(tex.image.width, tex.image.height);
  planeMat.needsUpdate = true;
  console.log(texture);
});

var plane = new THREE.Mesh(planeGeo, planeMat);
scene.add(plane);

var clock = new THREE.Clock();
var time = 0;

render();

function render() {
  requestAnimationFrame(render);
  time += clock.getDelta();
  planeMat.uniforms.iTime.value = time;
  renderer.render(scene, camera);
}
body {
  overflow: hidden;
  margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>