2D Specular Maps

Since Godot doesn’t support specular maps in 2D natively like normals and I couldn’t find a solution anywhere, I decided to make this.

Advice for improvements is welcome, I just learned shaders and this is my first one.

Godot’s shader language doesn’t support adding any new textures, so I came up with the idea to put the specular map on the same sprite that is already loaded in.  From there you can crop the specular map out with the fragment() funtion, and from the light() function you can have access to the light colors, the texture colors, and the specular map colors.

I don’t know anything about building a proper specularity system and the solution I arrived at was in large part trial and error. I hope someone out there can improve the math, but the basic concept is there.  Lighter values on the specular map reflect more light.

HOW TO SETUP

Since this code pulls the specular maps directly from the image, you will need to make sure you alter your images to double their height, and have a mirrored image of the specular map directly above the original.  You will need to do the same for your normal maps, should you choose to use them(recommended), however for normals you dont need to create a specular map, just mirror the normal directly above itself.  Keep in mind when I say mirror, I mean the images need to be literally flipped over.  It’s also important to note that any transparency in the original image needs to be maintained in the specular map.

I’ve included reference photos for both kinds of file, aswell as a github link to the Godot project displayed in the screenshots.

From there it is largely standard.  Disable the Filter option in the Import tab to prevent the specular map from bleeding onto your sprite, and then add it to a node(I’ve tested sprite and tilemap), and finally apply the shader.

This effect is improved in my opinion by the use of glow.  Add a World Environment node and under Background set the Mode to Canvas, then under Glow check Enabled.

You can change the intesity value in the shader code(which refers to the intensity of the reflections) to your liking.

 

Shader code
shader_type canvas_item;

uniform vec4 intensity: hint_color = vec4(1000.0,1000.0,1000.0,1000.0);

void light()
{
	if (UV.y > 0.5) {
		vec4 specular_map = texture(TEXTURE,vec2(UV.x,1.0-UV.y));
		float specularity = (((specular_map.r + specular_map.g + specular_map.b) /3.0) /255.0);
		vec4 tex = texture(TEXTURE,UV);
		vec4 color = mix(tex,LIGHT,specularity);
		float alpha = LIGHT.a;
		vec3 reflection = mix(color.rgb,intensity.rgb,specularity);
		LIGHT.rgb = reflection.rgb;
	}
}

void fragment()
{
	if (UV.y < 0.5) {
		COLOR.a = 0.0;
	}
	if (UV.y > 0.5) {
		vec4 tex = texture(TEXTURE,UV);
		COLOR.rgb = tex.rgb;
		COLOR.a = tex.a;
	}
}
Tags
2d, lighting, Map, Normal, Specular, Specularity
The shader code and all code snippets in this post are under GNU GPL v.3 license and can be used freely. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

Related shaders

Extensible Color Palette (Gradient Maps) Now with Palette Blending!

Subscribe
Notify of
guest

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
zhyrin
zhyrin
1 year ago

With a sampler2D type uniform you could pass an image into the shader, so you don’t need to store the data in the image 🙂

Calinou
1 year ago

> Since Godot doesn’t support specular maps in 2D natively like normals and I couldn’t find a solution anywhere, I decided to make this.

Note that this comment only applies to Godot 3.x. Godot 4.0 has native support for specular maps in 2D lighting (using the CanvasTexture resource), with adjustable specular exponent.