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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import super::super::shader::u_globals;
@group(0) @binding(2) var<storage, read> s_light_array: array<Light>;
struct Light {
position: vec4<f32>,
direction: vec4<f32>, // x, y, z, outer_cutoff_angle
color: vec4<f32>, // r, g, b, light_type (0=directional, 1=point, 2=spot)
constant: f32,
lin: f32,
quadratic: f32,
cutoff: f32, // inner cutoff (cos of angle)
}
const LIGHT_DIRECTIONAL: u32 = 0u;
const LIGHT_POINT: u32 = 1u;
const LIGHT_SPOT: u32 = 2u;
fn diffuse(
light_dir: vec3<f32>,
normal: vec3<f32>,
light_color: vec3<f32>,
object_color: vec3<f32>,
) -> vec3<f32> {
let diff = max(dot(normal, light_dir), 0.0);
return diff * light_color * object_color;
}
// Blinn-Phong specular
fn specular(
light_dir: vec3<f32>,
normal: vec3<f32>,
view_dir: vec3<f32>,
light_color: vec3<f32>,
shininess: f32,
) -> vec3<f32> {
let halfway = normalize(light_dir + view_dir);
let spec = pow(max(dot(normal, halfway), 0.0), shininess);
return light_color * spec;
}
fn attenuation(light: Light, tangent_light_pos: vec3<f32>, tangent_pos: vec3<f32>) -> f32 {
let d = length(tangent_light_pos - tangent_pos);
return 1.0 / (light.constant + light.lin * d + light.quadratic * d * d);
}
fn calculate_light(
light: Light,
tangent_pos: vec3<f32>,
tangent_matrix: mat3x3<f32>,
normal: vec3<f32>, // normalised tangent-space surface normal
view_dir: vec3<f32>, // normalised tangent-space direction from fragment to camera
object_color: vec3<f32>,
shininess: f32,
spec_scale: f32,
) -> vec3<f32> {
let light_type = u32(light.color.w);
let light_color = light.color.xyz;
var light_dir: vec3<f32>;
var atten: f32 = 1.0;
var intensity: f32 = 1.0;
if light_type == LIGHT_DIRECTIONAL {
light_dir = normalize(tangent_matrix * (-light.direction.xyz));
} else {
// point and spot included
let tangent_light_pos = tangent_matrix * light.position.xyz;
light_dir = normalize(tangent_light_pos - tangent_pos);
atten = attenuation(light, tangent_light_pos, tangent_pos);
if light_type == LIGHT_SPOT {
// smooth edge
let theta = dot(light_dir, normalize(tangent_matrix * (-light.direction.xyz)));
let inner_cutoff = light.cutoff;
let outer_cutoff = light.direction.w;
let epsilon = inner_cutoff - outer_cutoff;
intensity = clamp((theta - outer_cutoff) / epsilon, 0.0, 1.0);
}
}
let d = diffuse(light_dir, normal, light_color, object_color) * intensity;
let s = specular(light_dir, normal, view_dir, light_color, shininess) * intensity * spec_scale;
return (d + s) * atten;
}
fn calculate_lighting(
tangent_pos: vec3<f32>,
tangent_matrix: mat3x3<f32>,
normal: vec3<f32>,
view_dir: vec3<f32>,
object_color: vec3<f32>,
shininess: f32,
spec_scale: f32,
) -> vec3<f32> {
var result = u_globals.ambient_strength * object_color;
let num_lights = u_globals.num_lights;
for (var i = 0u; i < num_lights; i++) {
result += calculate_light(
s_light_array[i],
tangent_pos,
tangent_matrix,
normal,
view_dir,
object_color,
shininess,
spec_scale,
);
}
return result;
}