Sobel filter sampling for gunk normal maps

This commit is contained in:
Rob Kelly 2025-09-04 14:21:17 -06:00
parent e5ce84bec1
commit 66b8c90070
14 changed files with 116 additions and 54 deletions

View File

@ -1,3 +1,4 @@
// -*- mode: glsl -*-
/* Gunk shader adapted to a canvas item */
shader_type canvas_item;

View File

@ -1,3 +1,4 @@
// -*- mode: glsl -*-
// Common logic for gunk shaders
group_uniforms gunk_material;
@ -36,6 +37,23 @@ uniform sampler2D overlay_albedo: hint_default_transparent, filter_nearest;
uniform sampler2D overlay_emission: hint_default_transparent, filter_nearest;
uniform float overlay_emission_scale = 1.0;
#if defined(USE_MASK)
group_uniforms gunk_mask;
uniform float edge_bleed = 0.25;
uniform sampler2D gunk_mask;
#endif
float bump_sample(vec2 uv, vec3 uvt, float dx, float dy) {
vec2 offset = vec2(dx / pixellation, dy / pixellation);
float height = texture(gunk_noise, uvt + vec3(offset, 0.0)).r;
#if defined(USE_MASK)
float mask = texture(gunk_mask, uv + offset).r;
height *= smoothstep(1.0, 0.0, mask);
#endif
return height;
}
vec3 rim_glow(
vec3 normal,
vec3 view,
@ -89,3 +107,82 @@ vec3 base_emission(vec2 uv, float value) {
float base_specular() {
return 0.5 * inversesqrt(specular_contribution);
}
// Convolution methods
vec3 godot_convolution(vec2 uv, vec3 uvt) {
// from https://github.com/godotengine/godot/blob/master/core/io/image.cpp#L3758
float here = bump_sample(uv, uvt, 0.0, 0.0);
float to_right = bump_sample(uv, uvt, 1.0, 0.0);
float above = bump_sample(uv, uvt, 0.0, -1.0);
vec3 up = vec3(0.0, 1.0, (here - above) * bump_strength);
vec3 across = vec3(1.0, 0.0, (to_right - here) * bump_strength);
return normalize(cross(across, up));
}
vec3 minimal_convolution(vec2 uv, vec3 uvt) {
float h_center = bump_sample(uv, uvt, 0.0, 0.0);
float h_right = bump_sample(uv, uvt, 1.0, 0.0);
float h_down = bump_sample(uv, uvt, 0.0, 1.0);
float dX = (h_center - h_right);
float dY = (h_center - h_down);
float dZ = 1.0 / bump_strength;
return normalize(vec3(dX, dY, dZ));
}
vec3 cross_convolution(vec2 uv, vec3 uvt) {
float h_right = bump_sample(uv, uvt, 1.0, 0.0);
float h_down = bump_sample(uv, uvt, 0.0, 1.0);
float h_left = bump_sample(uv, uvt, -1.0, 0.0);
float h_up = bump_sample(uv, uvt, 0.0, -1.0);
float dX = (h_left - h_right);
float dY = (h_right - h_down);
float dZ = 1.0 / bump_strength * 2.0;
return normalize(vec3(dX, dY, dZ));
}
vec3 sobel_convolution(vec2 uv, vec3 uvt) {
float tl = bump_sample(uv, uvt, -1.0, -1.0);
float l = bump_sample(uv, uvt, -1.0, 0.0);
float bl = bump_sample(uv, uvt, -1.0, 1.0);
float t = bump_sample(uv, uvt, 0.0, -1.0);
float b = bump_sample(uv, uvt, 0.0, 1.0);
float tr = bump_sample(uv, uvt, 1.0, -1.0);
float r = bump_sample(uv, uvt, 1.0, 0.0);
float br = bump_sample(uv, uvt, 1.0, 1.0);
float dX = (tr + 2.0 * r + br) - (tl + 2.0 * l + bl);
float dY = (bl + 2.0 * b + br) - (tl + 2.0 * t + tr);
float dZ = 1.0 / bump_strength * 8.0;
return normalize(vec3(dX, dY, dZ));
}
vec3 scharr_convolution(vec2 uv, vec3 uvt) {
float tl = bump_sample(uv, uvt, -1.0, -1.0);
float l = bump_sample(uv, uvt, -1.0, 0.0);
float bl = bump_sample(uv, uvt, -1.0, 1.0);
float t = bump_sample(uv, uvt, 0.0, -1.0);
float b = bump_sample(uv, uvt, 0.0, 1.0);
float tr = bump_sample(uv, uvt, 1.0, -1.0);
float r = bump_sample(uv, uvt, 1.0, 0.0);
float br = bump_sample(uv, uvt, 1.0, 1.0);
float dX = 3.0*tl + 10.0*l + 3.0*bl - 3.0*tr - 10.0*r - 3.0*br;
float dY = 3.0*tl + 10.0*t + 3.0*tr - 3.0*bl - 10.0*b - 3.0*br;
float dZ = 1.0 / bump_strength * 12.0;
return normalize(vec3(dX, dY, dZ));
}
vec3 sobel_5x5_convolution(vec2 uv, vec3 uvt) {
// Expensive!!
// TODO this can be decomposed into a few small matrix ops
float s[25];
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
s[i*5 + j] = bump_sample(uv, uvt, float(i - 2), float(j - 2));
}
}
float dX = 4.0 * (s[3] + s[23] - s[1] - s[21]) + 5.0 * (s[4] + s[24] - s[0] - s[20]) + 8.0 * (s[9] + s[19] - s[5] - s[15]) + 10.0 * (s[8] + s[14] + s[18] - s[6] - s[10] - s[16]) + 20.0 * (s[13] - s[11]);
float dY = 4.0 * (s[19] + s[15] - s[9] - s[5]) + 5.0 * (s[24] + s[20] - s[4] - s[0]) + 8.0 * (s[23] + s[21] - s[3] - s[1]) + 10.0 * (s[18] + s[22] + s[16] - s[8] - s[2] - s[6]) + 20.0 * (s[17] - s[7]);
float dZ = 1.0 / bump_strength * 240.0;
return normalize(vec3(dX, dY, dZ));
}

