Making NodeMaterials work with envMap

I am trying to convert a MeshStandardMaterial to a MeshStandardNodeMaterial. I have loaded an envMap using THREE.CubeTextureLoader and saved the result as scene.background = envMap.

Here is the original material:

Mat = new THREE.MeshStandardMaterial({
	color: difCol,
	metalness: 1.0,
	roughness: 0.7,
	normalMap: nrmMap,
	normalScale: new THREE.Vector2(2.5,2.5),
	envMap: envMap,
	envMapIntensity: 4,	
	premultipliedAlpha: true,					
});

Here is the NodeTexture, so far:

Mat = new MeshStandardNodeMaterial({
	colorNode: color(difCol),
	metalnessNode: float(1.0),
	roughnessNode: float(0.7),
	normalNode: normalMap(texture(nrmMap),vec2(2.5,2.5)),
});	

Is there a Node equivalent for the envMap and envMapIntensity properties?

This example, indicates that you can make the material reflect the envMap by adding scene.environment = envMap;

However, it would seem that there should be a NodeMaterial definition - otherwise all materials would reflect. Also the resulting texture is darker, likely because something like the envMapIntensity is needed to make the lighter environment color mix more with the darker material color.

ALMOST THERE (30 Mar 2024)

A method for creating a Skybox reflection was apparently in the example program:
webgl_nodes_materials_instance_uniform

This program contains a little Class-type extender called InstanceUniformNode, which you should add to your program.

Before defining the material, add these lines:

	let instanceUniform = nodeObject(new InstanceUniformNode());
	let cubeTextureNode = cubeTexture(cubeMap);

where cubeMap is the address of your Skybox

Within your material definitions, add this line:

	colorNode: instanceUniform.add(cubeTextureNode),

Finally, after using the material to create your mesh, add:

mesh.color = new THREE.Color(color);

(This last step is important since that is the color that is mixed with the reflection.)

The example program also contains material definitions for the emissiveNode, but these are apparently not necessary and might prevent your material from reflecting lights.

Another option is to simply ignore all of the above and use this colorNode definition:

	colorNode: cubeTexture(cubeMap),

However, with this method, you can’t change the base color of the material.

The only remaining problem is that, while the InstanceUniformNode solution creates a mirror reflection of the Skybox, I cannot figure out how to mix the base color (or a colored texture) with the reflection. There is apparently no Node-equivalent command for envMapIntensity.

[Edit} Or perhaps there is a command that allows you to control the “mix” of the reflection and the color or texture. The color definition indicates that it is adding the reflection to the color.

EDIT - AN UNEXPECTED TWIST (31 Mar 2024)

In searching for something that would allow me to mix colors, I ran across this example program:
webgpu_cubemap_dynamic.html

Although it is a webgpu program, it provided some interesting information about nodes and shows how to change the intensity of the reflection. However, in doing so, it took me right back to where I started by showing that there is an envMap node and an envMapIntensity node - which took me back to where I had started.

One of the material definitions was:

	const material2 = new Nodes.MeshStandardNodeMaterial( {
		map: uvTexture,
		roughness: 0.1,
		metalness: 0,
		envMap: texture,
	} );

where uvTexture is a texture and texture is the cubeMap texture.

This was a bit surprising in that it did not use the same variable definitions, such as float and texture(). Nor are there references to Nodes, such as roughnessNode, metalnessNode and envMapNode. Through trial and error, I was able to get the following combination of commands to work:

	Mat = new MeshStandardNodeMaterial( {
		colorNode: color(color2),
		map: grd_.Dif,				// Diffuse map
		metalness: 0.5,
		roughness: 0.1,
		roughnessMap: grd_.Ruf,	// Roughness map
		normalMap: wav_.Nrm,		// Animated normal map
		positionNode: positionLocal.add(texture(wav_.Dsp)),	// Animated displacement map
		envMap: cubeMap,
		envMapIntensity: 1,
	} );

Note that this uses normalMap, rather than normalNode. And for some reason, the envMap and envMapIntensity which did not seem to work before are now working fine. It uses different inputs than my original version, but I think the result is looks better than the original.

One problem is that there are visible seams between adjoining displacement maps, but that is a problem for another post.

I realize that this has turned into a long post, but it does show a couple of ways to add cubeMap reflections to your texture, so I will leave it as is until I find out which answer is best.

MeshStandardNodeMaterial extends NodeMaterial, and it seems to have an envNode property.

There is also an EnvironmentNode Class, and it also seems to have the envMapIntensity property:

Okay, I tried several variations, and I don’t get an error message when I insert:

envNode: texture(envMap),

But I can’t tell if it is having any effect.

I also don’t get an error message when I insert:

envMapIntensity: float(4.0)

But it turns everything black.

All other variations give me a notice that it “is not a property of THREE.MeshStandardNodeMaterial”

Since “envMapIntensity” did show an error, I tried some compound commands, like:

envMapIntensity: intensity(float(4.0), texture(envMap))

But they came back with a notice that, e.g. intensity is not defined.

1 Like

You may have noticed, i’m using the word “seems”. Without a clear documentation, I can only make assumptions. But from what I see, the envNode, takes a texture or a cubeTexture, so it should be working?

On the other hand, I can’t find any clear indication on how to set the Intensity, and it looks like an internal variable inside the EnvironmentNode constructor, without any exposed accessor or handler. Then there is this comment // @TODO: Add materialEnvIntensity in MaterialNode, which makes me assume it’s not implemented yet?

Understood. We are all using whatever examples we can find to try to transition to nodes.

BTW - I finally figured out how to add a displacement map to my flat plane. It was quite simple actually:

positionNode: positionLocal.add(texture(dspMap)),

This is really useful because, unlike most displacement maps which only act along one-dimension, my program was creating a displacement map in three dimensions (aka a “vector displacement map”). In my old program, I had to modify the standard Material shader with a custom “onBeforeCompile” snippet. Now, apparently, all I need is this one line to generate movement in all 3 directions. (I am going to test it further to make sure that I don’t need to swap or reverse values, but it seems to be working.)

If I can get the envMap working correctly, I can finally replace 100% of my old Materials with node Materials.

1 Like

I give it shot, also tried multiple combinations, just to get to your conclusion, its not working. (It is there, I can see it, I can feel it, but I can’t get it to work :face_holding_back_tears:).

Also went to the three.js - playground, and it’s not included either.

Thanks for letting me know about the playground - I had either forgotten or was unaware of that webpage.

I don’t see anything on the r161 milestones referring to the environment map. I’m sure they are planning on adding that feature, if they haven’t already.

Something to look forward to.

1 Like

A couple of the example programs now show how to use the envMap (cubeMap), including the r163 version of webgpu_cubemap_dynamic.html. I have added to my original post to discuss what I found.

1 Like

That’s good new!

Node materials are becoming the new standard, we’ll have to adapt.

1 Like

Although the settings are entirely different from my non-nodes version, I think there is a vast improvement in the appearance of my animated Ocean. Here is the old version. Here is the new version.

This is especially apparent if you dive down to around 100 feet (using the arrow keys). It self-centers so if you get fairly level it will level itself out. If you cruise above the waves and look around the old version looks almost cartoonish and does not appear to have a lot of detail. The nodes version seems to have a lot more detail even though the grid size and segments are the same. There is more reflection than I like and there is a seam in front. I hope that I can fix those problems without sacrificing the improvements.

1 Like