Why is There a Visible Seam with my Nodes PositionNode?

I have converted my Ocean simulation to use node materials. I think it is a vast improvement.

However, an important part of the simulation is an animated displacement map which I use for each grid and which should tile perfectly with adjoining grids. In my non-nodes version, there is no apparent seam between these grids. In the nodes version, there is a visible seam. Anyone know how to fix that?

Here is how the non-nodes version appears. Here is the nodes version. As you can see there is a visible seam straight ahead and straight behind.

The material definition I use for the non-node material is this:

	grd_.Mt0[idx] = new MeshStandardMaterial({
		color: grd_.Col,
		map: grd_.Df0[idx],
		metalness: 1.0,			// 1 for max reflection
		roughness: 0.7,			// 0 for max reflection
		roughnessMap: grd_.Rf0[idx],
		normalMap: grd_.Nrm,	// Animated normalMap
		normalScale: new Vector2(4,4),
		envMap: scene.background,			
		envMapIntensity: 4,		// max reflection suggested = 5
		premultipliedAlpha: true,
		onBeforeCompile: shader => {
			shader.uniforms.dmap = {value: grd_.Dsp};	// Displacement Map
			shader.vertexShader = `
				uniform sampler2D dmap;
				${shader.vertexShader}
			`
			.replace(
				`#include <begin_vertex>`,
				`#include <begin_vertex>
					transformed += vec3(1.0,1.0,1.0)*(texture(dmap, uv).rgb * 1.0 + 0.0);
				`
			);
		}	
	});

I have to use the onBeforeCompile section because the displacement map is a 3-dimensional map.

The material definition I use for the nodes version is this:

	grd_.Mt0[idx] = new MeshStandardNodeMaterial({
		colorNode: color(grd_.Col),
		map: grd_.Df0[idx],
		metalness: 0.5,			// 1 for max reflection
		roughness: 0.5,			// 0 for max reflection
		roughnessMap: grd_.Rf0[idx],
		normalMap: grd_.Nrm,	// Animated normalMap
		positionNode: positionLocal.add(texture(grd_.Dsp)),  // Displacement Map
		envMap: scene.background,			
		envMapIntensity: 0.5,
	});

FURTHER THOUGHTS
Having posted this, I am now wondering if the problem has to do with the old problem of trying to create Normal maps where, at the edges, you are trying to access displacement values that are “off the map”. I am using a shader to compute the normal values. But, because this the displacement map generates values in three dimensions (rather than just height), the shader has to use 5 points of reference, rather than 3. (At least, that is my understanding of what is happening.)

Consequently, at the edges, the shader is attempting to access values that are off the map. The good news is that, because the displacement values repeat, I should be able to use the values from the first (or last) row or column. I will try to work this out. But in the meantime, I wonder if there is a standard solution to this problem. Here the the shader code that I am using:

//= 5. Fragment Shader - Normal Map
// Note: The original program did not generate a "plug and play" Normal Map .
// Values had to be switched (at the bottom)
let ocean_normals = `
	precision highp float;		// GLSL3
	precision highp int;
	precision highp sampler2D;
	in vec2 vUv;
	out vec4 outColor;
	uniform float u_grdres;  // Segments in plane (256)
	uniform float u_grdsiz;  // Size of grid (1600)
	uniform sampler2D u_displacementMap;
	void main (void) {
		float texel = 1.0/u_grdres;
		float texelSize = u_grdsiz/u_grdres;
		vec3 ctr = texture(u_displacementMap, vUv).rgb;
		vec3 rgt = vec3(texelSize, 0.0, 0.0) + texture(u_displacementMap, vUv + vec2(texel, 0.0)).rgb - ctr;
		vec3 lft = vec3(-texelSize, 0.0, 0.0) + texture(u_displacementMap, vUv + vec2(-texel, 0.0)).rgb - ctr;
		vec3 top = vec3(0.0, 0.0, -texelSize) + texture(u_displacementMap, vUv + vec2(0.0, -texel)).rgb - ctr;
		vec3 bot = vec3(0.0, 0.0, texelSize) + texture(u_displacementMap, vUv + vec2(0.0, texel)).rgb - ctr;
		vec3 topRgt = cross(rgt, top);
		vec3 topLft = cross(top, lft);
		vec3 botLft = cross(lft, bot);
		vec3 botRgt = cross(bot, rgt);
		vec3 nrm3 = vec3(normalize(topRgt + topLft + botLft + botRgt));
		vec3 tmp2 = nrm3;
		nrm3.b = tmp2.g;	// Switch b with g
		nrm3.g = tmp2.b;	// Siwtch g with b
		outColor = vec4(nrm3*0.5+0.5, 1.0);
	}
`;

Do I have to add some special handling if vUv.x or vUv.y => u_grdres? Or can I use some kind of modulo to force the index to wrap around? Or does it automatically wrap (to prevent errors)?

I set up a GitHub repository. Will that work?

I was hoping to put stuff on there so I load it to my files without violating the CORS policy, just like I can do with three.js files. But, for some reason that doesn’t work - even though it is a separate website.

But can I put program files on there that you can run? I just tried loading the program here and all you get is a listing of the code.

Thanks. I will give that a try.
Is there an easy way to create directories and sub-directories so I can organize things?

It seems that most people set up their GitHub pages so that programmers using one of the assemblers can use them. I don’t need that, as I merely want to share relatively short demo programs, modules, textures and models with people. Hopefully, GitHub lends itself to that kind of sharing.

disregard my instructions. One day, I may just set it all up for you.

I may have found the solution.

While the results were nice-looking, the command I used to define the Normal Map was apparently incorrect. But when I used the correct command, the results were “lumpy”. After some trial and error, it appears that the y-value of the Normal Map Scale had to be flipped.

The normal map shader in the original Ocean Wave Generator was not designed for use with standard three.js materials, so I had made various modifications to the shader. Apparently, I had overlooked the need to also make the above change.

To recap, the main program now includes this definition:

	let normalMapScale = vec2(1.0,-1.0);

And the material definition now includes this command:

	normalNode: normalMap(texture(grd_.Nrm),normalMapScale),

I have no idea if this is the correct solution, but it seems to solved my problems…

1 Like