summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryum <yum.food.vr@gmail.com>2026-01-13 20:42:27 -0800
committeryum <yum.food.vr@gmail.com>2026-01-13 22:27:33 -0800
commitad551018904f13b8af0d1c4c2aa8380ac57de89a (patch)
tree9915d06a03988cacd3c13131edc32946d7c9876d
parent6df5a9a34d81d1200231b974fcc85f89e80eb63e (diff)
Impostors: fix depth capture
-rw-r--r--DepthBlit.shader52
-rw-r--r--Impostor.shader3
-rw-r--r--Scripts/Impostors.cs109
-rw-r--r--impostor.cginc67
4 files changed, 198 insertions, 33 deletions
diff --git a/DepthBlit.shader b/DepthBlit.shader
new file mode 100644
index 0000000..04187c2
--- /dev/null
+++ b/DepthBlit.shader
@@ -0,0 +1,52 @@
+Shader "Hidden/yum_food/DepthBlit"
+{
+ // Reads from depth texture and outputs linearized depth to color
+ Properties
+ {
+ _MainTex ("", 2D) = "white" {}
+ }
+
+ SubShader
+ {
+ Cull Off ZWrite Off ZTest Always
+
+ Pass
+ {
+ CGPROGRAM
+ #pragma vertex vert
+ #pragma fragment frag
+
+ #include "UnityCG.cginc"
+
+ sampler2D _DepthTex;
+
+ struct v2f
+ {
+ float4 pos : SV_POSITION;
+ float2 uv : TEXCOORD0;
+ };
+
+ v2f vert(float4 vertex : POSITION, float2 uv : TEXCOORD0)
+ {
+ v2f o;
+ o.pos = UnityObjectToClipPos(vertex);
+ o.uv = uv;
+ return o;
+ }
+
+ float4 frag(v2f i) : SV_Target
+ {
+ float rawDepth = tex2D(_DepthTex, i.uv).r;
+
+ // Orthographic depth is already linear.
+ // We just need to handle the platform-specific Z-buffer direction.
+ #if defined(UNITY_REVERSED_Z)
+ return 1.0 - rawDepth;
+ #else
+ return rawDepth;
+ #endif
+ }
+ ENDCG
+ }
+ }
+}
diff --git a/Impostor.shader b/Impostor.shader
index 1dff6c9..f2eb602 100644
--- a/Impostor.shader
+++ b/Impostor.shader
@@ -3,11 +3,14 @@ Shader "yum_food/Gimmicks/Impostors"
Properties
{
_ImpostorAtlas("Impostor Atlas", 2D) = "white" {}
+ _ImpostorDepthAtlas("Impostor Depth Atlas", 2D) = "white" {}
_GridResolution("Grid Resolution", Int) = 5
+ _SphereRadius("Sphere Radius", Float) = 1.0
_Cutoff("Alpha Cutoff", Range(0, 1)) = 0.5
_Color("Tint", Color) = (1, 1, 1, 1)
[Toggle] _DebugMode("Debug Mode", Float) = 0
+ [Toggle] _DebugDepth("Debug Depth", Float) = 0
[Enum(UnityEngine.Rendering.CullMode)] _Cull("Cull", Float) = 0
[Enum(Off, 0, On, 1)] _ZWrite("ZWrite", Int) = 1
diff --git a/Scripts/Impostors.cs b/Scripts/Impostors.cs
index 6d8d67b..ded7e6d 100644
--- a/Scripts/Impostors.cs
+++ b/Scripts/Impostors.cs
@@ -130,29 +130,67 @@ public class Impostors : MonoBehaviour
SetRenderersEnabled(true);
if (cameras == null || cameras.Length != gridResolution * gridResolution || cameras[0] == null) CreateCameras();
+ // Load depth blit shader
+ Shader depthBlitShader = Shader.Find("Hidden/yum_food/DepthBlit");
+ if (depthBlitShader == null) { Debug.LogError("DepthBlit shader not found"); return; }
+ Material depthBlitMat = new Material(depthBlitShader);
+
// Render atlas
int size = cameraResolution * gridResolution;
- Texture2D atlas = new Texture2D(size, size, TextureFormat.RGBA32, false);
- RenderTexture rt = RenderTexture.GetTemporary(cameraResolution, cameraResolution, 24);
+ Texture2D colorAtlas = new Texture2D(size, size, TextureFormat.RGBA32, false);
+ Texture2D depthAtlas = new Texture2D(size, size, TextureFormat.RFloat, false);
+
+ // Create RT with depth buffer that can be sampled as texture
+ RenderTextureDescriptor desc = new RenderTextureDescriptor(cameraResolution, cameraResolution, RenderTextureFormat.ARGB32, 24);
+ desc.sRGB = true;
+ RenderTexture colorRT = RenderTexture.GetTemporary(desc);
+
+ // Separate depth texture
+ RenderTextureDescriptor depthDesc = new RenderTextureDescriptor(cameraResolution, cameraResolution, RenderTextureFormat.Depth, 24);
+ RenderTexture depthOnlyRT = RenderTexture.GetTemporary(depthDesc);
+
+ // Output for linearized depth
+ RenderTexture linearDepthRT = RenderTexture.GetTemporary(cameraResolution, cameraResolution, 0, RenderTextureFormat.RFloat);
int idx = 0;
for (int y = 0; y < gridResolution; y++)
{
for (int x = 0; x < gridResolution; x++)
{
- cameras[idx++].targetTexture = rt;
- cameras[idx - 1].Render();
- RenderTexture.active = rt;
- Texture2D temp = new Texture2D(cameraResolution, cameraResolution);
- temp.ReadPixels(new Rect(0, 0, cameraResolution, cameraResolution), 0, 0);
- temp.Apply();
- atlas.SetPixels(x * cameraResolution, y * cameraResolution, cameraResolution, cameraResolution, temp.GetPixels());
- DestroyImmediate(temp);
+ Camera cam = cameras[idx++];
+
+ // Render to color + depth buffers simultaneously
+ cam.SetTargetBuffers(colorRT.colorBuffer, depthOnlyRT.depthBuffer);
+ cam.Render();
+
+ // Read color
+ RenderTexture.active = colorRT;
+ Texture2D colorTemp = new Texture2D(cameraResolution, cameraResolution);
+ colorTemp.ReadPixels(new Rect(0, 0, cameraResolution, cameraResolution), 0, 0);
+ colorTemp.Apply();
+ colorAtlas.SetPixels(x * cameraResolution, y * cameraResolution, cameraResolution, cameraResolution, colorTemp.GetPixels());
+ DestroyImmediate(colorTemp);
+
+ // Blit depth buffer through linearization shader
+ depthBlitMat.SetTexture("_DepthTex", depthOnlyRT);
+ Graphics.Blit(null, linearDepthRT, depthBlitMat);
+
+ // Read linearized depth
+ RenderTexture.active = linearDepthRT;
+ Texture2D depthTemp = new Texture2D(cameraResolution, cameraResolution, TextureFormat.RFloat, false);
+ depthTemp.ReadPixels(new Rect(0, 0, cameraResolution, cameraResolution), 0, 0);
+ depthTemp.Apply();
+ depthAtlas.SetPixels(x * cameraResolution, y * cameraResolution, cameraResolution, cameraResolution, depthTemp.GetPixels());
+ DestroyImmediate(depthTemp);
}
}
- atlas.Apply();
+ colorAtlas.Apply();
+ depthAtlas.Apply();
RenderTexture.active = null;
- RenderTexture.ReleaseTemporary(rt);
+ RenderTexture.ReleaseTemporary(colorRT);
+ RenderTexture.ReleaseTemporary(depthOnlyRT);
+ RenderTexture.ReleaseTemporary(linearDepthRT);
+ DestroyImmediate(depthBlitMat);
// Save
if (!AssetDatabase.IsValidFolder(OutputFolder))
@@ -163,24 +201,43 @@ public class Impostors : MonoBehaviour
}
string name = gameObject.name.Replace(" ", "_");
- string path = Path.Combine(OutputFolder, $"{name}_atlas.png");
- File.WriteAllBytes(Path.Combine(Application.dataPath, "..", path), atlas.EncodeToPNG());
- DestroyImmediate(atlas);
+ string colorPath = Path.Combine(OutputFolder, $"{name}_atlas.png");
+ string depthPath = Path.Combine(OutputFolder, $"{name}_depth.exr");
+
+ File.WriteAllBytes(Path.Combine(Application.dataPath, "..", colorPath), colorAtlas.EncodeToPNG());
+ File.WriteAllBytes(Path.Combine(Application.dataPath, "..", depthPath), depthAtlas.EncodeToEXR(Texture2D.EXRFlags.OutputAsFloat));
+ DestroyImmediate(colorAtlas);
+ DestroyImmediate(depthAtlas);
AssetDatabase.Refresh();
- TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter;
- if (importer != null)
+
+ // Configure color atlas importer
+ TextureImporter colorImporter = AssetImporter.GetAtPath(colorPath) as TextureImporter;
+ if (colorImporter != null)
+ {
+ colorImporter.mipmapEnabled = true;
+ colorImporter.alphaIsTransparency = true;
+ colorImporter.wrapMode = TextureWrapMode.Clamp;
+ colorImporter.filterMode = FilterMode.Trilinear;
+ colorImporter.SaveAndReimport();
+ }
+
+ // Configure depth atlas importer
+ TextureImporter depthImporter = AssetImporter.GetAtPath(depthPath) as TextureImporter;
+ if (depthImporter != null)
{
- importer.mipmapEnabled = true;
- importer.alphaIsTransparency = true;
- importer.wrapMode = TextureWrapMode.Clamp;
- importer.filterMode = FilterMode.Trilinear;
- importer.SaveAndReimport();
+ depthImporter.mipmapEnabled = false;
+ depthImporter.sRGBTexture = false; // Linear data
+ depthImporter.wrapMode = TextureWrapMode.Clamp;
+ depthImporter.filterMode = FilterMode.Bilinear;
+ depthImporter.textureCompression = TextureImporterCompression.Uncompressed;
+ depthImporter.SaveAndReimport();
}
// Create impostor
- Texture2D tex = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
- if (tex != null)
+ Texture2D colorTex = AssetDatabase.LoadAssetAtPath<Texture2D>(colorPath);
+ Texture2D depthTex = AssetDatabase.LoadAssetAtPath<Texture2D>(depthPath);
+ if (colorTex != null)
{
DestroyExistingImpostor();
@@ -188,8 +245,10 @@ public class Impostors : MonoBehaviour
if (shader == null) { Debug.LogError("Shader not found"); return; }
impostorMaterial = new Material(shader);
- impostorMaterial.SetTexture("_ImpostorAtlas", tex);
+ impostorMaterial.SetTexture("_ImpostorAtlas", colorTex);
+ impostorMaterial.SetTexture("_ImpostorDepthAtlas", depthTex);
impostorMaterial.SetInt("_GridResolution", gridResolution);
+ impostorMaterial.SetFloat("_SphereRadius", sphere_radius_);
AssetDatabase.CreateAsset(impostorMaterial, Path.Combine(OutputFolder, $"{name}_mat.mat"));
impostorObject = GameObject.CreatePrimitive(PrimitiveType.Quad);
diff --git a/impostor.cginc b/impostor.cginc
index e265438..f6c0d72 100644
--- a/impostor.cginc
+++ b/impostor.cginc
@@ -6,13 +6,16 @@
SamplerState bilinear_clamp_s;
Texture2D _ImpostorAtlas;
+Texture2D _ImpostorDepthAtlas;
int _GridResolution;
+float _SphereRadius;
float _Cutoff;
float3 _ImpostorMainCameraPos;
#ifndef IMPOSTOR_SHADOW_PASS
float4 _Color;
float _DebugMode;
+float _DebugDepth;
#endif
float2 HemiOctEncode(float3 N) {
@@ -45,6 +48,10 @@ float4 SampleAtlas(float2 uv, float2 cell) {
return _ImpostorAtlas.Sample(bilinear_clamp_s, (cell + uv) / _GridResolution);
}
+float SampleDepthAtlas(float2 uv, float2 cell) {
+ return _ImpostorDepthAtlas.Sample(bilinear_clamp_s, (cell + uv) / _GridResolution).r;
+}
+
// ============================================================================
// Vertex/Fragment
// ============================================================================
@@ -62,10 +69,15 @@ struct v2f {
V2F_SHADOW_CASTER;
#else
float4 pos : SV_POSITION;
- UNITY_FOG_COORDS(3)
+ UNITY_FOG_COORDS(7)
#endif
float2 uv : TEXCOORD1;
float2 cell : TEXCOORD2;
+ // For depth reconstruction
+ float3 worldPos : TEXCOORD3;
+ float3 viewWS : TEXCOORD4;
+ float3 center : TEXCOORD5;
+ float radius : TEXCOORD6;
UNITY_VERTEX_OUTPUT_STEREO
};
@@ -108,22 +120,61 @@ v2f vert(appdata v) {
BillboardBasis(snapOS, right, up);
o.uv = float2(1 - (dot(localOff, right) + 0.5), dot(localOff, up) + 0.5);
o.cell = float2(cell);
+
+ // Pass data for depth reconstruction
+ float3 viewWS = normalize(camPos - center);
+ o.worldPos = worldPos;
+ o.viewWS = viewWS;
+ o.center = center;
+ o.radius = _SphereRadius * scale.x;
+
return o;
}
+#ifdef IMPOSTOR_SHADOW_PASS
+// Shadow pass - just alpha test, no depth output
float4 frag(v2f i) : SV_Target {
float4 col = SampleAtlas(i.uv, i.cell);
-#ifndef IMPOSTOR_SHADOW_PASS
- if (_DebugMode > 0.5) return float4(1, 0, 0, 1);
- col *= _Color;
-#endif
clip(col.a - _Cutoff);
-#ifdef IMPOSTOR_SHADOW_PASS
SHADOW_CASTER_FRAGMENT(i)
+}
#else
+// Forward pass - with depth output
+struct FragOutput {
+ float4 color : SV_Target;
+ float depth : SV_Depth;
+};
+
+FragOutput frag(v2f i) {
+ FragOutput o;
+
+ float4 col = SampleAtlas(i.uv, i.cell);
+ float depth = SampleDepthAtlas(i.uv, i.cell);
+
+ // Compute depth-corrected world position
+ // depth=0 means front of sphere (toward camera), depth=1 means back (away from camera)
+ float3 depthWorldPos = i.worldPos - i.viewWS * (2.0 * depth - 1.0) * i.radius;
+
+ // Convert to clip space depth
+ float4 clipPos = mul(UNITY_MATRIX_VP, float4(depthWorldPos, 1.0));
+ o.depth = clipPos.z / clipPos.w;
+
+ if (_DebugMode > 0.5) {
+ o.color = float4(i.uv, 0, 1);
+ return o;
+ }
+ if (_DebugDepth > 0.5) {
+ o.color = float4(depth.xxx, 1);
+ return o;
+ }
+
+ col *= _Color;
+ clip(col.a - _Cutoff);
+
UNITY_APPLY_FOG(i.fogCoord, col);
- return col;
-#endif
+ o.color = col;
+ return o;
}
+#endif
#endif