#define_import_path bevy_pbr::pbr_fragment #import bevy_pbr::{ pbr_functions, pbr_bindings, pbr_types, prepass_utils, mesh_bindings::mesh, mesh_view_bindings::view, parallax_mapping::parallaxed_uv, lightmap::lightmap, } #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION #import bevy_pbr::mesh_view_bindings::screen_space_ambient_occlusion_texture #import bevy_pbr::gtao_utils::gtao_multibounce #endif #ifdef PREPASS_PIPELINE #import bevy_pbr::prepass_io::VertexOutput #else #import bevy_pbr::forward_io::VertexOutput #endif // prepare a basic PbrInput from the vertex stage output, mesh binding and view binding fn pbr_input_from_vertex_output( in: VertexOutput, is_front: bool, double_sided: bool, ) -> pbr_types::PbrInput { var pbr_input: pbr_types::PbrInput = pbr_types::pbr_input_new(); pbr_input.flags = mesh[in.instance_index].flags; pbr_input.is_orthographic = view.projection[3].w == 1.0; pbr_input.V = pbr_functions::calculate_view(in.world_position, pbr_input.is_orthographic); pbr_input.frag_coord = in.position; pbr_input.world_position = in.world_position; #ifdef VERTEX_COLORS pbr_input.material.base_color = in.color; #endif pbr_input.world_normal = pbr_functions::prepare_world_normal( in.world_normal, double_sided, is_front, ); #ifdef LOAD_PREPASS_NORMALS pbr_input.N = prepass_utils::prepass_normal(in.position, 0u); #else pbr_input.N = normalize(pbr_input.world_normal); #endif return pbr_input; } // Prepare a full PbrInput by sampling all textures to resolve // the material members fn pbr_input_from_standard_material( in: VertexOutput, is_front: bool, ) -> pbr_types::PbrInput { let double_sided = (pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u; var pbr_input: pbr_types::PbrInput = pbr_input_from_vertex_output(in, is_front, double_sided); pbr_input.material.flags = pbr_bindings::material.flags; pbr_input.material.base_color *= pbr_bindings::material.base_color; pbr_input.material.deferred_lighting_pass_id = pbr_bindings::material.deferred_lighting_pass_id; #ifdef VERTEX_UVS var uv = in.uv; #ifdef VERTEX_TANGENTS if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_DEPTH_MAP_BIT) != 0u) { let V = pbr_input.V; 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( pbr_bindings::material.parallax_depth_scale, pbr_bindings::material.max_parallax_layer_count, pbr_bindings::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 // VERTEX_TANGENTS if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) { pbr_input.material.base_color *= textureSampleBias(pbr_bindings::base_color_texture, pbr_bindings::base_color_sampler, uv, view.mip_bias); } #endif // VERTEX_UVS pbr_input.material.flags = pbr_bindings::material.flags; // NOTE: Unlit bit not set means == 0 is true, so the true case is if lit if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) { pbr_input.material.reflectance = pbr_bindings::material.reflectance; pbr_input.material.ior = pbr_bindings::material.ior; pbr_input.material.attenuation_color = pbr_bindings::material.attenuation_color; pbr_input.material.attenuation_distance = pbr_bindings::material.attenuation_distance; pbr_input.material.alpha_cutoff = pbr_bindings::material.alpha_cutoff; // emissive // TODO use .a for exposure compensation in HDR var emissive: vec4 = pbr_bindings::material.emissive; #ifdef VERTEX_UVS if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) { emissive = vec4(emissive.rgb * textureSampleBias(pbr_bindings::emissive_texture, pbr_bindings::emissive_sampler, uv, view.mip_bias).rgb, 1.0); } #endif pbr_input.material.emissive = emissive; // metallic and perceptual roughness var metallic: f32 = pbr_bindings::material.metallic; var perceptual_roughness: f32 = pbr_bindings::material.perceptual_roughness; #ifdef VERTEX_UVS if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) { let metallic_roughness = textureSampleBias(pbr_bindings::metallic_roughness_texture, pbr_bindings::metallic_roughness_sampler, uv, view.mip_bias); // Sampling from GLTF standard channels for now metallic *= metallic_roughness.b; perceptual_roughness *= metallic_roughness.g; } #endif pbr_input.material.metallic = metallic; pbr_input.material.perceptual_roughness = perceptual_roughness; var specular_transmission: f32 = pbr_bindings::material.specular_transmission; #ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_SPECULAR_TRANSMISSION_TEXTURE_BIT) != 0u) { specular_transmission *= textureSample(pbr_bindings::specular_transmission_texture, pbr_bindings::specular_transmission_sampler, uv).r; } #endif pbr_input.material.specular_transmission = specular_transmission; var thickness: f32 = pbr_bindings::material.thickness; #ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_THICKNESS_TEXTURE_BIT) != 0u) { thickness *= textureSample(pbr_bindings::thickness_texture, pbr_bindings::thickness_sampler, uv).g; } #endif // scale thickness, accounting for non-uniform scaling (e.g. a “squished” mesh) thickness *= length( (transpose(mesh[in.instance_index].model) * vec4(pbr_input.N, 0.0)).xyz ); pbr_input.material.thickness = thickness; var diffuse_transmission = pbr_bindings::material.diffuse_transmission; #ifdef PBR_TRANSMISSION_TEXTURES_SUPPORTED if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_DIFFUSE_TRANSMISSION_TEXTURE_BIT) != 0u) { diffuse_transmission *= textureSample(pbr_bindings::diffuse_transmission_texture, pbr_bindings::diffuse_transmission_sampler, uv).a; } #endif pbr_input.material.diffuse_transmission = diffuse_transmission; // occlusion // TODO: Split into diffuse/specular occlusion? var occlusion: vec3 = vec3(1.0); #ifdef VERTEX_UVS if ((pbr_bindings::material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) { occlusion = vec3(textureSampleBias(pbr_bindings::occlusion_texture, pbr_bindings::occlusion_sampler, uv, view.mip_bias).r); } #endif #ifdef SCREEN_SPACE_AMBIENT_OCCLUSION let ssao = textureLoad(screen_space_ambient_occlusion_texture, vec2(in.position.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; // N (normal vector) #ifndef LOAD_PREPASS_NORMALS pbr_input.N = pbr_functions::apply_normal_mapping( pbr_bindings::material.flags, pbr_input.world_normal, double_sided, is_front, #ifdef VERTEX_TANGENTS #ifdef STANDARDMATERIAL_NORMAL_MAP in.world_tangent, #endif #endif #ifdef VERTEX_UVS uv, #endif view.mip_bias, ); #endif #ifdef LIGHTMAP pbr_input.lightmap_light = lightmap( in.uv_b, pbr_bindings::material.lightmap_exposure, in.instance_index); #endif } return pbr_input; }