Three.js. MeshPhongMaterial has a uniform called diffuse. Why is it is not exposed nor can it be set? - three.js

In Three.js I am looking closely at the glsl source for MeshPhongMaterial. I notice the fragment shader relies on a uniform named diffuse. However there does not appear to be any way to set it. There is also an uniform named color that can be set and does indeed alter what looks like the diffuse color. Huh?
Is color copied to diffuse under the hood somehow? Can someone untangle this mystery for me.

If you look or search though the source you'll find it's set based on the color of the material
For example here

MeshPhongMaterial is not a subclass of ShaderMaterial even though they both extend Material.
Internally WebGLRenderer keeps track of / knows how to deal with two different things:
Any ShaderMaterial with any uniform. This means that you can make an exact copy of MeshPhongMaterial using ShaderMaterial and instead of myMaterial.color you'd have myMaterial.uniforms.color.
"Built-in" materials and their properties. For these, WebGLRenderer is aware of all the possible properties these materials expose. Gman has pointed out where in the code this happens (one of the places). For each material WebGLRenderer internally creates a ShaderMaterial but instead of handling it's uniforms like it would with any other such material, it manually keeps track and updates everything.
The built-in materials make little sense, since you can easily pipe foo.bar to foo.uniforms.bar using getters and setters leaving the renderer to only deal with shader materials.
If you really really want a uniform created for a built in material, you have to wait until the renderer creates them, and you can tap into this process with material.onBeforeCompile = shader=>{} (the shader object will contain the uniforms).

Related

Ambient occlusion not showing in three.js

