Working with world points in shader

Godot Version

4.4

Question

I am currently working on a dithering shader that takes 2x2 or 4x4 chunks of a texture and applies bayer matrix dithering to them. Basically each chunk depending on the level of dithering will make certain pixels in it transparent. I want to be able to let users pass in a point, preferably a world point but a local point would also work, and based on the point apply a certain level of dithering for each chunk.

The problem I’m running into is that it’s kinda tough working with points and translating them into UV points in a texture. I’ve found that multiplying or diving by TEXTURE_PIXEL_SIZE or SCREEN_PIXEL_SIZE can convert them between UV positions between 0.0 and 1.0 to one based on pixel count but the issue I’m running into is how to figure out where a world point is on a texture in the shader code.

“chunk” is the size in pixels of the chunks, (2x2 chunks would use 2, 4x4 chunks use 4, etc.) and dither_point is the value passed in. Currently it’s set to take in a local position from the Node however the issue I ran into with that is that it will only work if the pivot of the node is in the upper left corner of the texture.

void fragment() {
	...
	// Calculates the center point of the chunk that the current fragment is in
	vec2 chunk_position = UV - mod(UV, TEXTURE_PIXEL_SIZE * float(chunk)) + (TEXTURE_PIXEL_SIZE * float(chunk) / 2.0);

	switch (dither_type) {
		case 0: // FLAT
			...
		 case 2: // POINT
			vec2 chunk_point = chunk_position / TEXTURE_PIXEL_SIZE;
			vec2 point_dif = chunk_point - dither_point;
			float length_from_point = sqrt(pow(point_dif.x, 2.0) + pow(point_dif.y, 2.0));
			COLOR.a = is_fragment_transparent(fragment_dither, clamp(length_from_point / dither_point_radius, 0.0, 1.0)) ? COLOR.a : 0.0;
			break;
	}
}

Fragment function happens in screen space, but the vertex function happens in local space. You can transform the vertex UV with the MODEL_MATRIX to get the UV in world space. Then use a varying variable to pass the world space position to the fragment function.

You can do the same thing from the fragment shader but it requires potentially two transformations to get to world space.

1 Like

Tried using MODEL_MATRIX but I’m not sure if I’m using it right? Is this how you would use it? This is how they use it in the docs but it doesn’t seem to work right for me, the point just doesn’t show up at all now. chunk_position and chunk_world_position are both varyings and chunk_position works but not chunk_world_position.

void vertex() {
	chunk_position = (UV - mod(UV, TEXTURE_PIXEL_SIZE * float(chunk))) + (float(chunk) * TEXTURE_PIXEL_SIZE) / 2.0;
	chunk_world_position = (MODEL_MATRIX * vec4(chunk_position, 0.0, 1.0)).xy;
}

Tooled around a little bit more and fixed it. Using the VERTEX instead of the UV was the way to go since VERTEX gives you the actual coords which is helpful.

void vertex() {
	chunk_point = (VERTEX - mod(VERTEX, float(chunk))) + float(chunk) / 2.0;
	chunk_world_point = (MODEL_MATRIX * vec4(chunk_point, 0.0, 1.0)).xy;
}