extends RefCounted ## Materialien ohne Texturdateien: Holz, Filz und Messing werden im Shader ## aus Rauschen gerechnet. Alles, was Maserung, Flecken oder Anlauf ist, sind ## Zahlen - keine Bilder. Siehe assets.md. Kein class_name (headless kein Cache). const RAUSCHEN := """ float hash2(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); } float noise2(vec2 p) { vec2 i = floor(p); vec2 f = fract(p); f = f * f * (3.0 - 2.0 * f); return mix(mix(hash2(i), hash2(i + vec2(1, 0)), f.x), mix(hash2(i + vec2(0, 1)), hash2(i + vec2(1, 1)), f.x), f.y); } float fbm2(vec2 p) { float v = 0.0; float a = 0.5; for (int k = 0; k < 4; k++) { v += a * noise2(p); p = p * 2.03 + 17.0; a *= 0.5; } return v; } """ ## Holz: Jahresringe entlang einer Achse, leicht verzerrt, plus feine ## Faser. Weltkoordinaten, damit die Maserung über Teile hinweg durchläuft. const HOLZ := """ shader_type spatial; uniform vec4 base_color : source_color = vec4(0.42, 0.27, 0.13, 1.0); uniform vec4 dark_color : source_color = vec4(0.22, 0.13, 0.06, 1.0); uniform float wear : hint_range(0.0, 1.0) = 0.4; uniform float ring_scale = 9.0; varying vec3 wp; """ + RAUSCHEN + """ void vertex() { wp = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz; } void fragment() { vec2 p = wp.xz; float verzerrt = p.x * ring_scale + fbm2(p * 2.5) * 3.0; float ring = smoothstep(0.2, 0.8, abs(fract(verzerrt) - 0.5) * 2.0); float faser = noise2(vec2(p.x * 60.0, p.y * 4.0)) * 0.12; vec3 col = mix(dark_color.rgb, base_color.rgb, ring) + faser - 0.06; float flecken = fbm2(p * 6.0 + 40.0); col = mix(col, col * 0.75, smoothstep(0.55, 0.8, flecken) * wear); ALBEDO = col; ROUGHNESS = 0.55 + ring * 0.25 + wear * 0.15; SPECULAR = 0.3; } """ ## Filz: gleichmässige Fläche mit feinem Flaum, leicht fleckig, an den ## Rändern dunkler. Ohne das sähe die Zone aus wie Plastik. const FILZ := """ shader_type spatial; uniform vec4 base_color : source_color = vec4(0.07, 0.20, 0.13, 1.0); uniform float wear : hint_range(0.0, 1.0) = 0.5; uniform float radius = 0.68; varying vec3 lp; varying vec3 wp; """ + RAUSCHEN + """ void vertex() { lp = VERTEX; wp = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz; } void fragment() { float flaum = noise2(wp.xz * 90.0) * 0.10 + noise2(wp.xz * 300.0) * 0.05; float flecken = fbm2(wp.xz * 3.0) ; float rand = smoothstep(radius * 0.7, radius, length(lp.xz)); vec3 col = base_color.rgb * (0.9 + flaum) * (1.0 - flecken * 0.25 * wear) * (1.0 - rand * 0.25); ALBEDO = col; ROUGHNESS = 0.97; SPECULAR = 0.1; } """ ## Messing: metallisch, mit Anlauf in den Vertiefungen und einem Hauch ## Farbschwankung. Die Prägung bleibt ein eigenes Mesh. const MESSING := """ shader_type spatial; uniform vec4 base_color : source_color = vec4(0.80, 0.62, 0.26, 1.0); uniform float wear : hint_range(0.0, 1.0) = 0.4; uniform float emission : hint_range(0.0, 2.0) = 0.35; varying vec3 wp; """ + RAUSCHEN + """ void vertex() { wp = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz; } void fragment() { float anlauf = fbm2(wp.xz * 40.0); vec3 col = base_color.rgb * (0.85 + anlauf * 0.3); col = mix(col, col * vec3(0.7, 0.6, 0.5), smoothstep(0.6, 0.85, anlauf) * wear); ALBEDO = col; METALLIC = 0.9; ROUGHNESS = 0.25 + anlauf * 0.35 * wear; EMISSION = base_color.rgb * 0.6 * emission; } """ static var _holz: Shader static var _filz: Shader static var _messing: Shader static func _shader(code: String) -> Shader: var s := Shader.new() s.code = code return s static func holz(farbe: Color, dunkel: Color, wear := 0.4, ring_scale := 9.0) -> ShaderMaterial: if _holz == null: _holz = _shader(HOLZ) var m := ShaderMaterial.new() m.shader = _holz m.set_shader_parameter("base_color", farbe) m.set_shader_parameter("dark_color", dunkel) m.set_shader_parameter("wear", wear) m.set_shader_parameter("ring_scale", ring_scale) return m static func filz(farbe: Color, radius: float, wear := 0.5) -> ShaderMaterial: if _filz == null: _filz = _shader(FILZ) var m := ShaderMaterial.new() m.shader = _filz m.set_shader_parameter("base_color", farbe) m.set_shader_parameter("radius", radius) m.set_shader_parameter("wear", wear) return m static func messing(emission := 0.35, wear := 0.4) -> ShaderMaterial: if _messing == null: _messing = _shader(MESSING) var m := ShaderMaterial.new() m.shader = _messing m.set_shader_parameter("emission", emission) m.set_shader_parameter("wear", wear) return m