Trailer Video

Camera-Related

A. Color-Delta UV-Offset Outline

image.png

What it is (one-liner)

A lightweight per-material outline that detects edges by comparing the base texture to a slightly UV-offset sample; large color differences are drawn as a crisp outline.

Core idea

Edges often coincide with high local color contrast. If the color at uv differs enough from the color at a nearby uv + δ, we’re crossing a boundary—mark it with an outline color. This is a simple, fast edge detector (a 1-tap gradient approximation) you can extend by sampling several directions.

Minimal shader sketch (HLSL)

float _OutlineThreshold = 0.10;   // sensitivity (0–1)
float _OutlineWidth     = 1.0;    // in texels (scaled below)
float4 _OutlineColor    = float4(0,0,0,1);

float4 frag (v2f i) : SV_Target
{
    float4 col        = tex2D(_MainTex, i.uv);

    // Convert width (in texels) to UV offset using Unity’s built-in _MainTex_TexelSize
    float2 texel      = _MainTex_TexelSize.xy;     // (1/width, 1/height)
    float2 d          = texel * _OutlineWidth;

    // Single-direction sample (left as in your slide). Add more directions for thicker/robust outlines.
    float4 offsetCol  = tex2D(_MainTex, i.uv - d);

    // Edge strength = color gradient magnitude
    float edge = length(col.rgb - offsetCol.rgb);

    // Hard or soft edge
    float m = step(_OutlineThreshold, edge);       // hard threshold
    // float m = smoothstep(_OutlineThreshold, _OutlineThreshold*1.3, edge); // softer

    // Composite: draw outline where edge is strong
    float4 outCol = lerp(col, _OutlineColor, m);
    return outCol;
}

Parameters & what they do

Making it production-friendly

  1. Multi-direction sampling (cleaner edges):

    Sample ±X, ±Y (and optionally diagonals) and take the max edge value.

    float4 c = tex2D(_MainTex, i.uv);
    float2 d = _MainTex_TexelSize.xy * _OutlineWidth;
    float3 e1 = abs(c.rgb - tex2D(_MainTex, i.uv + float2( d.x, 0)).rgb);
    float3 e2 = abs(c.rgb - tex2D(_MainTex, i.uv + float2(-d.x, 0)).rgb);
    float3 e3 = abs(c.rgb - tex2D(_MainTex, i.uv + float2(0,  d.y)).rgb);
    float3 e4 = abs(c.rgb - tex2D(_MainTex, i.uv + float2(0, -d.y)).rgb);
    float edge = max(max(length(e1), length(e2)), max(length(e3), length(e4)));
    
    

    This mimics a tiny gradient kernel without the cost of a full Sobel.

  2. Thickness control:

    Increase _OutlineWidth or run a tiny dilation by sampling a second, larger offset and OR-ing the mask.

  3. Stability tips:

  4. Performance notes:

  5. Where to use it:

    Disables (“hides”) a set of renderers/GameObjects only when the camera is in front of the portal and has a clear view to it. If anything between the camera and the portal is hit by a ray/spherecast (your occlusion mask), the objects are kept enabled to avoid the black void that appears when two stencil volumes overlap.