Skip to content

Instantly share code, notes, and snippets.

@solrz
Last active January 26, 2026 17:35
Show Gist options
  • Select an option

  • Save solrz/19532ac9f015d747d27eca3ea303a827 to your computer and use it in GitHub Desktop.

Select an option

Save solrz/19532ac9f015d747d27eca3ea303a827 to your computer and use it in GitHub Desktop.
The shader for naked-eye 3D display Companion 01, which realtime convert RGB-D video on device. Change constant may required for each device.
//!HOOK OUTPUT
//!BIND HOOKED
//!DESC Lenticular 3D - Full-SBS Fix V3
precision highp float;
// --- 參數設置 ---
#define LINE_NUMBER 19.6401
#define OBLIQUITY 0.105
#define DEVIATION 3.589
#define SCREEN_W 1440.0
#define SCREEN_H 2560.0
// --- 3D 強度 ---
#define THRESHOLD 40.0
#define PROTRUDE 0.0
#define DEPTH_INVERT 1.0
// --- 智能突出設置 ---
#define POP_START 0.6
#define POP_STRENGTH 2.5
#define POP_SMOOTHNESS 0.2
// 邏輯開始
float deform_depth_smart(float d) {
d = clamp(d, 0.0, 1.0);
float start = POP_START - POP_SMOOTHNESS;
float end = POP_START + POP_SMOOTHNESS;
float mask = smoothstep(start, end, d);
float boosted_depth = d + (mask * d * (POP_STRENGTH - 1.0));
return clamp(boosted_depth, 0.0, 2.0);
}
vec4 sample_quilt(vec2 uv, float viewId) {
// --- 1. 計算比例與縮放 ---
// 強制 Full-SBS 比例: (寬 * 0.5) / 高
float source_aspect = (input_size.x * 0.5) / input_size.y;
float screen_aspect = target_size.x / target_size.y;
vec2 scale = vec2(1.0);
if (screen_aspect > source_aspect) {
// 螢幕比影片寬 (Pillarbox, 左右黑邊)
scale.x = screen_aspect / source_aspect;
} else {
// 螢幕比影片窄 (Letterbox, 上下黑邊) -> 你的情況 (9:16 螢幕看 4:3)
scale.y = source_aspect / screen_aspect;
}
// 算出縮放後的原始 UV (範圍可能超出 0~1)
vec2 fittedUV = (uv - 0.5) * scale + 0.5;
// --- 2. 邊緣處理 (關鍵修正) ---
// 我們需要分別處理 X 和 Y
// Y 軸 (上下): 我們想要它延伸 (Smear),所以只做簡單的 clamp 0~1
// X 軸 (左右): 這是 SBS 的分割區,絕對不能延伸,否則會把左眼邊緣顏色拉成一條線蓋在畫面上
// 先對 Y 軸做延伸 (解決黑邊問題)
// 內縮 0.002 防止讀取到影片外部的黑色像素
float clampedY = clamp(fittedUV.y, 0.002, 0.998);
// 對 X 軸保持原樣,稍後再處理,但先檢查是否越界
float currentX = fittedUV.x;
// 如果 X 軸完全超出了影片範圍 (例如左右黑邊的情況),直接 clamp
// 但在你的情況 (左右填滿),這個值通常在 0~1 之間
float clampedX = clamp(currentX, 0.002, 0.998);
// 組合出用於採樣顏色的 Base UV
// 注意:這裡我們用 clampedX 來取色,防止左右邊緣出現雜訊
vec2 baseUV = vec2(clampedX, clampedY);
// --- 3. 採樣深度 ---
vec2 depthUV = baseUV * vec2(0.5, 1.0) + vec2(0.5, 0.0);
float depthVal = HOOKED_tex(depthUV).r;
depthVal = deform_depth_smart(depthVal);
// --- 4. 計算視差偏移 ---
float normalizedView = viewId;
float thresh = ((normalizedView - 0.5) * 2.0 / THRESHOLD);
float xOffset = (depthVal - (0.0 - PROTRUDE) - 0.5) * thresh * DEPTH_INVERT;
// --- 5. 最終採樣座標 ---
// 這裡要用 fittedUV.x (未 clamp 的) 加上偏移嗎?
// 不,因為如果 fittedUV.x 已經在邊緣,再加偏移就會錯。
// 所以我們基於 clampedX 進行偏移
// 左眼 RGB 區域: 0.0 ~ 0.5
vec2 finalUV_L = vec2(clampedX * 0.5 + xOffset, clampedY);
// 安全鎖定:這是消除「影片上多餘顏色」的最關鍵一步
// 無論視差怎麼偏移,絕對不能讓採樣點跑出左眼 (0.0~0.5) 的範圍
// 同時稍微內縮 (0.001) 避免讀到分割線
finalUV_L.x = clamp(finalUV_L.x, 0.001, 0.499);
// --- 6. 決定輸出顏色 ---
// 如果原始 fittedUV.x 超出了 0~1 (左右黑邊區域),我們顯示延伸色
// 但因為你的情況是左右填滿,所以這部分邏輯主要處理 Y 軸延伸
return HOOKED_tex(finalUV_L);
}
vec4 hook() {
vec2 screenUV = gl_FragCoord.xy / target_size;
float pitch = (SCREEN_W * 3.0) / LINE_NUMBER;
float slope = OBLIQUITY * (SCREEN_H / SCREEN_W);
float subp = 1.0 / (SCREEN_W * 3.0);
float center = (DEVIATION / SCREEN_W) * pitch;
float r, g, b;
float z;
z = (screenUV.x + 0.0 * subp + screenUV.y * slope) * pitch - center;
z = mod(z + ceil(abs(z)), 1.0);
r = sample_quilt(screenUV, z).r;
z = (screenUV.x + 1.0 * subp + screenUV.y * slope) * pitch - center;
z = mod(z + ceil(abs(z)), 1.0);
g = sample_quilt(screenUV, z).g;
z = (screenUV.x + 2.0 * subp + screenUV.y * slope) * pitch - center;
z = mod(z + ceil(abs(z)), 1.0);
b = sample_quilt(screenUV, z).b;
return vec4(r, g, b, 1.0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment