6
6
#import bevy_pbr ::clustered_forward
7
7
#import bevy_pbr ::lighting
8
8
#import bevy_pbr ::shadows
9
+ #import bevy_pbr ::pbr_functions
9
10
10
11
struct FragmentInput {
11
12
[[builtin (front_facing )]] is_front : bool ;
@@ -24,22 +25,31 @@ struct FragmentInput {
24
25
[[stage (fragment )]]
25
26
fn fragment (in : FragmentInput ) -> [[location (0 )]] vec4 <f32 > {
26
27
var output_color : vec4 <f32 > = material . base_color;
27
- #ifdef VERTEX_COLORS
28
+ #ifdef VERTEX_COLORS
28
29
output_color = output_color * in . color;
29
- #endif
30
+ #endif
30
31
if ((material . flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u ) {
31
32
output_color = output_color * textureSample (base_color_texture , base_color_sampler , in . uv);
32
33
}
33
34
34
- // // NOTE: Unlit bit not set means == 0 is true, so the true case is if lit
35
+ // NOTE: Unlit bit not set means == 0 is true, so the true case is if lit
35
36
if ((material . flags & STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u ) {
37
+ // Prepare a 'processed' StandardMaterial by sampling all textures to resolve
38
+ // the material members
39
+ var pbr_input : PbrInput ;
40
+
41
+ pbr_input . material. base_color = output_color ;
42
+ pbr_input . material. reflectance = material . reflectance;
43
+ pbr_input . material. flags = material . flags;
44
+ pbr_input . material. alpha_cutoff = material . alpha_cutoff;
45
+
36
46
// TODO use .a for exposure compensation in HDR
37
47
var emissive : vec4 <f32 > = material . emissive;
38
48
if ((material . flags & STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u ) {
39
49
emissive = vec4 <f32 >(emissive . rgb * textureSample (emissive_texture , emissive_sampler , in . uv). rgb, 1.0 );
40
50
}
51
+ pbr_input . material. emissive = emissive ;
41
52
42
- // calculate non-linear roughness from linear perceptualRoughness
43
53
var metallic : f32 = material . metallic;
44
54
var perceptual_roughness : f32 = material . perceptual_roughness;
45
55
if ((material . flags & STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u ) {
@@ -48,158 +58,34 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
48
58
metallic = metallic * metallic_roughness . b;
49
59
perceptual_roughness = perceptual_roughness * metallic_roughness . g;
50
60
}
51
- let roughness = perceptualRoughnessToRoughness (perceptual_roughness );
61
+ pbr_input . material. metallic = metallic ;
62
+ pbr_input . material. perceptual_roughness = perceptual_roughness ;
52
63
53
64
var occlusion : f32 = 1.0 ;
54
65
if ((material . flags & STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u ) {
55
66
occlusion = textureSample (occlusion_texture , occlusion_sampler , in . uv). r;
56
67
}
68
+ pbr_input . occlusion = occlusion ;
57
69
58
- var N : vec3 <f32 > = normalize (in . world_normal);
59
-
60
- #ifdef VERTEX_TANGENTS
61
- #ifdef STANDARDMATERIAL_NORMAL_MAP
62
- // NOTE: The mikktspace method of normal mapping explicitly requires that these NOT be
63
- // normalized nor any Gram-Schmidt applied to ensure the vertex normal is orthogonal to the
64
- // vertex tangent! Do not change this code unless you really know what you are doing.
65
- // http://www.mikktspace.com/
66
- var T : vec3 <f32 > = in . world_tangent. xyz;
67
- var B : vec3 <f32 > = in . world_tangent. w * cross (N , T );
68
- #endif
69
- #endif
70
+ pbr_input . frag_coord = in . frag_coord;
71
+ pbr_input . world_position = in . world_position;
72
+ pbr_input . world_normal = in . world_normal;
70
73
71
- if ((material . flags & STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u ) {
72
- if (! in . is_front) {
73
- N = - N ;
74
- #ifdef VERTEX_TANGENTS
75
- #ifdef STANDARDMATERIAL_NORMAL_MAP
76
- T = - T ;
77
- B = - B ;
78
- #endif
79
- #endif
80
- }
81
- }
74
+ pbr_input . is_orthographic = view . projection[3 ]. w == 1.0 ;
82
75
76
+ pbr_input . N = prepare_normal (
77
+ in . world_normal,
83
78
#ifdef VERTEX_TANGENTS
84
79
#ifdef STANDARDMATERIAL_NORMAL_MAP
85
- let TBN = mat3x3 <f32 >(T , B , N );
86
- // Nt is the tangent-space normal.
87
- var Nt : vec3 <f32 >;
88
- if ((material . flags & STANDARD_MATERIAL_FLAGS_TWO_COMPONENT_NORMAL_MAP) != 0u ) {
89
- // Only use the xy components and derive z for 2-component normal maps.
90
- Nt = vec3 <f32 >(textureSample (normal_map_texture , normal_map_sampler , in . uv). rg * 2.0 - 1.0 , 0.0 );
91
- Nt . z = sqrt (1.0 - Nt . x * Nt . x - Nt . y * Nt . y);
92
- } else {
93
- Nt = textureSample (normal_map_texture , normal_map_sampler , in . uv). rgb * 2.0 - 1.0 ;
94
- }
95
- // Normal maps authored for DirectX require flipping the y component
96
- if ((material . flags & STANDARD_MATERIAL_FLAGS_FLIP_NORMAL_MAP_Y) != 0u ) {
97
- Nt . y = - Nt . y;
98
- }
99
- // NOTE: The mikktspace method of normal mapping applies maps the tangent-space normal from
100
- // the normal map texture in this way to be an EXACT inverse of how the normal map baker
101
- // calculates the normal maps so there is no error introduced. Do not change this code
102
- // unless you really know what you are doing.
103
- // http://www.mikktspace.com/
104
- N = normalize (Nt . x * T + Nt . y * B + Nt . z * N );
80
+ in . world_tangent,
105
81
#endif
106
82
#endif
107
-
108
- if ((material . flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE) != 0u ) {
109
- // NOTE: If rendering as opaque, alpha should be ignored so set to 1.0
110
- output_color . a = 1.0 ;
111
- } else if ((material . flags & STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK) != 0u ) {
112
- if (output_color . a >= material . alpha_cutoff) {
113
- // NOTE: If rendering as masked alpha and >= the cutoff, render as fully opaque
114
- output_color . a = 1.0 ;
115
- } else {
116
- // NOTE: output_color.a < material.alpha_cutoff should not is not rendered
117
- // NOTE: This and any other discards mean that early-z testing cannot be done!
118
- discard ;
119
- }
120
- }
121
-
122
- var V : vec3 <f32 >;
123
- // If the projection is not orthographic
124
- let is_orthographic = view . projection[3 ]. w == 1.0 ;
125
- if (is_orthographic ) {
126
- // Orthographic view vector
127
- V = normalize (vec3 <f32 >(view . view_proj[0 ]. z, view . view_proj[1 ]. z, view . view_proj[2 ]. z));
128
- } else {
129
- // Only valid for a perpective projection
130
- V = normalize (view . world_position. xyz - in . world_position. xyz);
131
- }
132
-
133
- // Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886"
134
- let NdotV = max (dot (N , V ), 0.0001 );
135
-
136
- // Remapping [0,1] reflectance to F0
137
- // See https://google.github.io/filament/Filament.html#materialsystem/parameterization/remapping
138
- let reflectance = material . reflectance;
139
- let F0 = 0.16 * reflectance * reflectance * (1.0 - metallic ) + output_color . rgb * metallic ;
140
-
141
- // Diffuse strength inversely related to metallicity
142
- let diffuse_color = output_color . rgb * (1.0 - metallic );
143
-
144
- let R = reflect (- V , N );
145
-
146
- // accumulate color
147
- var light_accum : vec3 <f32 > = vec3 <f32 >(0.0 );
148
-
149
- let view_z = dot (vec4 <f32 >(
150
- view . inverse_view[0 ]. z,
151
- view . inverse_view[1 ]. z,
152
- view . inverse_view[2 ]. z,
153
- view . inverse_view[3 ]. z
154
- ), in . world_position);
155
- let cluster_index = fragment_cluster_index (in . frag_coord. xy, view_z , is_orthographic );
156
- let offset_and_count = unpack_offset_and_count (cluster_index );
157
- for (var i : u32 = offset_and_count [0 ]; i < offset_and_count [0 ] + offset_and_count [1 ]; i = i + 1u ) {
158
- let light_id = get_light_id (i );
159
- let light = point_lights . data[light_id ];
160
- var shadow : f32 = 1.0 ;
161
- if ((mesh . flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
162
- && (light . flags & POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u ) {
163
- shadow = fetch_point_shadow (light_id , in . world_position, in . world_normal);
164
- }
165
- let light_contrib = point_light (in . world_position. xyz, light , roughness , NdotV , N , V , R , F0 , diffuse_color );
166
- light_accum = light_accum + light_contrib * shadow ;
167
- }
168
-
169
- let n_directional_lights = lights . n_directional_lights;
170
- for (var i : u32 = 0u ; i < n_directional_lights ; i = i + 1u ) {
171
- let light = lights . directional_lights[i ];
172
- var shadow : f32 = 1.0 ;
173
- if ((mesh . flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
174
- && (light . flags & DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u ) {
175
- shadow = fetch_directional_shadow (i , in . world_position, in . world_normal);
176
- }
177
- let light_contrib = directional_light (light , roughness , NdotV , N , V , R , F0 , diffuse_color );
178
- light_accum = light_accum + light_contrib * shadow ;
179
- }
180
-
181
- let diffuse_ambient = EnvBRDFApprox (diffuse_color , 1.0 , NdotV );
182
- let specular_ambient = EnvBRDFApprox (F0 , perceptual_roughness , NdotV );
183
-
184
- output_color = vec4 <f32 >(
185
- light_accum +
186
- (diffuse_ambient + specular_ambient ) * lights . ambient_color. rgb * occlusion +
187
- emissive . rgb * output_color . a,
188
- output_color . a);
189
-
190
- output_color = cluster_debug_visualization (
191
- output_color ,
192
- view_z ,
193
- is_orthographic ,
194
- offset_and_count ,
195
- cluster_index ,
83
+ in . uv,
84
+ in . is_front,
196
85
);
86
+ pbr_input . V = calculate_view (in . world_position, pbr_input . is_orthographic);
197
87
198
- // tone_mapping
199
- output_color = vec4 <f32 >(reinhard_luminance (output_color . rgb), output_color . a);
200
- // Gamma correction.
201
- // Not needed with sRGB buffer
202
- // output_color.rgb = pow(output_color.rgb, vec3(1.0 / 2.2));
88
+ output_color = pbr (pbr_input );
203
89
}
204
90
205
91
return output_color ;
0 commit comments