Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions Sources/KeyPathAppKit/UI/KeyboardStage/Metal/KeyboardStage.metal
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ fragment float4 keypath_keyboard_stage_fragment(
// drive gloss, grain, and highlight variation; both derive from the key
// code, so the pattern never shifts between moments or launches.
float wear = isKey
? saturate(input.material.x) * saturate(uniforms.tuningE.z)
? saturate(input.material.x) * max(uniforms.tuningE.z, 0.0)
: 0.0;
float materialSeedA = keypath_hash(float2(input.material.y, 0.37));
float materialSeedB = keypath_hash(float2(input.material.y, 0.71));
Expand Down Expand Up @@ -565,7 +565,9 @@ fragment float4 keypath_keyboard_stage_fragment(
(isDeck ? 0.31 : 0.47)
+ roughnessVariation * (isDeck ? 0.055 : 0.035)
- wear * 0.11
+ (isKey ? (materialSeedA - 0.5) * 0.05 : 0.0),
+ (isKey
? (materialSeedA - 0.5) * 0.05 * min(uniforms.tuningE.w, 1.0)
: 0.0),
0.12,
0.82
);
Expand All @@ -584,8 +586,12 @@ fragment float4 keypath_keyboard_stage_fragment(
* microfacetVisibility
/ max(0.04, 4.0 * normalDotView * normalDotLight);
float specularPower = isDeck ? 88.0 : (isKey ? 42.0 : 34.0);
// Seed-only gloss deviation collapses to 1.0 as highlightJitter
// approaches zero, so the tuning knobs can fully restore the uniform
// pre-wear material.
float keySpecularGain = isKey
? (0.85 + materialSeedB * 0.30) * (1.0 + wear * 0.55)
? (1.0 + (materialSeedB - 0.5) * 0.30 * min(uniforms.tuningE.w, 1.0))
* (1.0 + wear * 0.55)
: 1.0;
float specular = pow(saturate(dot(normal, halfVector)), specularPower)
* keySpecularGain;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ struct KeyboardStageTuning: Equatable, Sendable {
var fillLightStrength: Float = 0.95
var fillSpecularStrength: Float = 0.15
/// Per-key wear (polished crowns, calmer grain on high-traffic caps).
var wearStrength: Float = 1.5
/// A plain multiplier: values above 1 amplify the per-key wear levels.
var wearStrength: Float = 1.0
/// Per-key highlight jitter (each cap catches the light slightly apart).
var highlightJitter: Float = 2.2
/// Window-space corner falloff; relaxes to 30% at the settled light.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ final class KeyboardStageTuningTests: XCTestCase {
XCTAssertEqual(tuning.gpuVectorC.w, tuning.backlightStrengthDark)
XCTAssertEqual(tuning.gpuVectorD.y, tuning.legendEmissionMax)
XCTAssertEqual(tuning.gpuVectorE.x, tuning.fillLightStrength)
XCTAssertEqual(tuning.gpuVectorE.y, tuning.fillSpecularStrength)
XCTAssertEqual(tuning.gpuVectorE.z, tuning.wearStrength)
XCTAssertEqual(tuning.gpuVectorE.w, tuning.highlightJitter)
XCTAssertEqual(tuning.vignetteVector.x, tuning.vignetteStrength)
XCTAssertEqual(tuning.vignetteVector.y, tuning.vignetteInnerRadius)
XCTAssertEqual(tuning.gpuVectorE.x, tuning.fillLightStrength)
XCTAssertEqual(tuning.gpuVectorE.z, tuning.wearStrength)
}

Expand Down
Loading