I'm using the MeshStandardMaterial in three.js and when I create and apply the material, all maps work fine except for the aoMap, which has no effect on the model. I suspect this is because I don't have a 2nd set of UVs (my UV unwrapping is done through Blender and I don't manually apply any UV at all in three.js), as the documentation says:
The red channel of this texture is used as the ambient occlusion map.
Default is null. The aoMap requires a second set of UVs, and
consequently will ignore the repeat and offset Texture properties.
I've tried using the below code to solve this:
var geometry = mesh.geometry;
geometry.addAttribute( 'uv2', new THREE.BufferAttribute( geometry.attributes.uv.array, 2 ) );
but had no luck. How do I copy my UV map to the uv2 property, or wherever it is needed to make ambient occlusion work?
An aoMap is an ambient occlusion map, and like its name says, it occludes ambient light. That is all it occludes.
There are currently three sources of ambient (or indirect) light in three.js: AmbientLight, HemiSphereLight, and LightMap.
So the aoMap occludes those three sources. It does not occlude direct light sources. Direct light sources include DirectionalLight, SpotLight, PointLight, and AreaLight.
three.js r.95
What kind of lighting are you using? I recreated your situation, and it works as expected. The things to look out for is that aoMap shows up with THREE.AmbientLight, but not with THREE.Spotlight. It also works if you use an envMap on your MeshStandardMaterial

Is there a way to make a MeshPhongMaterial ignore all lights

I am using ThreeJS r67
I have a static room with lighting baked so lights do not need to affect it, but I also have a person in the room that does need the lighting. Is there a way to set all the materials in the room to ignore the lights?
According to the ColladaLoader.js (in three.js r67) lines 3550-3572 you can specify in the collada model file what type of material to create. You are probably using blinn or phong and you need to change that to constant.

Why do I need to update uvs?

I'm working with the three.js editor where I parse an object from JSON format. As usual it first parses the materials and geometries, then I create meshes from it. While parsing materials I also load textures. The issue now is that I have to call...
object.geometry.uvsNeedUpdate = true;
object.geometry.buffersNeedUpdate = true;
... after the image for the texture completely loaded - but why?! The geometry never changed before, neither did its uvs or anything like it. It's still the plain old geometry, yet I always get a GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 2 when trying to render. It only works with this "hack" although the geometry is always the same.
In my opinion it should also work perfectly when I update the uvs after object creation (or not at all). I didn't find anything in the three.js editor code that would update the geometry or its faceVertexUvs.
I know it's a bit of an abstract problem, I'm mainly looking for some hints or insights why this hack might be necessary.
Thanks!
Three.js "guesses" whether uvs are needed according to your used textures in bufferGuessUVType. If you want to preallocate uv buffers you can either init a map attribute with an empty THREE.Texture, update the geometry after the map was assigned, etc.

lightMap / specularMap / shading with meshBasicMaterial

I'm currently working on something along the lines of a plugin for another program to add 3D capability to it, so I'm trying to put all the functionality i can from three.js into it, with the added goal of this being a good way to learn all the functionality of three.js firsthand.
I'm running into an issue now as i implement textures and materials that with mesh basic material, setting some things which the documentation on the main threejs.org site shows are features, doesn't actually do anything.
when i set a texture for either specularmap or lightmap nothing is actually showing up. Im pretty sure its not a mistake im making because setting the texture of the map works, but trying to set this same texture for the specularMap or lightMap is doing nothing. Does a regular texture work for these, or do i have to do something different?
I'd also like to know what the shading property does for mesh basic, because as far as i can see setting it to smoothshading/flatshading/noshading is doing nothing aswell.
MeshBasicMaterial does not respond to lights. Change your material to MeshLamberMaterial or MeshPhongMaterial, for example.
For MeshBasicMaterial and MeshLambertMaterial, the specularMap is used only to modulate the reflection when an environment map is used.
For any material, lightmaps require a second set of UVs. geometry.faceVertexUvs[ 0 ] contains the usual set of UVs; for a lightmap, you need to add geometry.faceVertexUvs[ 1 ], a second set of UVs.
For MeshBasicMaterial, the shading property only applies when an environment map is used. SmoothShading will yield smooth reflections; FlatShading will yield faceted reflections.
three.js r.66

Erroneous bindTexture(TEXTURE_2D, null); call, or bad shader? Texture disapparing with three.ShaderMaterial

In two cases, I have a THREE.ShaderMaterial that doesn't doesn't correctly render an object, omitting its texture.
On both examples, the middle object is a basic THREE.MeshPhongMaterial
Example1: http://jsfiddle.net/sG9MP/4/ The object that's closest to the screen never shows.
On this one, it works with renderer.render(...) but not composer.render(...).
renderer.render( scene, camera );
//composer.render();
Example2: http://jsfiddle.net/sG9MP/5/ Here I'm trying to duplicate the MeshPhongMaterial shader as a base so I can modify it. I tried to replicate it perfectly. I copied the uniform, vert, frag, and replicated what's in the object. I can't see anything different, so I don't get why it's not working the same as the standard Three.js phong shader.
So it's two cases where I'm using THREE.ShaderMaterial and it's not rendering the shader correctly, and I can't figure out why. On the second example(which is the one where I really need fixed. The first was an old test), in the webGL inspector I see the scene often looks fine until there is a "bindTexture(TEXTURE_2D, null);" call that happens under the hood by three.js. Though sometimes it just draws without it. In the first example, it's always drawing without it.
I feel like I must be missing some sort of flag in the renderer, or composer, or something. Or in my second example, where I'm trying to copy the Three.js phong shader, maybe I didn't copy something perfectly.
The goal here is just to copy the phong shader, so I can modify the uniform, vert, and frag on it. Sadly, I can't simply .clone() it since the vert and frag can't be modified after it's compiled.
It looks like while ShaderMaterial.map was being set, ShaderMaterial.uniforms.map.value was not consistently set.
I really don't understand this, though. In some cases I had issues not setting things at the top level under ShaderMaterial. Other cases I have issues not setting uniforms.
In my material, I just went and added this:
for(var k in phongMat){
if( typeof phongMat.uniforms[k] != 'undefined' ){
phongMat.uniforms[k].value = phongMat[k];
}
}

Resources