I am having a bit of an issue with shaders. I am making a game with a large hex-tiled map. I am using a shader to determine the UV coordinates of each hex.

Basically I create a 'compute image' with the same dimensions as my map. If it is 50 by 50 hexes, the compute image will be 50 by 50 pixels. I then set each pixel's color to (x, y, 0, 0) where x and y are the center coordinates of the appropriate hex in the atlas.

I set each vertex color in the mesh to (r, g, b, a) where r and g are the coordinates of the hex to which this vertex belongs, (so the first hex would be (0,0), etc) and (b, a) is the offset of this vertex from the center of the hex.

I then use the following shader to read from the compute image each frame and set the fragment color accordingly:

	shader_type canvas_item;
	uniform sampler2D computeTexture; 
	uniform float atlasHeight;
	void vertex()
	{
	    vec2 computeTextureCoords = vec2(COLOR.r,COLOR.g);
	    vec4 samplePoint4 = texture(computeTexture, computeTextureCoords);
	    vec2 samplePoint = vec2(samplePoint4.r, samplePoint4.g);
	    
	    vec2 offset = vec2(COLOR.b, COLOR.a);
	    UV = vec2(samplePoint.x / atlasHeight, samplePoint.y / atlasHeight) + offset;
	}
	void fragment()
	{
	    COLOR = texture(TEXTURE, UV);
	}

The compute image can then be altered in real time to update the textures without rebuilding the whole mesh.

I have this whole system working well except for one crucial aspect. It goes without a hitch as long as all the hexes are set to the same texture, but when you have two different colors next to each other in the compute image, the sampling is essentially corrupted. The 'samplePoint4' returned in the shader above is an interpolation of the two different colors instead of the actual color of the pixel I am trying to access. This means that when different terrains are next to each other the graphic overlay is out of place. I've attached an image to hopefully illustrate better what this means.

This happens despite my efforts to sample the 'center' of the pixel rather than an edge by adding an appropriate offset to (r,g). I tried making the compute texture use 9 pixels in a square for each hex and then sampling the center pixel, but the issue is not resolved by this.

Is this an unavoidable limitation of the floating point precision in the shader, or is there a way to avoid this blending when sampling from the compute texture? Any help with this greatly appreciated

@tdqctdqc said: Is this an unavoidable limitation of the floating point precision in the shader, or is there a way to avoid this blending when sampling from the compute texture? Any help with this greatly appreciated

When you create your backing Image and/or the Texture for computeTexture, make sure you disable texture flag FLAG_FILTER. I.e., I've been finding that a flags of 0 is what I want for this sort of thing. With FLAG_FILTER unset, arbitrary samples from the texture will take the values of their nearest neighbor, which should solve your problem.

Alternatively/additionally, check out the texelFetch() shader function (link to OpenGL ES 3.0 GLSL docs, but is the same function in Godot's shader language). That will allow you to fetch an exact texel from an integer vector coordinate within a texture, giving you complete control over what you read.

HTH!

2 years later