3
votes

Using Threejs (67) with a Webgl renderer, I can't seem to get a plane with a shader material to wear its texture. No matter what I do the material would just stay black.

My code at the moment looks quite basic :

var grassT = new Three.Texture(grass); // grass is an already loaded image.
grassT.wrapS = grassT.wrapT = Three.ClampToEdgeWrapping;
grassT.flipY = false;
grassT.minFilter = Three.NearestFilter;
grassT.magFilter = Three.NearestFilter;
grassT.needsUpdate = true;

var terrainUniforms = {
  grassTexture : { type: "t", value: grassT},
}

Then I just have this revelant part in the vertexShader :

vUv = uv;

And on the fragmentShader side :

gl_FragColor = texture2D(grassTexture, vUv);

This results in :

  • Black material.
  • No error in console.
  • gl_FragColor value is always (0.0, 0.0, 0.0, 1.0).

What I tryed / checked:

  • Everything works fine if I just apply custom plain colors.
  • All is ok if I use vertexColors with plain colors too.
  • My texture width / height is indeed a power of 2.
  • The image is on the same server than the code.
  • Tested others images with same result.
  • The image is actually loading in the browser debugger.
  • UVS for the mesh are corrects.
  • Played around with wrapT, wrapS, minFilter, magFilter
  • Adapted the mesh size so the texture has a 1:1 ratio.
  • Preloaded the image with requirejs image plugin and created the texture from THREE.Texture() instead of using THREE.ImageUtils();
  • Played around with needsUpdate : true;
  • Tryed to add defines['USE_MAP'] during material instanciation.
  • Tryed to add material.dynamic = true.
  • I have a correct rendering loop (interraction with terrain is working).

What I still wonder :

  • It's a multiplayer game using a custom port with express + socket.io. Am I hit by any Webgl security policy ?
  • I have no lights logic at the moment, is that a problem ?
  • Maybe the shader material needs other "defines" at instanciation ?

I guess I'm overlooking something simpler, this is why I'm asking... Thanks.

2
A bit hard to debug without all of your code - but I would definitely suggest adding an AmbientLight to your scene to check if the model actually does have the texture. I've found that you can often have one texture render ( i.e. grass ) and seem correct, only to realize that you've done it wrong and it's ignoring lighting. :)FunnyLookinHat
Thanks a lot for the suggestion. I checked with the light on and it's not better. I did some progress however getting my hands dirty into the renderer code. I found that the texture.needsUpdate property is set to false for some reason ! If I remove the conditional check at line 26278 -setTexture function- the texture is rendered.Julien Garcia
Therefore the new question is : Where and Why a needsUpdate property of a texture could be set from true to false.Julien Garcia

2 Answers

2
votes

I am applying various effects on the same shader. I have a custom API that merge all different effects uniforms simply by using Three.UniformsUtils.merge() However this function is calling the clone() method on the texture and this is causing to reset needsUpdate to false before the texture reach the renderer.

It appears that you should set your texture needsUpdate property to true when reaching the material level. On the texture level, if the uniform you set get merged, and therefore cloned, later in the process, it'll lose its needsUpdate property.

The issue is also detailled here: https://github.com/mrdoob/three.js/issues/3393

In my case the following wasn't working (grassT is my texture):

grassT.needsUpdate = true

while the following is running perfectly later on in the code:

material.uniforms.grassTexture.value.needsUpdate = true;
1
votes

Image loading is asynchronous. Most likely, you are rendering your scene before the texture image loads.

You must set the texture.needsUpdate flag to true after the image loads. three.js has a utility that will do that for you:

var texture = THREE.ImageUtils.loadTexture( "texture.jpg" );

Once rendered, the renderer sets the texture.needsUpdate flag back to false.

three.js r.68