Realistic FDM 3D printed shader material
Shader to mimic FDM 3D prints.
There is a seperate top pattern. The minimum “angle” that determines what surface is considered as “top” can be tweaked using top_pattern_strength.
The entire pattern can be rotated by editing x_degrees and y_degrees.
If you need a combination of the 3D printed look and another material, set an overlay_texture (with a transparent background) to overlay a texture onto the 3D printed material.
Builtin AA that fades away the pattern at further distances to reduce Moiré patterns.
Shader code
shader_type spatial;
uniform float top_pattern_strength: hint_range(0.5, 1.0) = 0.6;
uniform vec3 color: source_color;
uniform float layer_height_mm: hint_range(0.1, 1.0) = 0.2;
uniform float x_degrees: hint_range(-90.0, 90.0) = 0.0; // rotation around X
uniform float z_degrees: hint_range(-90.0, 90.0) = 0.0; // rotation around Z
uniform sampler2D overlay_texture:hint_default_transparent;
varying vec3 local_pos;
varying vec3 print_up;
varying vec3 local_normal;
vec3 get_normal_map(float x, float aa) {
float theta = fract(x)*PI;
vec3 normal = vec3(cos(theta), 0.0, -sin(theta)) * aa;
//convert normal vector to correct normal map color
return vec3(
0.5+normal.x*0.5,
0.5+normal.y*0.5,
0.5-normal.z*0.5);
}
mat3 rotX(float a) {
float c = cos(a), s = sin(a);
return mat3(
vec3(1.0, 0.0, 0.0),
vec3(0.0, c, s),
vec3(0.0, -s, c)
);
}
mat3 rotZ(float a) {
float c = cos(a), s = sin(a);
return mat3(
vec3(c, s, 0.0),
vec3(-s, c, 0.0),
vec3(0.0, 0.0, 1.0)
);
}
void vertex() {
mat3 print_rotation = rotX(radians(x_degrees)) * rotZ(radians(z_degrees));
local_pos = print_rotation * VERTEX;
print_up = print_rotation * vec3(0.0, 1.0, 0.0);
local_normal = NORMAL; // model/object space, unaffected by node's world transform
}
void fragment() {
const float INV_SQRT_2 = 0.70710678;
ROUGHNESS = 0.3;
float luminance = dot(color, vec3(0.299, 0.587, 0.114));
SPECULAR = mix(0.2, 0.5, luminance);
METALLIC = 0.0;
float sideness = round(abs(dot(local_normal, print_up))*top_pattern_strength);
float scale = (1.0/layer_height_mm)*1000.0;
vec4 overlay_color = texture(overlay_texture, UV);
float y_input = local_pos.y*scale;
float diag_input = (local_pos.x+local_pos.z)*INV_SQRT_2*scale;
float aa_y = 1.0 - smoothstep(0.7, 1.0, fwidth(y_input));
float aa_diag = 1.0 - smoothstep(0.7, 1.0, fwidth(diag_input));
NORMAL_MAP = mix(
mix(
get_normal_map(y_input, aa_y),
get_normal_map(diag_input, aa_diag),
sideness
),
vec3(0.5, 0.5, 1.0),
overlay_color.a
);
ALBEDO.rgb = mix(color, overlay_color.rgb, overlay_color.a);
}
