3D Sprite Edge Lighting

Creates a rim light effect around sprites based on world lights without the use of a normal map.

It supports billboards and sprite sheets.

 

This is a shader that I made for my game to make the sprites stand out a bit more. I use a lot of shaders from this website, so I’m trying to give back to the community with this.

Shader code
shader_type spatial;
render_mode cull_back, depth_prepass_alpha;

uniform sampler2D sprite_texture : source_color, filter_nearest;
uniform vec2 atlas_size;
uniform float highlight_amount;
instance uniform bool always_on_top;
instance uniform vec2 offset;
instance uniform vec3 damage_color : source_color = vec3(0.0f);
instance uniform int billboard : hint_enum("Disabled", "Enabled", "Y-Billboard");
instance uniform bool flip;

void vertex()
{
	// Makes the sprite a billboard if true
	if (billboard == 1)
	{
		MODELVIEW_MATRIX = VIEW_MATRIX * mat4(INV_VIEW_MATRIX[0], INV_VIEW_MATRIX[1], INV_VIEW_MATRIX[2], MODEL_MATRIX[3]);
	}
	else if (billboard == 2)
	{
		MODELVIEW_MATRIX = VIEW_MATRIX * mat4(vec4(normalize(cross(vec3(0.0, 1.0, 0.0), INV_VIEW_MATRIX[2].xyz)), 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(normalize(cross(INV_VIEW_MATRIX[0].xyz, vec3(0.0, 1.0, 0.0))), 0.0), MODEL_MATRIX[3]);
	}
}

void fragment()
{
	// Depth test
	if (always_on_top)
	{
		DEPTH = 1.0f;
	}
	else
	{
		DEPTH = FRAGCOORD.z;
	}

	// Damage color and albedo color
	vec2 offset_uv;
	if (flip)
	{
		offset_uv = (vec2(-UV.x, UV.y) / atlas_size) + ((vec2(1.0f, 0.0f) + offset) / atlas_size);
	}
	else
	{
		offset_uv = (UV / atlas_size) + (offset / atlas_size);
	}

	vec4 color = texture(sprite_texture, offset_uv);
	ALBEDO = vec3(max(color.r, damage_color.r), max(color.g, damage_color.g), max(color.b, damage_color.b));
	ALPHA = color.a;
}

void light()
{
	// Damage effect light
	float damage_light = length(damage_color);
	DIFFUSE_LIGHT += damage_light;

	// Standard lighting
	if (billboard == 1)
	{
		DIFFUSE_LIGHT += clamp(dot(vec3(0.0f,0.0f,1.0f), LIGHT), 0.0f, 1.0f) * ATTENUATION * LIGHT_COLOR / PI;
	}
	else if (billboard == 2)
	{
		DIFFUSE_LIGHT += ATTENUATION * LIGHT_COLOR / 10.0f;
	}
	else
	{
		DIFFUSE_LIGHT += clamp(dot(NORMAL, LIGHT), 0.0f, 1.0f) * ATTENUATION * LIGHT_COLOR / PI;
	}

	// Rim lighting
	vec3 camera_vector = normalize(-VIEW_MATRIX[2].xyz);
	vec3 light_vector = normalize((INV_VIEW_MATRIX * vec4(LIGHT, 0.0)).xyz);
	vec2 rim_offset = vec2(-((light_vector.x * camera_vector.z) + (light_vector.z * camera_vector.x)), -light_vector.y) * highlight_amount;

	vec2 offset_uv;
	if (flip)
	{
		offset_uv = (vec2(-UV.x, UV.y) / atlas_size) + ((vec2(1.0f, 0.0f) + offset) / atlas_size);
		offset_uv = offset_uv + rim_offset / vec2(-atlas_size.x, atlas_size.y);
	}
	else
	{
		offset_uv = (UV / atlas_size) + (offset / atlas_size);
		offset_uv = offset_uv + rim_offset / atlas_size;
	}

	float rim = (1.0f - texture(sprite_texture, offset_uv).a);

	if ((offset_uv.y < (offset / atlas_size).y || offset_uv.y > (1.0f / atlas_size.y) + (offset / atlas_size).y) || (offset_uv.x < (offset / atlas_size).x || offset_uv.x > (1.0f / atlas_size.x) + (offset / atlas_size).x))
	{
		rim = 1.0f;
	}

	DIFFUSE_LIGHT += rim * LIGHT_COLOR * ATTENUATION;
}
Tags
lighting, sprite
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

Related shaders

guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments