diff options
| -rw-r--r-- | 2ner.cginc | 11 | ||||
| -rw-r--r-- | 2ner.shader | 6 | ||||
| -rw-r--r-- | Scripts/gen_sdf | 51 | ||||
| -rw-r--r-- | features.cginc | 4 | ||||
| -rw-r--r-- | yum_lighting.cginc | 75 | ||||
| -rw-r--r-- | yum_pbr.cginc | 1 |
6 files changed, 77 insertions, 71 deletions
@@ -313,6 +313,12 @@ float4 frag(v2f i, uint facing : SV_IsFrontFace i.uv01.xy = eye_effect_00.uv;
#endif
+ float4x4 tangentToWorld = float4x4(
+ float4(i.tangent, 0),
+ float4(i.binormal, 0),
+ float4(i.normal, 0),
+ float4(0, 0, 0, 1)
+ );
#if defined(_CUSTOM30)
#if defined(FORWARD_BASE_PASS) || (!defined(_DEPTH_PREPASS) && defined(SHADOW_CASTER_PASS))
#if defined(_CUSTOM30_BASICCUBE)
@@ -337,7 +343,7 @@ float4 frag(v2f i, uint facing : SV_IsFrontFace #endif
#endif
- float4x4 tangentToWorld = float4x4(
+ tangentToWorld = float4x4(
float4(i.tangent, 0),
float4(i.binormal, 0),
float4(i.normal, 0),
@@ -350,7 +356,8 @@ float4 frag(v2f i, uint facing : SV_IsFrontFace ssao = get_ssao(i, tangentToWorld, debug);
#endif
YumPbr pbr = GetYumPbr(i, tangentToWorld);
- pbr.ao *= ssao;
+ //pbr.ao *= ssao;
+ pbr.albedo.rgb *= ssao;
#if defined(META_PASS)
#if defined(_EMISSION)
diff --git a/2ner.shader b/2ner.shader index 2ef0b51..b9c724d 100644 --- a/2ner.shader +++ b/2ner.shader @@ -1939,6 +1939,12 @@ Shader "yum_food/2ner" [HideInInspector] m_end_Shadow_Casting("Cast shadows", Float) = 0 //endex + //ifex _Spherical_Harmonics_L1_Enabled==0 + [HideInInspector] m_start_Spherical_Harmonics_L1("L1 spherical harmonics", Float) = 0 + [ThryToggle(_SPHERICAL_HARMONICS_L1)] _Spherical_Harmonics_L1_Enabled("Enable", Float) = 0 + [HideInInspector] m_end_Spherical_Harmonics_L1("L1 spherical harmonics", Float) = 0 + //endex + //ifex _Depth_Prepass_Enabled==0 [HideInInspector] m_start_Depth_Prepass("Depth Prepass", Float) = 0 [ThryToggle(_DEPTH_PREPASS)] _Depth_Prepass_Enabled("Enable", Float) = 0 diff --git a/Scripts/gen_sdf b/Scripts/gen_sdf index 52ef487..88469dc 100644 --- a/Scripts/gen_sdf +++ b/Scripts/gen_sdf @@ -5,7 +5,7 @@ import cv2 import argparse import os -def compute_sdf(img, scale_factor): +def compute_sdf(img, n_px, bit_depth=8): # Convert to binary image if not already _, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) @@ -13,21 +13,36 @@ def compute_sdf(img, scale_factor): dist_transform_fg = cv2.distanceTransform(binary, cv2.DIST_L2, 5) dist_transform_bg = cv2.distanceTransform(255 - binary, cv2.DIST_L2, 5) - # Combine the distance fields and scale by factor - sdf = (dist_transform_fg - dist_transform_bg) / scale_factor + # Combine to get signed distance field (positive outside, negative inside) + sdf = dist_transform_fg - dist_transform_bg - # Normalize values to [0, 255] range - sdf_min = np.min(sdf) - sdf_max = np.max(sdf) - sdf = ((sdf - sdf_min) * 255 / (sdf_max - sdf_min)) + # Clip to ±n_px range + sdf = np.clip(sdf, -n_px, n_px) - return sdf.astype(np.uint8) + # Map from [-n_px, +n_px] to [0, 1] + sdf_normalized = (sdf + n_px) / (2 * n_px) + + # Quantize to requested bit depth + if bit_depth == 8: + max_value = 255 + dtype = np.uint8 + elif bit_depth == 16: + max_value = 65535 + dtype = np.uint16 + else: + raise ValueError(f"Unsupported bit depth: {bit_depth}") + + sdf_quantized = np.round(sdf_normalized * max_value).astype(dtype) + + return sdf_quantized def main(): - parser = argparse.ArgumentParser(description='Generate SDF from black and white image') + parser = argparse.ArgumentParser(description='Generate SDF from black and white image with fixed range encoding') parser.add_argument('input_images', nargs='+', help='Path to input image(s)') - parser.add_argument('--scale', type=float, default=1.0, - help='Scale factor for distance (in texels)') + parser.add_argument('--n_px', type=float, default=64.0, + help='Maximum distance to encode in pixels (default: 64)') + parser.add_argument('--bit_depth', type=int, default=8, choices=[8, 16], + help='Output bit depth (default: 8)') args = parser.parse_args() # Process each input image @@ -42,12 +57,18 @@ def main(): print(f"Error: Could not read image {input_path}") continue - # Compute SDF with scale factor - sdf = compute_sdf(img, args.scale) + # Compute SDF with fixed range + sdf = compute_sdf(img, args.n_px, args.bit_depth) # Save result - cv2.imwrite(output_path, sdf) - print(f"SDF generated and saved to {output_path}") + if args.bit_depth == 16: + # For 16-bit images, ensure proper saving + cv2.imwrite(output_path, sdf) + else: + cv2.imwrite(output_path, sdf) + + print(f"SDF generated and saved to {output_path} (±{args.n_px}px range, {args.bit_depth}-bit)") + print(f" Decoding: 0.5 = contour, 0.0 = -{args.n_px}px (inside), 1.0 = +{args.n_px}px (outside)") if __name__ == "__main__": main() diff --git a/features.cginc b/features.cginc index 1e46f48..d0c3fd1 100644 --- a/features.cginc +++ b/features.cginc @@ -380,5 +380,9 @@ #pragma shader_feature_local _BAKERY_MONOSH //endex +//ifex _Spherical_Harmonics_L1_Enabled==0 +#pragma shader_feature_local _SPHERICAL_HARMONICS_L1 +//endex + #endif // __FEATURES_INC diff --git a/yum_lighting.cginc b/yum_lighting.cginc index 9dab2d8..e769375 100644 --- a/yum_lighting.cginc +++ b/yum_lighting.cginc @@ -166,63 +166,25 @@ float3 getIndirectSpecular(v2f i, YumPbr pbr, float3 view_dir, float diffuse_lum } float3 yumSH9(float4 n, float3 worldPos, inout YumLighting light) { -#if defined(YUM_SH9_STANDARD) - // Unity gives us the first three bands (L0-L2) of SH coefficients as follows: - // unity_SHA*.w: L0 coefficients - // unity_SHA*.xyz: L1 coefficients - // unity_SHB*: first four of the L2 coefficients - // unity_SHC: last L2 coefficient - - // Parse out coefficients into a simpler but less efficient format. - float3 L00 = float3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w); - float3 L1_1 = float3(unity_SHAr.x, unity_SHAg.x, unity_SHAb.x); - float3 L10 = float3(unity_SHAr.y, unity_SHAg.y, unity_SHAb.y); - float3 L11 = float3(unity_SHAr.z, unity_SHAg.z, unity_SHAb.z); - float3 L2_2 = float3(unity_SHBr.x, unity_SHBg.x, unity_SHBb.x); - float3 L2_1 = float3(unity_SHBr.y, unity_SHBg.y, unity_SHBb.y); - float3 L20 = float3(unity_SHBr.z, unity_SHBg.z, unity_SHBb.z); - float3 L21 = float3(unity_SHBr.w, unity_SHBg.w, unity_SHBb.w); - float3 L22 = unity_SHC; - - // Equation 13 from "An Efficient Representation for Irradiance Environment - // Maps" by Ramamoorthi and Hanrahan. Note that the order of some - // coefficients is different, and normalization constants have been - // premultiplied by Unity. - float3 L0 = L00; - float3 L1 = L1_1 * n.x + L10 * n.y + L11 * n.z; - float3 L2 = - L2_2 * n.x * n.y + - L2_1 * n.y * n.z + - L20 * n.z * n.z + - L21 * n.x * n.z + - L22 * (n.x * n.x - n.y * n.y); - - light.L00 = L00; - light.L01r = unity_SHAr.xyz; - light.L01g = unity_SHAg.xyz; - light.L01b = unity_SHAb.xyz; - - return L0 + L1 + L2; -#elif 1 - // Light volumes. We omit the L01 contribution since flat shading looks - // better on avatars. LightVolumeSH(worldPos, light.L00, light.L01r, light.L01g, light.L01b); - return LightVolumeEvaluate(n.xyz, light.L00, - _UdonLightVolumeEnabled ? light.L01r : 0, - _UdonLightVolumeEnabled ? light.L01g : 0, - _UdonLightVolumeEnabled ? light.L01b : 0); +#if defined(_SPHERICAL_HARMONICS_L1) + return LightVolumeEvaluate(n.xyz, light.L00, light.L01r, light.L01g, light.L01b); #else - // On non-photorealistic avatars, simply using the diffuse component looks - // better. *shruge* - float3 L00 = float3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w); - - light.L00 = L00; - light.L01r = unity_SHAr.xyz; - light.L01g = unity_SHAg.xyz; - light.L01b = unity_SHAb.xyz; + return LightVolumeEvaluate(n.xyz, light.L00, 0, 0, 0); +#endif +} - return L00; +float4 getIndirectDiffuse(v2f i, float4 vertexLightColor, + inout YumLighting light) { + float4 diffuse = vertexLightColor; +#if defined(FORWARD_BASE_PASS) +#if defined(LIGHTMAP_ON) + diffuse.xyz = DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap, i.uv01.zw)); +#else + diffuse.xyz += max(0, yumSH9(float4(i.normal, 0), i.worldPos, light)); #endif +#endif + return diffuse; } YumLighting GetYumLighting(v2f i, YumPbr pbr) { @@ -242,9 +204,10 @@ YumLighting GetYumLighting(v2f i, YumPbr pbr) { float3x3 tangentToWorld = float3x3(i.tangent, i.binormal, i.normal); // Use Bakery-aware irradiance function +#if defined(LIGHTMAP_ON) light.diffuse = BakeryGI_Irradiance( pbr.normal, // worldNormal - i.worldPos, // worldPos + i.worldPos, // worldPos float4(i.uv01.zw, 0, 0), // lightmapUV (xy = uv0, zw = uv1) float3(0,0,0), // ambient (will be calculated internally) light.attenuation, // attenuation @@ -256,6 +219,10 @@ YumLighting GetYumLighting(v2f i, YumPbr pbr) { #if defined(_GRAYSCALE_LIGHTMAPS) light.diffuse.gb = light.diffuse.r; #endif +#else + light.diffuse = getIndirectDiffuse(i, 0, light); + light.occlusion = 1; +#endif #if defined(_MIN_BRIGHTNESS) light.diffuse = max(_Min_Brightness, light.diffuse); diff --git a/yum_pbr.cginc b/yum_pbr.cginc index 03026c3..342b020 100644 --- a/yum_pbr.cginc +++ b/yum_pbr.cginc @@ -133,6 +133,7 @@ YumPbr GetYumPbr(v2f i, float3x3 tangentToWorld) { #endif applyDecals(i, result.albedo, normal_tangent, result.metallic, result.smoothness); + result.smoothness = min(0.99, result.smoothness); propagateRoughness(result.smoothness, result.roughness_perceptual, result.roughness); #if defined(_OKLCH_CORRECTION) |
