# 油畫：Three.js 遊戲接入指南

「油畫／Oil Paint」把已渲染的遊戲畫面整理成方向性筆觸，並加入乾筆與顏料起伏。它是即時 GPU 後製，不使用生成式 AI 服務，也不修改模型。

## 下載與相容性

在 Shader 目錄開啟「Oil Paint」，調整參數後選擇「下載 .js」或「複製遊戲模組」。查看、複製及下載使用相同內容，下載內的 `defaultOptions` 保存當次設定。

已指定的相容基準為 **Three.js 0.187.0、WebGL2、EffectComposer**。請使用遊戲原有的同一份 `three`，保留匯出檔的 MIT 與 Three.js 授權。Unity、WebGPU、XR、遮罩後製及任意舊版 Three.js 不在第一版驗證範圍。

## 加入既有遊戲

將下載的 `oil-paint.js` 放入遊戲專案。已有 EffectComposer 的遊戲，只加入油畫 Pass；下面是最小組裝範例，`renderer`、`scene`、`camera` 均沿用遊戲既有物件：

```js
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
import createOilPaintPass from './oil-paint.js';

const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
const oilPaint = createOilPaintPass();
composer.addPass(oilPaint);
composer.addPass(new OutputPass());

// 在遊戲原本的每幀更新中呼叫，取代原來直接 renderer.render 的位置。
composer.render(deltaSeconds);
```

順序是「場景及既有線性後製 → 油畫 → OutputPass → HUD」。OutputPass 負責最後的色調映射與顯示色彩轉換；不要再補第二次 gamma 轉換。油畫接收並輸出線性顏色，保留 HDR 亮部與輸入 alpha。油畫包含先前已畫入場景的透明物件與粒子。

DOM 介面可維持疊在 canvas 上方。若 HUD 本身是 Three.js 場景，請在 composer 完成後單獨繪製；先前已畫進輸入畫面的文字，油畫無法辨識並恢復清晰。

## 調整、尺寸與清理

五個參數為 `intensity`（效果強度）、`brushSize`（筆觸大小）、`edgePreservation`（輪廓保留）、`paintRelief`（顏料厚度）、`canvasStrength`（畫布紋理）。初始值可讀取 `defaultOptions`；目前有效值由 `oilPaint.settings` 提供副本。

顏料厚度預設為 `2`，範圍 `0–3`，調整間距為 `0.01`。
效果強度預設為 `0.75`，範圍 `0–2`；超過 `1` 時加強油畫與原圖之間的差異。

輪廓保留預設為 `-3`，範圍 `-10–1`；負值加強輪廓附近的刮痕與厚塗質感，`0` 不補回原圖輪廓，正值保留原圖輪廓。從 `0` 調至 `-10` 會逐步加強輪廓周圍的筆觸與厚塗，並抑制細亮線。

```js
oilPaint.setOptions({ intensity: 0.8, paintRelief: 0.35 });
oilPaint.enabled = false; // 由 EffectComposer 跳過效果
oilPaint.enabled = true;
```

`intensity: 0` 複製原畫面；`enabled: false` 則讓 EffectComposer 完全跳過這個 Pass。筆觸為畫面空間效果，移動鏡頭時會重新計算，不會像 UV 貼圖一樣黏在模型表面；顏料厚度是視覺模擬，不會產生真實幾何或陰影。

調整視窗時，沿用遊戲的 renderer、camera 與 composer 尺寸更新；EffectComposer 會把實際像素尺寸傳給 `oilPaint.setSize`。直接呼叫 Pass 時也應傳實際 framebuffer 像素，不能重複乘上 DPR。昂貴的內部處理採半寬、半高解析度，最後仍輸出完整尺寸。

移除效果時先從 composer 移除，再呼叫 `oilPaint.dispose()`；此方法只釋放效果自己的材質與中間貼圖，不銷毀遊戲的 renderer、composer 或輸入輸出畫面。整個遊戲結束時，仍由遊戲清理自己建立的其他 Pass、composer 及 renderer。

## 效能與限制

設計目標為 RTX 3060 級電腦、1920×1080、60 FPS；這是需要實測的目標，並非任意場景的效能保證。高 DPI 與較大畫面會增加成本；筆觸大小改變採樣範圍，方向性濾鏡的採樣數固定為 29，不隨筆觸大小增加。網站縮圖的流暢度不能當成 1080p 遊戲效能證據。

第一版無模型選取遮罩、動作向量、歷史畫面累積及模型專用筆觸。相同畫面與參數不依時間隨機變化，但鏡頭或物件移動時，筆觸方向與色塊仍可能變化。請在自己的遊戲檢查遠處輪廓、戰鬥提示、鏡頭移動與透明粒子。

演算法參考：[方向性 Kuwahara](https://www.kyprianidis.com/p/gpupro/)；接入參考：[Three.js OutputPass](https://threejs.org/docs/pages/OutputPass.html)。

筆觸方向以每 15° 直接切換，沒有旋轉過渡；角度分界附近可能出現跳動。
