#ifndef __PBR_INC #define __PBR_INC #include "aperiodic_tiling.cginc" #include "burley.cginc" #include "custom31.cginc" #include "data.cginc" #include "decal.cginc" #include "filamented.cginc" #include "instancing.cginc" #include "interpolators.cginc" #include "letter_grid.cginc" #include "texture_utils.cginc" #include "triplanar.cginc" #if defined(_PARALLAX_HEIGHTMAP_TEXTURE) || defined(_BURLEY_TILING_HEIGHTMAP) float heightmap_sample(float2 uv) { #if defined(_BURLEY_TILING_HEIGHTMAP) float2 delta = (uv - _burley_ctx.base_uv) * _burley_ctx.uv_scale; float4 s0 = _Burley_Tiling_Heightmap.SampleGrad(aniso4_trilinear_repeat_s, _burley_ctx.patch_0.uv + mul(_burley_ctx.patch_0.uv_to_patch, delta), _burley_ctx.patch_0.dx, _burley_ctx.patch_0.dy); float4 s1 = _Burley_Tiling_Heightmap.SampleGrad(aniso4_trilinear_repeat_s, _burley_ctx.patch_1.uv + mul(_burley_ctx.patch_1.uv_to_patch, delta), _burley_ctx.patch_1.dx, _burley_ctx.patch_1.dy); float4 s2 = _Burley_Tiling_Heightmap.SampleGrad(aniso4_trilinear_repeat_s, _burley_ctx.patch_2.uv + mul(_burley_ctx.patch_2.uv_to_patch, delta), _burley_ctx.patch_2.dx, _burley_ctx.patch_2.dy); float4 blend = s0 * _burley_ctx.weights.x + s1 * _burley_ctx.weights.y + s2 * _burley_ctx.weights.z; return burley_degaussianize( _Burley_Tiling_Heightmap_LUT, burley_apply_soft_clipping(blend.rgb, _burley_ctx.weights), false).r; #elif defined(_PARALLAX_HEIGHTMAP_TEXTURE) return _Parallax_Heightmap.Sample(linear_repeat_s, uv * _Parallax_Heightmap_ST.xy + _Parallax_Heightmap_ST.zw).r; #endif } #endif // _PARALLAX_HEIGHTMAP_TEXTURE || _BURLEY_TILING_HEIGHTMAP #if defined(_PARALLAX_HEIGHTMAP) float2 parallax_offset(float2 uv, float3 view_dir_world, float3x3 tbn) { float3 view_dir_tangent = mul(tbn, view_dir_world); float view_z = max(view_dir_tangent.z, 1e-3f); float2 uv_step = view_dir_tangent.xy / view_z * _Parallax_Heightmap_Scale; #if defined(_PARALLAX_HEIGHTMAP_RAY_MARCHING) // Adapt steps by angle to keep cost down while preserving glancing detail. float angle = saturate(view_z); float base_steps = _Parallax_Heightmap_Ray_Marching_Steps; float step_count = lerp(base_steps * 1.5, base_steps * 0.75, angle); int step_count_i = (int)ceil(clamp(step_count, 2.0, max(base_steps, 2.0))); float inv_step_count = rcp((float)step_count_i); float2 delta_uv = uv_step * inv_step_count; float delta_layer = inv_step_count; float2 cur_uv = uv - uv_step * _Parallax_Heightmap_Bias; float2 prev_uv = cur_uv; float cur_layer = 0.0; float sampled_height = heightmap_sample(cur_uv); float prev_layer = cur_layer; [loop] for (int i = 0; i < step_count_i; i++) { if (cur_layer >= sampled_height) break; prev_layer = cur_layer; prev_uv = cur_uv; cur_uv += delta_uv; cur_layer += delta_layer; sampled_height = heightmap_sample(cur_uv); } // Short binary refine between last two samples to tighten the hit float2 low_uv = prev_uv; float low_layer = prev_layer; float2 high_uv = cur_uv; float high_layer = cur_layer; [unroll(2)] for (int j = 0; j < 2; j++) { float2 mid_uv = 0.5 * (low_uv + high_uv); float mid_layer = 0.5 * (low_layer + high_layer); float mid_height = heightmap_sample(mid_uv); if (mid_layer < mid_height) { low_uv = mid_uv; low_layer = mid_layer; } else { high_uv = mid_uv; high_layer = mid_layer; } } float2 refine_uv = 0.5 * (low_uv + high_uv); return refine_uv - uv; #else float height = heightmap_sample(uv) - _Parallax_Heightmap_Bias; return uv_step * height; #endif } #endif // _PARALLAX_HEIGHTMAP // Tokuyoshi and Kaplanyan 2019 "Improved Geometric Specular Antialiasing" float normalFiltering(float3 geometric_normal, float perceptual_roughness) { float3 du = ddx(geometric_normal); float3 dv = ddy(geometric_normal); float variance = _Specular_AA_Variance * (dot(du, du) + dot(dv, dv)); float roughness = perceptual_roughness * perceptual_roughness; float kernel_roughness = min(2.0f * variance, _Specular_AA_Threshold); float square_roughness = saturate(roughness * roughness + kernel_roughness); return saturate(sqrt(sqrt(square_roughness))); } void propagateSmoothness(inout Pbr pbr) { pbr.smoothness = 1.0f - normalFiltering(pbr.geometric_normal, 1.0f - pbr.smoothness); pbr.roughness_perceptual = clamp(1.0f - pbr.smoothness, MIN_PERCEPTUAL_ROUGHNESS, 1); pbr.roughness = clamp(pbr.roughness_perceptual * pbr.roughness_perceptual, MIN_ROUGHNESS, 1); #if defined(_CLEARCOAT) pbr.cc_roughness_perceptual = clamp(pbr.cc_roughness_perceptual, MIN_PERCEPTUAL_ROUGHNESS, 1); pbr.cc_roughness_perceptual = normalFiltering(pbr.geometric_normal, pbr.cc_roughness_perceptual); pbr.cc_roughness = max(MIN_ROUGHNESS, pbr.cc_roughness_perceptual * pbr.cc_roughness_perceptual); #endif } void apply_marble(float3 world_pos, inout float3 albedo) { #if defined(_MARBLE) float3 uvw = world_pos * _Marble_Scale; float3 offset = 0; #if defined(_MARBLE_TIME) offset += _Time[0] * _Marble_Speed * _Marble_Direction; #endif #if defined(_MARBLE_OFFSET) offset += _Marble_Offset; #endif uvw += offset; float3 noise = domain_warp_procedural(uvw, _Marble_Strength, _Marble_Octaves, _Marble_Lacunarity, _Marble_Gain); noise = _Marble_Post_Ramp.Sample(linear_clamp_s, float2(noise.x, 0)); albedo = noise; #endif } void apply_kintsugi(float3 world_pos, inout float3 albedo, inout float smoothness, inout float metallic) { #if defined(_KINTSUGI) float3 uvw = world_pos * _Kintsugi_Scale; #if defined(_KINTSUGI_DOMAIN_WARPING) float3 warp = domain_warp_procedural( world_pos * _Kintsugi_Domain_Warping_Scale, _Kintsugi_Domain_Warping_Strength, _Kintsugi_Domain_Warping_Octaves, _Kintsugi_Domain_Warping_Lacunarity, _Kintsugi_Domain_Warping_Gain); uvw += warp; #endif float mask = 1.0f - voronoi_d_3d(uvw); float width = max(fwidth(mask) * 0.5f, _Kintsugi_Width); float threshold = _Kintsugi_Threshold; mask = smoothstep(threshold - width, threshold + width, mask); #if defined(_KINTSUGI_NOISE_INVERT) mask = 1.0f - mask; #endif // _KINTSUGI_NOISE_INVERT albedo = lerp(albedo, _Kintsugi_Color, mask); smoothness = lerp(smoothness, _Kintsugi_Smoothness, mask); metallic = lerp(metallic, _Kintsugi_Metallic, mask); #endif // _KINTSUGI } void apply_letter_grid(v2f i, inout Pbr pbr) { #if defined(_LETTER_GRID) LetterGridOutput lg = LetterGrid(i); pbr.albedo.rgb = lerp(pbr.albedo.rgb, lg.albedo, lg.albedo.a); pbr.metallic = lerp(pbr.metallic, lg.metallic, lg.albedo.a); pbr.roughness = lerp(pbr.roughness, lg.roughness, lg.albedo.a); #if defined(FORWARD_BASE_PASS) pbr.emission += lg.emission * lg.albedo.a; #endif #endif } void apply_burley_tiling(inout Pbr pbr, inout float3 normal_tangent) { #if defined(_BURLEY_TILING) float3 weights = _burley_ctx.weights; #if defined(_BURLEY_TILING_MAINTEX) float4 patch_0 = burley_sample_patch(_Burley_Tiling_Maintex, _burley_ctx.patch_0); float4 patch_1 = burley_sample_patch(_Burley_Tiling_Maintex, _burley_ctx.patch_1); float4 patch_2 = burley_sample_patch(_Burley_Tiling_Maintex, _burley_ctx.patch_2); float4 gaussian_blend = patch_0 * weights.x + patch_1 * weights.y + patch_2 * weights.z; pbr.albedo.xyz = burley_degaussianize(_Burley_Tiling_Maintex_LUT, burley_apply_soft_clipping(gaussian_blend.rgb, weights)); #endif // _BURLEY_TILING_MAINTEX #if defined(_BURLEY_TILING_SMOOTHNESS) float4 patch_0_smoothness = burley_sample_patch(_Burley_Tiling_Smoothness_Map, _burley_ctx.patch_0); float4 patch_1_smoothness = burley_sample_patch(_Burley_Tiling_Smoothness_Map, _burley_ctx.patch_1); float4 patch_2_smoothness = burley_sample_patch(_Burley_Tiling_Smoothness_Map, _burley_ctx.patch_2); float4 smoothness_blend = patch_0_smoothness * weights.x + patch_1_smoothness * weights.y + patch_2_smoothness * weights.z; pbr.smoothness = burley_degaussianize(_Burley_Tiling_Smoothness_Map_LUT, burley_apply_soft_clipping(smoothness_blend, weights)).r; #if defined(_BURLEY_TILING_SMOOTHNESS_INVERT) pbr.smoothness = 1.0f - pbr.smoothness; #endif // _BURLEY_TILING_SMOOTHNESS_INVERT #endif // _BURLEY_TILING_SMOOTHNESS #if defined(_BURLEY_TILING_NORMAL) // TODO whiteout blending? float4 patch_0_normal = burley_sample_patch(_Burley_Tiling_Normal_Map, _burley_ctx.patch_0); float4 patch_1_normal = burley_sample_patch(_Burley_Tiling_Normal_Map, _burley_ctx.patch_1); float4 patch_2_normal = burley_sample_patch(_Burley_Tiling_Normal_Map, _burley_ctx.patch_2); float4 normal_blend = patch_0_normal * weights.x + patch_1_normal * weights.y + patch_2_normal * weights.z; normal_tangent = burley_degaussianize(_Burley_Tiling_Normal_Map_LUT, burley_apply_soft_clipping(normal_blend.rgb, weights), false); normal_tangent.xy = normal_tangent.xy * 2 - 1; normal_tangent.xy *= _Burley_Tiling_Normal_Strength; normal_tangent = normalize(normal_tangent); #endif // _BURLEY_TILING_NORMAL #endif // _BURLEY_TILING } void apply_zebra(float2 uv, inout float3 albedo) { #if defined(_ZEBRA) //float theta = 0.25 * PI; //float theta = _Time[1]; float theta = _Zebra_Angle * TAU; uv.x *= _Zebra_X_Scale; float st, ct; sincos(theta, st, ct); float4 phacelle = phacelle_noise_2d(uv * _Zebra_Scale, float2(ct, st)); float2 dir = phacelle.xy; float2 ortho_dir = phacelle.zw; float mask = dot(dir, float2(0, 1)); float mask_fw = fwidth(mask); albedo = smoothstep(-mask_fw * 0.5, mask_fw * 0.5, mask); #endif } Pbr getPbr(v2f i) { Pbr pbr = (Pbr) 0; float3 n = i.normal; pbr.geometric_normal = n; float3 t = i.tangent.xyz; t = normalize(t - n * dot(n, t)); // Gram-Schmidt to avoid skew float3 b = normalize(cross(n, t)) * i.tangent.w; pbr.tbn = float3x3(t, b, n); #if defined(_UV_SCROLL) i.uv01.xy += getTime() * _UV_Scroll_Speed; #endif // _UV_SCROLL #if defined(_BURLEY_TILING) burley_tiling_setup(i.uv01.xy); #endif #if defined(_PARALLAX_HEIGHTMAP) float2 uv_parallax = i.uv01.xy + parallax_offset(i.uv01.xy, normalize(-i.eyeVec.xyz), pbr.tbn); #else float2 uv_parallax = i.uv01.xy; #endif // _PARALLAX_HEIGHTMAP #if defined(_BURLEY_TILING) burley_tiling_setup(uv_parallax); #endif #if defined(_PARALLAX_HEIGHTMAP) || defined(_BURLEY_TILING_HEIGHTMAP) pbr.height = heightmap_sample(uv_parallax); #endif #if defined(OUTLINES_PASS) && defined(_OUTLINES) pbr.albedo = _Outlines_Color; #else pbr.albedo = _MainTex.Sample(aniso4_trilinear_repeat_s, uv_parallax * _MainTex_ST.xy + _MainTex_ST.zw); pbr.albedo *= _Color; #endif float3 normal_tangent = UnpackNormal(_BumpMap.Sample(aniso4_trilinear_repeat_s, uv_parallax * _BumpMap_ST.xy)); normal_tangent.xy *= _BumpScale; #if defined(_DETAILS) float2 detail_uv = get_uv_by_channel(i, _Details_UV_Channel); float3 detail_normal = UnpackNormal(_DetailNormalMap.Sample(aniso4_trilinear_repeat_s, detail_uv * _DetailNormalMap_ST.xy)); detail_normal.xy *= _DetailNormalMapScale; float detail_mask = _DetailMask.Sample(aniso4_trilinear_repeat_s, detail_uv * _DetailMask_ST.xy).r; detail_normal.xy *= detail_mask; normal_tangent = blendNormalsHill12(normal_tangent, detail_normal); #endif float4 metallic_gloss = _MetallicGlossMap.Sample(aniso4_trilinear_repeat_s, uv_parallax * _MetallicGlossMap_ST.xy); pbr.smoothness = metallic_gloss.a * _Glossiness; pbr.metallic = metallic_gloss.r * _Metallic; apply_marble(i.worldPos, pbr.albedo.xyz); apply_kintsugi(i.worldPos, pbr.albedo.xyz, pbr.smoothness, pbr.metallic); apply_letter_grid(i, pbr); apply_burley_tiling(pbr, normal_tangent); apply_triplanar_layers(i.worldPos, i.normal, pbr, normal_tangent); apply_custom31_world(i, pbr, normal_tangent); apply_aperiodic_tiling(i.uv01.xy, pbr.albedo.xyz, pbr.smoothness, normal_tangent); apply_zebra(i.uv01.xy, pbr.albedo.xyz); applyDecals(i, pbr, normal_tangent); pbr.normal = normalize(mul(normal_tangent, pbr.tbn)); #if defined(_DEBUG_VIEW_TANGENT_SPACE_NORMALS) pbr.tangent_normal = normal_tangent; #endif #if defined(_BENT_NORMALS) float3 bent_ts = UnpackNormal(_Bent_Normals_Map.Sample( aniso4_trilinear_repeat_s, uv_parallax * _Bent_Normals_Map_ST.xy + _Bent_Normals_Map_ST.zw)); pbr.bent_normal = normalize(mul(bent_ts, pbr.tbn)); #endif #if defined(_CLEARCOAT) pbr.cc_roughness = _Clearcoat_Roughness; pbr.cc_roughness_perceptual = sqrt(pbr.cc_roughness); pbr.cc_strength = _Clearcoat_Strength; #if defined(_CLEARCOAT_NORMALS) float3 cc_normal_ts = UnpackNormal(_Clearcoat_Normals.Sample( aniso4_trilinear_repeat_s, uv_parallax * _Clearcoat_Normals_ST.xy + _Clearcoat_Normals_ST.zw)); cc_normal_ts.xy *= _Clearcoat_Normals_Strength; pbr.cc_normal = normalize(mul(cc_normal_ts, pbr.tbn)); #else pbr.cc_normal = i.normal; #endif // _CLEARCOAT_NORMALS #endif // _CLEARCOAT propagateSmoothness(pbr); #if defined(_EMISSIONS) && defined(FORWARD_BASE_PASS) float3 emission_tint = _EmissionColor; float3 emission_color = _EmissionMap.Sample(trilinear_repeat_s, i.uv01.xy * _EmissionMap_ST.xy + _EmissionMap_ST.zw); float emission_mask = _EmissionMask.Sample(trilinear_repeat_s, i.uv01.xy * _EmissionMask_ST.xy + _EmissionMask_ST.zw).r; pbr.emission = emission_tint * emission_color * emission_mask; #endif return pbr; } #endif // __PBR_INC