Last active
September 9, 2024 09:49
-
-
Save BenMcLean/9327690b93690b8a92a921df003f7954 to your computer and use it in GitHub Desktop.
Draw a procedurally-generated palette-indexed texture using a shader in Godot.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Uses a byte retrieval function to treat a texture like a byte array | |
shader_type canvas_item; | |
render_mode blend_disabled; | |
uniform vec4[256] u_palette; | |
uniform sampler2D u_texture : filter_nearest; | |
uint byte(uint index, uint width){ | |
//uint width = uint(textureSize(u_texture, 0).x); | |
uint index4th = index >> uint(2); | |
return uint(texelFetch(u_texture, ivec2(int(index4th % width), int(index4th / width)), 0)[index % uint(4)] * 255.); | |
} | |
void fragment(){ | |
uvec2 texture_size = uvec2(textureSize(u_texture, 0)), | |
real_texture_size = uvec2(texture_size.x << uint(2), texture_size.y), | |
position = uvec2(UV * vec2(real_texture_size)); | |
COLOR = u_palette[byte(uint(position.y * real_texture_size.x + position.x), texture_size.x)]; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public partial class ShadedColorRect : ColorRect | |
{ | |
public const string GodotShaderCode = @" | |
shader_type canvas_item; | |
render_mode blend_disabled; | |
uniform vec4[7] u_palette; | |
uniform sampler2D u_texture : filter_nearest; | |
void fragment(){ | |
vec2 texture_size = vec2(textureSize(u_texture, 0)); | |
//COLOR = texture(u_texture, (floor(UV * texture_size) + .5) / texture_size); | |
COLOR = u_palette[int(texture(u_texture, | |
(floor(UV * texture_size) + .5) / texture_size | |
)[ | |
int(UV.x * texture_size.x * 4.) % 4 | |
] * 255.)]; | |
} | |
"; | |
public static readonly ShaderMaterial ShaderMaterial = new ShaderMaterial | |
{ | |
Shader = new Shader { Code = GodotShaderCode, }, | |
}; | |
public static readonly ReadOnlyCollection<uint> Rainbow = Array.AsReadOnly(new uint[7] | |
{//Just a color test, not a political statement. | |
0xFF0000FFu,//Red | |
0xFFA500FFu,//Orange | |
0xFFFF00FFu,//Yellow | |
0x00FF00FFu,//Green | |
0x0000FFFFu,//Blue | |
0x4B0082FFu,//Indigo | |
0x8F00FFFFu,//Violet | |
}); | |
public static byte[] RainbowTexture(ushort width, ushort height = 0) | |
{ | |
if (height < 1) | |
height = width; | |
byte[] result = new byte[width * height]; | |
ushort stripe = (ushort)(width / 7); | |
byte color = 0; | |
for (ushort start = 0; start < width && color < 7; start += stripe, color++) | |
for (ushort x = 0; x < stripe && start + x < width; x++) | |
result[start + x] = color; | |
for (ushort x = (ushort)(stripe * 7); x < width; x++) | |
result[x] = 6; | |
for (int start = width, y = 1; start < result.Length - 1; start += width, y++) | |
Array.Copy( | |
sourceArray: result, | |
sourceIndex: 0, | |
destinationArray: result, | |
destinationIndex: start + y, | |
length: width - y); | |
return result; | |
} | |
public override void _Ready() | |
{ | |
TextureFilter = TextureFilterEnum.Nearest; | |
ushort width = 256, height = 256; | |
Size = new Vector2(x: width, y: height); | |
Material = ShaderMaterial; | |
ShaderMaterial.SetShaderParameter("u_palette", Rainbow.Select(color => new Godot.Color(color)).ToArray()); | |
ShaderMaterial.SetShaderParameter("u_texture", Godot.ImageTexture.CreateFromImage(Godot.Image.CreateFromData( | |
width: width >> 2, | |
height: height, | |
useMipmaps: false, | |
format: Godot.Image.Format.Rgba8, | |
data: RainbowTexture(width, height)))); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment