讓角色半透明:從 Ordered Dithering 說起(一)

問題

一個多月前,當我們再次思考相機時,一個問題不得不先解決:如何避免相機與角色穿插?當環境變的複雜、遊戲內容變得複雜時,似乎沒有一個令人滿意的相機運動規則既能避開角色又能兼顧體驗。

於是我們選擇了另一條路:讓角色靠近相機時變的半透明直到消失。

在 3D 遊戲中,「半透明」永遠是開發者的痛(因為半透明物體不能直接在 deferred 中完成繪製,以及排序困難)。而角色這樣的非凸多面體存在透視問題——比如透過人體表面看到眼球、舌頭或另一條手臂,這會嚴重影響體驗的。因此不能通過使用半透明 shader 解決問題。

這樣的半透明是不是超刺激?

Ordered Dithering

一種模擬半透明的經典方法是 Screen-Door Transparency,簡單來說就是通過在物體上「挖洞」模擬半透明,洞的密度就是透明度。如何挖也有很多不同方式,我們所用到的的稱為 Ordered Dithering,這種演算法本來是設計用來處理圖像的,在模擬半透明上道理相同。這種演算法的特點是結果穩定、(模擬的)透明度相同的物體在屏幕空間挖的洞位置也相同。

這是兩倍大小看到的 Ordered Dithering 模擬半透明效果,注意不同深度、不同部位挖的洞在屏幕上是規則排列的

原理

在角色 shader 中可以通過 clip / discard 拋棄像素,這會取消像素輸出(包括深度,此為一坑),可以用來實現挖洞。然後問題就是,挖的規則是什麼?

Ordered Dithering 使用一個矩陣,矩陣每個元素代表不透明度的臨界值,當繪製一個像素時,將每個屏幕像素坐標對應到矩陣中一個位置(就是對 x 和 y 分別取余),如果該位置的值大於需要設置的不透明度就拋棄此像素。

現在唯一需要確定的就是矩陣大小和內容了。矩陣大小決定可以控制的不透明度等級數,比如常見的 4x4 矩陣只能產生 17 個級別(除了 16 個半透明級別還有一個完全透明級別),在實際應用中效果不夠理想。我最開始想使用 16x16 的矩陣,可惜沒有找到。於是寫了一個演算法用來產生各種級別的 Ordered Dithering 矩陣:

/// /// 創建 Ordered Dithering 矩陣 /// /// size 必須為 2 的整數次冪,至少為 2 public static int[,] CreateOrderedDitheringMatrix(int size) { if (size <= 1 || !Mathf.IsPowerOfTwo(size)) { return null; } int inputSize = 1; int outputSize; int[,] input = new int[,] { { 0 } }; int[,] output; while (true) { outputSize = inputSize * 2; output = new int[outputSize, outputSize]; for (int i = 0; i < inputSize; i++) { for (int j = 0; j < inputSize; j++) { int value = input[i, j] * 4; output[i, j] = value; output[i + inputSize, j + inputSize] = value + 1; output[i + inputSize, j] = value + 2; output[i, j + inputSize] = value + 3; } } if (outputSize >= size) return output; else { inputSize = outputSize; input = output; } } }

如何應用

現在,我們已經可以在物體上挖洞了。一種最直接的應用想法是,用深度來計算不透明度,然後挖洞模擬。然而這種方法不可行,這與半透明透視問題本質是一樣的:會看到不該看的東西。我們使用 Ordered Dithering 的目的就是解決此問題。

如果每個獨立的物體,它的各部分具有統一的不透明度,那麼在 Ordered Dithering 作用後就不會有透視問題。因此,應將一個物體或角色看成整體,根據這個整體與相機的最短距離計算不透明度,然後應用到整體。

使用膠囊體粗略模擬角色外形

比如對於角色,使用膠囊體粗略模擬外形,然後計算整體與相機最短距離,根據最短距離計算不透明度,應用到身體、衣服等部位。這裡有個小優化,就是使用 bounding sphere 快速剔除,避免不必要的計算。對於存在大量角色的遊戲,這種優化是十分必要的,因為通常最多也就幾個角色會同時靠近相機。

根據距離計算不透明度

到這裡,基本功能已經完成,但遠沒有達到我們的要求。如何達到頭圖的效果呢?下一篇繼續。

(遊戲 原生體 Protoform)


推薦閱讀:

走樣與反走樣(Aliasing/Anti-Aliasing):Graphics Cases
拓幻圖形學工程師教學手冊(第三講)|一字一字敲出OpenGL學習教程
圖靈宇宙漫遊指南
譯:UE4是如何渲染一幀的(1)
趁熱度來做個捏臉

TAG:遊戲開發 | 遊戲編程 | 計算機圖形學 |