#import bevy_pbr::mesh_view_bindings #import bevy_pbr::pbr_bindings #import bevy_pbr::mesh_bindings #import bevy_pbr::utils #import bevy_pbr::clustered_forward #import bevy_pbr::lighting #import bevy_pbr::pbr_ambient #import bevy_pbr::shadows #import bevy_pbr::fog #import bevy_pbr::pbr_functions #import bevy_pbr::parallax_mapping #import bevy_pbr::prepass_utils #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION #import bevy_pbr::gtao_utils #endif struct FragmentInput { @builtin(front_facing) is_front: bool, @builtin(position) frag_coord: vec4, #import bevy_pbr::mesh_vertex_output }; @fragment fn fragment(in: FragmentInput) -> @location(0) vec4 { let is_orthographic = view.projection[3].w == 1.0; let V = calculate_view(in.world_position, is_orthographic); #ifdef VERTEX_UVS var uv = in.uv; #ifdef VERTEX_TANGENTS if ((material.flags & STANDARD_MATERIAL_FLAGS_DEPTH_MAP_BIT) != 0u) { let N = in.world_normal; let T = in.world_tangent.xyz; let B = in.world_tangent.w * cross(N, T); // Transform V from fragment to camera in world space to tangent space. let Vt = vec3(dot(V, T), dot(V, B), dot(V, N)); uv = parallaxed_uv( material.parallax_depth_scale, material.max_parallax_layer_count, material.max_relief_mapping_search_steps, uv, // Flip the direction of Vt to go toward the surface to make the // parallax mapping algorithm easier to understand and reason // about. -Vt, ); } #endif #endif var output_color: vec4 = material.base_color; #ifdef VERTEX_COLORS output_color = output_color * in.color; #endif #ifdef VERTEX_UVS if ((material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) { output_color = output_color * textureSample(base_color_texture, base_color_sampler, uv); } #endif // NOTE: Unlit bit not set means == 0 is true, so the true case is if lit if ((material.flags & STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) { // Prepare a 'processed' StandardMaterial by sampling all textures to resolve // the material members var pbr_input: PbrInput; pbr_input.material.base_color = output_color; pbr_input.material.reflectance = material.reflectance; pbr_input.material.flags = material.flags; pbr_input.material.alpha_cutoff = material.alpha_cutoff; // TODO use .a for exposure compensation in HDR var emissive: vec4 = material.emissive; #ifdef VERTEX_UVS if ((material.flags & STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) { emissive = vec4(emissive.rgb * textureSample(emissive_texture, emissive_sampler, uv).rgb, 1.0); } #endif pbr_input.material.emissive = emissive; var metallic: f32 = material.metallic; var perceptual_roughness: f32 = material.perceptual_roughness; #ifdef VERTEX_UVS if ((material.flags & STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) { let metallic_roughness = textureSample(metallic_roughness_texture, metallic_roughness_sampler, uv); // Sampling from GLTF standard channels for now metallic = metallic * metallic_roughness.b; perceptual_roughness = perceptual_roughness * metallic_roughness.g; } #endif pbr_input.material.metallic = metallic; pbr_input.material.perceptual_roughness = perceptual_roughness; // TODO: Split into diffuse/specular occlusion? var occlusion: vec3 = vec3(1.0); #ifdef VERTEX_UVS if ((material.flags & STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) { occlusion = vec3(textureSample(occlusion_texture, occlusion_sampler, in.uv).r); } #endif #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION let ssao = textureLoad(screen_space_ambient_occlusion_texture, vec2(in.frag_coord.xy), 0i).r; let ssao_multibounce = gtao_multibounce(ssao, pbr_input.material.base_color.rgb); occlusion = min(occlusion, ssao_multibounce); #endif pbr_input.occlusion = occlusion; pbr_input.frag_coord = in.frag_coord; pbr_input.world_position = in.world_position; #ifdef LOAD_PREPASS_NORMALS pbr_input.world_normal = prepass_normal(in.frag_coord, 0u); #else // LOAD_PREPASS_NORMALS pbr_input.world_normal = prepare_world_normal( in.world_normal, (material.flags & STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u, in.is_front, ); #endif // LOAD_PREPASS_NORMALS pbr_input.is_orthographic = is_orthographic; pbr_input.N = apply_normal_mapping( material.flags, pbr_input.world_normal, #ifdef VERTEX_TANGENTS #ifdef STANDARDMATERIAL_NORMAL_MAP in.world_tangent, #endif #endif #ifdef VERTEX_UVS uv, #endif ); pbr_input.V = V; pbr_input.occlusion = occlusion; pbr_input.flags = mesh.flags; output_color = pbr(pbr_input); } else { output_color = alpha_discard(material, output_color); } // fog if (fog.mode != FOG_MODE_OFF && (material.flags & STANDARD_MATERIAL_FLAGS_FOG_ENABLED_BIT) != 0u) { output_color = apply_fog(fog, output_color, in.world_position.xyz, view.world_position.xyz); } #ifdef TONEMAP_IN_SHADER output_color = tone_mapping(output_color); #ifdef DEBAND_DITHER var output_rgb = output_color.rgb; output_rgb = powsafe(output_rgb, 1.0 / 2.2); output_rgb = output_rgb + screen_space_dither(in.frag_coord.xy); // This conversion back to linear space is required because our output texture format is // SRGB; the GPU will assume our output is linear and will apply an SRGB conversion. output_rgb = powsafe(output_rgb, 2.2); output_color = vec4(output_rgb, output_color.a); #endif #endif #ifdef PREMULTIPLY_ALPHA output_color = premultiply_alpha(material.flags, output_color); #endif return output_color; }