View File

@ -1,31 +1,16 @@
// -*- mode: glsl -*-
shader_type spatial;
render_mode depth_prepass_alpha, unshaded;
#define USE_MASK
#include "common.gdshaderinc"
group_uniforms gunk_mask;
uniform float edge_bleed = 0.25;
uniform sampler2D gunk_mask;
float bump_sample(vec2 uv, vec3 uvt, float dx, float dy) {
vec2 offset = vec2(dx / pixellation, dy / pixellation);
float height = texture(gunk_noise, uvt + vec3(offset, 0.0)).r;
float mask = texture(gunk_mask, uv + offset / uv_scale).r;
return height * smoothstep(1.0, 0.0, mask);
}
void fragment() {
vec3 uvt = scale_uvt(UV, TIME);
float value = sample_noise(uvt);
// Build normal map from bump map
float h_center = bump_sample(UV, uvt, 0.0, 0.0);
float h_right = bump_sample(UV, uvt, 1.0, 0.0);
float h_down = bump_sample(UV, uvt, 0.0, 1.0);
float dx = (h_center - h_right) * bump_strength;
float dy = (h_center - h_down) * bump_strength;
vec3 normal_diff_map = normalize(vec3(dx, dy, 1.0));
ALBEDO = normal_diff_map / 2.0 + 0.5;
vec3 nmap = sobel_convolution(UV, uvt);
ALBEDO = nmap / 2.0 + 0.5;
}

View File

@ -1,3 +1,4 @@
// -*- mode: glsl -*-
shader_type spatial;
render_mode depth_prepass_alpha;
@ -23,11 +24,6 @@ void vertex() {
VERTEX *= 1.0 + jitter;
}
float bump_sample(vec3 uvt, float dx, float dy) {
vec2 offset = vec2(dx / pixellation, dy / pixellation);
return texture(gunk_noise, uvt + vec3(offset, 0.0)).r;
}
void fragment() {
vec3 uvt = scale_uvt(UV, TIME);
float value = sample_noise(uvt);
@ -36,17 +32,11 @@ void fragment() {
EMISSION = base_emission(UV, value);
SPECULAR = base_specular();
// Build normal map from bump map
float h_center = bump_sample(uvt, 0.0, 0.0);
float h_right = bump_sample(uvt, 1.0, 0.0);
float h_down = bump_sample(uvt, 0.0, 1.0);
float dx = (h_center - h_right) * bump_strength;
float dy = (h_center - h_down) * bump_strength;
vec3 normal_diff_map = normalize(vec3(dx, dy, 1.0));
NORMAL_MAP = normal_diff_map / 2.0 + 0.5;
vec3 nmap = sobel_convolution(UV, uvt);
NORMAL_MAP = nmap / 2.0 + 0.5;
// add fresnel
vec3 world_normal = mat3(TANGENT, BINORMAL, NORMAL) * normal_diff_map;
vec3 world_normal = mat3(TANGENT, BINORMAL, NORMAL) * nmap;
EMISSION += rim_glow(
world_normal,
VIEW,

View File

@ -1,20 +1,10 @@
// -*- mode: glsl -*-
shader_type spatial;
render_mode depth_prepass_alpha;
#define USE_MASK
#include "common.gdshaderinc"
group_uniforms gunk_mask;
uniform float edge_bleed = 0.25;
uniform sampler2D gunk_mask;
float bump_sample(vec2 uv, vec3 uvt, float dx, float dy) {
vec2 offset = vec2(dx / pixellation, dy / pixellation);
float height = texture(gunk_noise, uvt + vec3(offset, 0.0)).r;
float mask = texture(gunk_mask, uv + offset / uv_scale).r;
return height * smoothstep(1.0, 0.0, mask);
}
void fragment() {
vec3 uvt = scale_uvt(UV, TIME);
float value = sample_noise(uvt);
@ -23,17 +13,16 @@ void fragment() {
EMISSION = base_emission(UV, value);
SPECULAR = base_specular();
// Build normal map from bump map
float h_center = bump_sample(UV, uvt, 0.0, 0.0);
float h_right = bump_sample(UV, uvt, 1.0, 0.0);
float h_down = bump_sample(UV, uvt, 0.0, 1.0);
float dx = (h_center - h_right) * bump_strength;
float dy = (h_center - h_down) * bump_strength;
vec3 normal_diff_map = normalize(vec3(dx, dy, 1.0));
NORMAL_MAP = normal_diff_map / 2.0 + 0.5;
// vec3 nmap = minimal_convolution(UV, uvt);
// vec3 nmap = cross_convolution(UV, uvt);
vec3 nmap = sobel_convolution(UV, uvt);
// vec3 nmap = sobel_5x5_convolution(UV, uvt);
// vec3 nmap = scharr_convolution(UV, uvt);
// vec3 nmap = godot_convolution(UV, uvt);
NORMAL_MAP = nmap / 2.0 + 0.5;
// add fresnel
vec3 world_normal = mat3(TANGENT, BINORMAL, NORMAL) * normal_diff_map;
vec3 world_normal = mat3(TANGENT, BINORMAL, NORMAL) * nmap;
EMISSION += rim_glow(
world_normal,
VIEW,