1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
-- PBR fragment shader
-- Demonstrates: multiple uniforms, texture sampling, math builtins
struct FragInput
@location(0) uv: vec2
@location(1) normal: vec3
@location(2) world_pos: vec3
end
struct FragOutput
@location(0) color: vec4
end
struct Material
base_color: vec3
roughness: float
metallic: float
end
struct Light
position: vec3
color: vec3
intensity: float
end
@group(0) @binding(0)
uniform material: Material
@group(0) @binding(1)
uniform light: Light
@group(1) @binding(0)
uniform albedo_map: sampler2D
const PI = 3.14159265
-- Helper: Schlick Fresnel approximation
function fresnel_schlick(cos_theta: float, f0: vec3) -> vec3
return f0 + (vec3(1.0, 1.0, 1.0) - f0) * pow(1.0 - cos_theta, 5.0)
end
@entry(fragment)
function main(input: FragInput) -> FragOutput
let n = normalize(input.normal)
let v = normalize(-input.world_pos)
let l = normalize(light.position - input.world_pos)
let h = normalize(v + l)
let albedo = sample(albedo_map, input.uv).rgb * material.base_color
let ndotl = max(dot(n, l), 0.0)
let ndoth = max(dot(n, h), 0.0)
let f0 = mix(vec3(0.04, 0.04, 0.04), albedo, material.metallic)
let f = fresnel_schlick(max(dot(h, v), 0.0), f0)
let diffuse = albedo * (1.0 - material.metallic)
let specular = f * pow(ndoth, (1.0 - material.roughness) * 256.0)
let result = (diffuse + specular) * light.color * light.intensity * ndotl
return FragOutput {
color = vec4(result, 1.0),
}
end