Strange artifacts on stiches, when sampling from 2 textures

Started by
11 comments, last by denisve 8 years, 6 months ago

Hi, all.

I'm working on terrain editor, and the idea so far, is to have a set of textures I could place on each square with UV coords 1x1. I wrote a simple shader, which samples from 2 different textures, depending on UV coords, but for some reason, even though the textures should connect seamlessly, I'm having a strange artifact pixels on the seams. Now the really funny part, is that it works perfectly on a crappy Intel on-board video card, and doesn't work on my GTX500 with drivers up to date (!!!)

For instance, on an arbitrary hexagonal surface I sample everything, except for one square from grass texture, and one square from desert texture.

rsFhQDF.jpg

Magnified (Where are those pixels sampled from exactly? o.O)

ssMy9Gg.png

Vertex shader code:


precision highp float;
precision highp int;

uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
attribute vec3 position;
attribute vec2 uv;
varying vec2 vUv

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

Fragment shader code:


precision highp float;
precision highp int;

uniform vec3 diffuse
uniform float opacity;
varying vec2 vUv;
uniform sampler2D map;
uniform sampler2D map1;

void main()
{
	gl_FragColor = vec4( diffuse, opacity );

	vec4 texelColor;
	if(vUv.x >= 0.0 && vUv.x < 1.0 && vUv.y >=0.0 && vUv.y < 1.0) // <- Sample from a different texture for that square
		texelColor = texture2D( map1, vUv );
	else
		texelColor = texture2D( map, vUv );

	gl_FragColor = gl_FragColor * texelColor;
}

JSFiddle link (sorry for a big chunk of code, embedded textures take some space). The code is first 100 lines, everything below is THREE.js code to render the scene (Scroll wheel: dolly in/out, left click: rotate, right click: pan) -> https://jsfiddle.net/denisve/aucxz90s/

What am I doing wrong? The code is pretty straight forward, just sample from a different texture depending on UV.

Thanks in advance,

Denis.

Advertisement

could you show us what do you expect it to be, actually i don't understand your problem at all.

and explain it a little bit further coz:

and the idea so far, is to have a set of textures I could place on each square with UV coords 1x1.

doesnt say to me anything.

I expect it to be as it is now, just without the artifacts on the seams.

The idea is very simple: I have a UV mapped mesh, I have a set of textures. Now depending on UV coords I sample from different textures, each textures covers UV square of size 1 x 1.

See the image below, hope it explains better. Yellow lines denote faces, red grid is the UV grid, each square is 1 x 1. What I want, is for each square on the grid, the shader would sample from a different texture. See fragment shader code in OP.

ztoAesm.jpg

I expect it to be as it is now, just without the artifacts on the seams.

Were are these artifacts?

now to that second image it seems that you store even 3 faces on 1x1 square

do you have artifacts on these yellow lines? i assume those black tiny squares (black grid) are screen pixels?

its hard for me to understand what is what 1x1 square doesnt say to me anything. but i assume if you have artifaces near yellow lines then you will have to sample all textures that are near the pixel line and maybe i dont know divide that by the amount of textures sampled for pixel.

(rgb1 + rgb2 + rgb3 )/ 3

or maybe you dont use squared textures since its not es3 (opengl ES is strict for that and you always should use textures like: 2x2 4x4 8x8 16x16 32x32 64x64, not 32x16 or 16x32 <- those are not valid for es 2.0 context), maybe you set clamping or repeating and shader leprs between last and first texel of texture that results in wrong color so maybe setting texture filtering to gl_nearest could somehow fix the problem but then you will get pixeloze)

im waiting for more info

each textures covers UV square of size 1 x 1.

You mean in that image you have more than 8*5 = 40 ? textures?

explain please how you think the algorithm should work.

Or maybe do you just put a set of faces on a grid and make their vertices to be UV'ed accordingly to grid position?

so then it may be the problem with lerping between two colors from one texture and not taking into account the second texture (you could also show textures)

Do your textures have mipmaps? Try turning those off and see if it makes a difference. The mip level selected during a sample will depend on the texture coordinate gradient between adjacent pixels. you have conditional logic to sample from one texture or the other, so weird things might happen at that "boundary"
Try restructuring your shader to look like this:

void main()
{
	gl_FragColor = vec4( diffuse, opacity );
	vec4 a = texture2D( map1, vUv );
	vec4 b = texture2D( map, vUv );

	vec4 texelColor;
	if(vUv.x >= 0.0 && vUv.x < 1.0 && vUv.y >=0.0 && vUv.y < 1.0) // <- Sample from a different texture for that square
		texelColor = a;
	else
		texelColor = b;

	gl_FragColor = gl_FragColor * texelColor;
}
Calling texture2D inside a non-uniform if block can result in undefined behaviour, so you should hoist them out of the if/else and do them all the time. Then you can select which result you want.

Were are these artifacts?

See the images I linked above in my first post. The "desert" square has a border of weird pixels around it, you can also see it on the magnified image.

And yes, I'm planning to have 40 different textures, or even more. This was just an example, so I've passed 2 uniforms, the real implementation is going to be one big texture atlas and I'm going to sample from it directly.

Do your textures have mipmaps? Try turning those off and see if it makes a difference. The mip level selected during a sample will depend on the texture coordinate gradient between adjacent pixels. you have conditional logic to sample from one texture or the other, so weird things might happen at that "boundary"

Mipmaps is an issue I'll have to solve separately, since I'm going to use atlas texture, so I'll have to implement something to avoid pixel bleeding. I'm not there yet, though, far from it. Just trying to basic code right now, to see it it will work at all.

Try restructuring your shader to look like this:


void main()
{
	gl_FragColor = vec4( diffuse, opacity );
	vec4 a = texture2D( map1, vUv );
	vec4 b = texture2D( map, vUv );

	vec4 texelColor;
	if(vUv.x >= 0.0 && vUv.x < 1.0 && vUv.y >=0.0 && vUv.y < 1.0) // <- Sample from a different texture for that square
		texelColor = a;
	else
		texelColor = b;

	gl_FragColor = gl_FragColor * texelColor;
}
Calling texture2D inside a non-uniform if block can result in undefined behaviour, so you should hoist them out of the if/else and do them all the time. Then you can select which result you want.

That actually solved it! Lots of thanks! Could you perhaps link me to an article that explains why sampling textures shouldn't be done inside if? Also what other functionality should be avoided?


Mipmaps is an issue I'll have to solve separately, since I'm going to use atlas texture, so I'll have to implement something to avoid pixel bleeding. I'm not there yet, though, far from it. Just trying to basic code right now, to see it it will work at all.

But the artifact you're seeing is exactly what you'd see where there is a discontinuity in texture sampling coordinates and it samples from the wrong mip level. The fix being what Xycaleth posted.

The shader needs information from adjacent pixels in order to calculate the mip level. So if one of those adjacent pixels has undefined texture coordinates (or say a sharp discontinuity if you are sampling from a different part of a texture atlas), then your mip level will be wrong.

This topic is closed to new replies.

Advertisement