Godot Version
4.2.1.stable.mono
Question
I will preface this by saying I am completely new to shaders. I have been trying to implement a shader for my 2D player sprite that maps a color from a “skin texture” to the player sprite, based on the sprite’s RGBA value. This is based on Aarthificial’s popular youtube video, so check it out and credit to him:
Check his pinned comment for his more detailed explanation. He probably does a better job than me.
I have been trying to port his shader, which he made in Unity’s visual shader, to a .gdshader. I think I have made it most of the way to a functional shader, but I seem to have reached the end of my ability.
I hope to be fairly descriptive so that those more skilled with shaders can help me easier, but also so that people can potentially use this thread as a resource in the future. I’ll first share some screenshots with descriptions:
Here is an example of a frame from the player sprite. I used the color picker on his nose to show the RGBA values of that pixel, because I will use it as an example throughout this post. They are (10, 60, 0, 255). These will be used as UV coordinates for the “skin texture”.
Here is an example of a skin. I have added a selection box from the bottom left corner of the texture to the bottom left corner of the nose pixel. The box is 10 x 60, which are the ‘r’ and ‘g’ values of nose pixel on the player sprite. Based on my understanding, this is how UV coordinates work in Godot. From the bottom left.
These are the gdshader I have written, the inspector, and the output. As you can see, the output is an all black silhouette. The shader is pretty self explanatory, but I will at least describe the UV scaling.
Shader:
shader_type canvas_item;
uniform sampler2D Skin;
void fragment()
{
// collect RGBA of pixel from sprite texture
vec4 map = texture(TEXTURE, UV);
// scale the UV to the 64 x 64 "skin" sprite
vec2 scaledUV = (map.rg * 255.0 + 0.5) / 64.0;
// debug to check if any given scaleUV is being calculated correctly
//if (abs(scaledUV.r - 0.164) < 0.001 && abs(scaledUV.g - 0.945) < 0.001)
// COLOR = vec4(1.0, 0.0, 0.0, map.a);
// collect the rgba from the 'skin' sprite based on the scaledUV
vec4 color = texture(Skin, scaledUV);
// assign original sprite transparency
color.a = map.a;
// set new color
COLOR = color;
}
All the map.rg values will be in a range of 0 - 0.251, because my skin sprite is 64 x 64 and therefor the biggest possible RGBA pixel values would be (64, 64, 0, 255). Godot normalizes all the values to a scale of 0 - 1 (divide all numbers by 255), so the largest RGBA values possible on the player sprite would be (0.251, 0.251, 0, 1).
For the nose pixel example, the RGBA would be (0.039, 0.235, 0, 1). The ‘r’ and ‘g’ values are each multiplied by 255 to get them back up to 10 and 60. 0.5 is added, so we deal with the middle of the pixel rather than its edge. And then each value is divide by the length and width of the skin texture, in my case 64 for each.
The result is that the ‘r’ value for the nose pixel is equal to 0.164 (~10.5/64) and the ‘g’ value for the nose pixel is 0.945 (~60.5/64).
I have confirmed that these numbers are in fact being calculated, as shown by the nose turning red when I run this debug version of the script, so I assume my issue is to do with a step following the scaledUV calculation.
My best guess is that I am misunderstanding the following line:
vec4 color = texture(Skin, scaledUV)
I would assume that it would return the RGBA value of a pixel from the skin at the scaledUV coordinate, but as I said I am new to shaders.
Any insight would be greatly appreciated and let me know if I can provide any additional details.
Thanks!
stion here! Try to give as many details as possible →