Shader code
shader_type spatial;
// ============================================================
// COLORES
// ============================================================
uniform vec4 base_color : source_color = vec4(0.12, 0.12, 0.14, 1.0);
uniform vec4 dirt_color : source_color = vec4(0.72, 0.65, 0.52, 1.0);
uniform sampler2D dirt_texture : hint_default_white, repeat_enable;
uniform float dirt_texture_strength : hint_range(0.0, 1.0) = 0.25;
uniform float dirt_intensity : hint_range(0.0, 2.0) = 1.35;
uniform int dirt_blend_mode : hint_range(0, 2) = 0;
// ============================================================
// DOS CAPAS DE NOISE
// ============================================================
// Capa 1: Ruido de fondo GRANDE (manchas / distribución general)
uniform sampler2D noise_large : hint_default_black, repeat_enable;
uniform float large_scale : hint_range(0.01, 2.0) = 0.07; // ← más grande (antes 0.15)
uniform float large_contrast : hint_range(0.1, 5.0) = 1.25;
// Capa 2: Ruido de PUNTOS DE POLVO (detalle fino)
uniform sampler2D noise_detail : hint_default_black, repeat_enable;
uniform float detail_scale : hint_range(0.1, 20.0) = 6.5; // ← mucho más fino (antes 2.0)
uniform float detail_contrast : hint_range(0.1, 5.0) = 3.2;
// Mezcla: 0 = solo fondo grande, 1 = fondo + puntos de polvo
uniform float detail_strength : hint_range(0.0, 1.0) = 0.85;
// Brillo global del ruido combinado
uniform float noise_brightness : hint_range(-1.0, 1.0) = -0.05;
// ============================================================
// DISTRIBUCIÓN
// ============================================================
uniform float dirt_amount : hint_range(0.0, 1.0) = 0.82;
uniform float edge_softness : hint_range(0.001, 1.0) = 0.14;
uniform float invert_dirt : hint_range(0.0, 1.0) = 0.0;
uniform vec3 dirt_direction = vec3(0.0, -1.0, 0.0);
uniform float direction_strength : hint_range(0.0, 1.0) = 0.85;
uniform bool mirror_direction = false;
uniform float side_strength : hint_range(0.0, 1.0) = 0.55;
uniform float top_influence : hint_range(0.0, 1.0) = 0.08;
uniform bool world_space_noise = false;
// ============================================================
// MATERIAL
// ============================================================
uniform float roughness_base : hint_range(0.0, 1.0) = 0.28;
uniform float roughness_dirt : hint_range(0.0, 1.0) = 0.92;
uniform float normal_dirt_strength : hint_range(0.0, 1.0) = 0.18;
// ============================================================
// VARYINGS
// ============================================================
varying vec3 obj_pos;
varying vec3 obj_normal;
varying vec3 world_normal;
varying vec3 world_pos;
// ============================================================
// FUNCIONES
// ============================================================
vec4 sample_triplanar(sampler2D tex, vec3 pos, vec3 normal, float scale) {
vec3 blend = abs(normal);
blend = pow(blend, vec3(4.0));
blend /= dot(blend, vec3(1.0));
vec3 p = pos * scale;
vec4 x = texture(tex, p.yz);
vec4 y = texture(tex, p.xz);
vec4 z = texture(tex, p.xy);
return x * blend.x + y * blend.y + z * blend.z;
}
vec3 blend_colors(vec3 base, vec3 dirt, float mask, int mode) {
if (mode == 0) return mix(base, dirt, mask * dirt_intensity);
else if (mode == 1) return mix(base, base * dirt, mask);
else return mix(base, min(base + dirt * dirt_intensity, vec3(1.0)), mask);
}
// ============================================================
// VERTEX
// ============================================================
void vertex() {
obj_pos = VERTEX;
obj_normal = NORMAL;
world_normal = normalize((MODEL_MATRIX * vec4(NORMAL, 0.0)).xyz);
world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
}
// ============================================================
// FRAGMENT
// ============================================================
void fragment() {
vec3 noise_pos = world_space_noise ? world_pos : obj_pos;
vec3 noise_normal = world_space_noise ? world_normal : obj_normal;
// --------------------------------------------------------
// CAPA 1: RUIDO DE FONDO GRANDE
// --------------------------------------------------------
float noise_1 = sample_triplanar(noise_large, noise_pos, noise_normal, large_scale).r;
noise_1 = pow(clamp(noise_1, 0.0, 1.0), large_contrast);
// --------------------------------------------------------
// CAPA 2: PUNTOS DE POLVO (detalle fino)
// --------------------------------------------------------
float noise_2 = sample_triplanar(noise_detail, noise_pos, noise_normal, detail_scale).r;
noise_2 = pow(clamp(noise_2, 0.0, 1.0), detail_contrast);
// --------------------------------------------------------
// COMBINAR NOISES
// --------------------------------------------------------
float combined_noise = mix(noise_1, noise_1 * noise_2, detail_strength);
combined_noise = clamp(combined_noise + noise_brightness, 0.0, 1.0);
// --------------------------------------------------------
// DIRECCIÓN Y MÁSCARAS
// --------------------------------------------------------
vec3 dirt_dir = normalize(dirt_direction);
float alignment = dot(world_normal, dirt_dir);
float direction_factor = alignment * 0.5 + 0.5;
float top_factor = world_normal.y * 0.5 + 0.5;
direction_factor = mix(direction_factor, top_factor, top_influence);
float direction_mask = 1.0 - direction_strength * (1.0 - direction_factor);
float side_factor = 1.0 - abs(world_normal.y);
float side_mask = 1.0 - side_strength * (1.0 - side_factor);
// --------------------------------------------------------
// MÁSCARA FINAL
// --------------------------------------------------------
float mask = combined_noise * direction_mask * side_mask;
float dirt_mask = smoothstep(0.5 - edge_softness, 0.5 + edge_softness, mask);
dirt_mask = mix(dirt_mask, 1.0 - dirt_mask, invert_dirt);
dirt_mask *= dirt_amount;
// --------------------------------------------------------
// COLOR Y MATERIAL
// --------------------------------------------------------
vec3 final_dirt_color = dirt_color.rgb;
if (dirt_texture_strength > 0.0) {
vec4 tex_color = sample_triplanar(dirt_texture, noise_pos, noise_normal, large_scale * 0.6);
final_dirt_color = mix(final_dirt_color, tex_color.rgb, dirt_texture_strength);
}
ALBEDO = blend_colors(base_color.rgb, final_dirt_color, dirt_mask, dirt_blend_mode);
ROUGHNESS = mix(roughness_base, roughness_dirt, dirt_mask);
SPECULAR = mix(0.5, 0.08, dirt_mask);
if (normal_dirt_strength > 0.0) {
NORMAL_MAP_DEPTH *= mix(1.0, 0.35, dirt_mask * normal_dirt_strength);
}
// DEBUG (descomenta para ver capas)
// ALBEDO = vec3(noise_1); // solo fondo grande
// ALBEDO = vec3(noise_2); // solo puntos de polvo
// ALBEDO = vec3(combined_noise); // combinado
// ALBEDO = vec3(dirt_mask); // máscara final
}
Live Preview