Surface contour lines

Shows lines similar to map elevation lines on any model in world space.

Adapted from a snippet found here: https://www.gamedev.net/forums/topic/529926-terrain-contour-lines-using-pixel-shader/

Uses fractal depth scaling to seamlessly fade in/out lines as objects get near/far.

Shader code
shader_type spatial;

uniform float line_thickness : hint_range(0.0, 2.0, 0.01) = 1.0;
uniform float line_interval : hint_range(0.01, 100.0, 0.01) = 0.25;
uniform vec3 line_color : source_color = vec3(0.9, 0.6, 0.2);

vec3 fractal_contour_lines(float thickness, float interval, vec3 world_pos, vec3 camera_position, vec3 base_ddx, vec3 base_ddy) {
	float depth = max(log(length(world_pos - camera_position) / interval) * (1.0 / log2(2.0)) - 1.0, 1.0);

	float interval_a = interval * exp2(max(floor(depth) - 1.0, 1.0)) * 0.5;
	float interval_b = interval * exp2(max(floor(depth), 1.0)) * 0.5;
	float interval_c = interval * exp2(max(floor(depth + 0.5) - 1.0, 1.0)) * 0.5;

	vec3 y = world_pos;
	vec3 y_fwidth = abs(base_ddx) + abs(base_ddy);

	float mi = max(0.0, thickness - 1.0);
	float ma = max(1.0, thickness);
	float mx = max(0.0, 1.0 - thickness);

	// Line A
	float inv_interval_a = 1.0 / interval_a;
	vec3 f_a = abs(fract((y + interval_a * 0.5) * inv_interval_a) - 0.5);
	vec3 df_a = y_fwidth * inv_interval_a;
	vec3 line_a = clamp((f_a - df_a * mi) / (df_a * (ma - mi)), mx, 1.0);

	// Line B
	float inv_interval_b = 1.0 / interval_b;
	vec3 f_b = abs(fract((y + interval_b * 0.5) * inv_interval_b) - 0.5);
	vec3 df_b = y_fwidth * inv_interval_b;
	vec3 line_b = clamp((f_b - df_b * mi) / (df_b * (ma - mi)), mx, 1.0);

	// Line C
	float inv_interval_c = 1.0 / interval_c;
	vec3 f_c = abs(fract((y + interval_c * 0.5) * inv_interval_c) - 0.5);
	vec3 df_c = y_fwidth * inv_interval_c;
	vec3 line_c = clamp((f_c - df_c * mi) / (df_c * (ma - mi)), mx, 1.0);

	// Blend out
	float p = fract(depth - 0.5);
	vec3 line = mix(mix(line_a, line_b, fract(depth)), line_c, (4.0 * p * (1.0 - p)));
	return 1.0 - vec3(line);
}

void fragment() {
	vec3 world_pos = (INV_VIEW_MATRIX * vec4(VERTEX,1.0)).xyz;
	vec3 ddx = dFdx(world_pos);
	vec3 ddy = dFdy(world_pos);
	vec3 world_normal = normalize(cross(ddy, ddx));
	vec3 line = fractal_contour_lines(line_thickness, line_interval, world_pos, CAMERA_POSITION_WORLD, ddx, ddy);
	
	// Y only
	ALBEDO = mix(vec3(0.1), line_color, clamp(line.y, 0., 1.));
	// XYZ
	//ALBEDO = mix(vec3(0.1), line_color, clamp(line.x + line.y + line.z, 0., 1.));
}
Live Preview
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.

More from xtarsia

Related shaders

guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments