3D space sprite stacking

3D space sprite stacking shader (and an addon).

 

Description:

Set the material (with this shader code) to a box mesh and specify a texture with the images to be stacked side by side, as in the example attached to albedo_texture uniform. Specify the number of albedo_texture divisions in frames uniform. It works in either ortho or perspective camera, but if camera is perspective, set is_perspective uniform to true.

Another version with lighting and normal generator are included in the demo project.

Complete source codes:

Shader code
shader_type spatial;
render_mode
	blend_mix,
	depth_draw_opaque,
	cull_back,
	unshaded;

uniform sampler2D albedo_texture : filter_nearest, source_color;
uniform int frames = 1;
uniform vec4 modulate : source_color = vec4(1.0);
uniform bool is_perspective;

varying vec3 box_extent;
varying vec3 ray_origin;
varying vec3 ray_direction;
varying float ray_t_step;
varying mat4 model_view_projection_matrix;

const int MAX_RAYMARCH_STEPS = 128;
const float MIN_RAYMARCH_T = 0.001;

const float DITHER_PATTERN[] = {
	 1.0 / 17.0,  9.0 / 17.0,  3.0 / 17.0, 11.0 / 17.0,
	13.0 / 17.0,  5.0 / 17.0, 15.0 / 17.0,  7.0 / 17.0,
	 4.0 / 17.0, 12.0 / 17.0,  2.0 / 17.0, 10.0 / 17.0,
	16.0 / 17.0,  8.0 / 17.0, 14.0 / 17.0,  6.0 / 17.0 };
const int[] COMPONENT_SELECTOR = {
	2, 2, 2, 2, 2, 2, 2, 2,
	0, 0, 0, 0, 0, 0, 0, 0,
	1, 1, 1, 1, 1, 1, 1, 1};

void vertex()
{
	box_extent = abs(VERTEX);
	ray_origin = VERTEX;
	ray_direction = normalize(is_perspective || PROJECTION_MATRIX[3].w == 0.0
		? ray_origin - (inverse(MODELVIEW_MATRIX) * vec4(0, 0, 0, 1)).xyz
		: inverse(mat3(MODELVIEW_MATRIX)) * vec3(0, 0, -1));
	ray_t_step = max(2.0 * box_extent[COMPONENT_SELECTOR[VERTEX_ID]] / float(MAX_RAYMARCH_STEPS), MIN_RAYMARCH_T);
	model_view_projection_matrix = PROJECTION_MATRIX * MODELVIEW_MATRIX;
}

void fragment()
{
	vec2 uv;
	vec3 color = vec3(0);
	float alpha = 0.0;
	float depth;
	float ray_t = MIN_RAYMARCH_T;
	bool ray_hit = false;
	for (int i = 0; i < MAX_RAYMARCH_STEPS; ++i)
	{
		vec3 ray_hit_position = ray_origin + ray_direction * ray_t;
		vec3 box_position = (ray_hit_position + box_extent) / (box_extent * 2.0);
		if (box_position.x < 0.0 || 1.0 < box_position.x ||
			box_position.y < 0.0 || 1.0 < box_position.y ||
			box_position.z < 0.0 || 1.0 < box_position.z) break;
		box_position.x = 1.0 - box_position.x;
		box_position.z = 1.0 - box_position.z;
		float slice_count = float(frames);
		float slice_offset = floor(box_position.y * slice_count) / slice_count;
		uv = vec2(box_position.x / slice_count + slice_offset, box_position.z);
		vec4 albedo = texture(albedo_texture, uv);
		if (0.0 < albedo.a)
		{
			color += (1.0 - alpha) * albedo.a * albedo.rgb;
			alpha += (1.0 - alpha) * albedo.a;
			if (!ray_hit)
			{
				vec4 ray_clip = model_view_projection_matrix * vec4(ray_hit_position, 1.0);
				depth = ray_clip.z / ray_clip.w;
				ray_hit = true;
			}
		}
		if (1.0 <= alpha) break;
		ray_t += ray_t_step;
	}
	if (!ray_hit) discard;
	ivec2 dither_position = ivec2(FRAGCOORD.xy) % ivec2(4);
	color *= modulate.rgb;
	alpha *= step(DITHER_PATTERN[dither_position.y * 4 + dither_position.x], alpha * modulate.a);
	ALBEDO = color / alpha;
	ALPHA = alpha;
	ALPHA_SCISSOR_THRESHOLD = 0.1;
	DEPTH = depth;
}
Tags
sprite stack
The shader code and all code snippets in this post are under MIT 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.

More from ydi

Related shaders

guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments