快轉到主要內容
  1. 生成庫/

紅樓夢角色

紅樓夢模擬器是一個會把 120 回演一遍的敘事引擎。角色說話要有立繪、說到傷心處要換一張、場景切換要有背景、每一回要有一張回目圖 —— 全部加起來 833 張,而原著裡有名有姓的人物有好幾百個。

這頁講的是那 833 張怎麼生出來的,以及為什麼中間丟掉過一整批。

62 個有名有姓的角色
#

圖 1 · 角色群像
六十二位紅樓夢角色的全身立繪排成網格,包含寶玉、黛玉、寶釵、熙鳳、賈母、劉姥姥、賈政、妙玉、平兒、晴雯等
圖說:62 位。整批共用同一套美術設定 —— 512×768 全身立繪、淡彩、留白背景、右下角一枚朱印 —— 所以放在一起不會有人看起來像從別的故事走錯棚。

每個人八種表情,而且臉以外的地方一動也不動
#

圖 2 · 六個人 × 八種表情(臉部特寫)
六位角色各八格臉部特寫,橫排依序為平時、笑、正色、驚、困惑、羞窘、怒、泣,每一列的髮型、頭飾、衣領、背景完全相同,只有五官表情改變
圖說:橫著看是同一個人的八種情緒。注意髮絲、頭飾、衣領、背景 —— 一個像素都沒變。變的只有五官。

這是這批跟一般做法最大的差別。

生成模型沒有記憶。你為「黛玉.笑」重寫一次提示詞,它就重新想像一次這個人:換髮型、換臉型、換一套衣服,然後你得到八張互不相干的圖。常見的補救是找一組跨張不變的錨(例如把服裝主色寫死),讓讀者至少認得出是同一個人 —— 這能用,但你仔細看還是會發現人在飄。

這批走的是另一條路:不重畫,只改臉。

每個角色先生一張基準立繪。之後七種表情不是新的生成任務 —— 是把那張基準圖讀回來,用臉部偵測框出五官,只對那一塊重新擴散,然後貼回原圖。框外的像素從頭到尾沒有進過模型。

所以一致性不是「調到很像」,是結構上不可能不像

代價是姿勢固定了。八張的手勢、站姿完全一樣,情緒全靠臉。對敘事引擎來說這是划算的交換:立繪是配在對話框旁邊的,讀者的視線本來就在臉上。

八種表情是這樣分的:

portrait 平時laughingserious 正色
surprisedconfused 困惑embarrassed 羞窘
angrycrying

沒名字的那幾百個人,靠 16 個原型
#

圖 3 · 泛用原型
十六個泛用角色原型的立繪,涵蓋平民男女、僧、尼、官員、書生、貴族的少年少女與成年與老年男女、僕役的少年與成年男女
圖說:按「階級 × 性別 × 年齡」切出來的 16 格。小廝甲、婆子乙、來報信的官差 —— 沒有台詞份量卻必須出現在畫面上的人,全部從這裡取。

120 回裡走過的人遠不只 62 個。門房、小廝、婆子、道士、來收租的、來報喪的 —— 這些人有一兩句話,然後就消失了。替他們每人畫一張,成本上做不到;讓他們沒有立繪,畫面上又會缺一塊。

所以切出 16 個原型:階級(平民 / 貴族 / 僕役 / 出家 / 官 / 士)× 性別 × 年齡(少 / 壯 / 老)。每個原型一樣有完整八種表情,所以一個沒名字的婆子照樣可以在挨罵時換成怒、在報喪時換成泣。

這個決定是整批素材裡投資報酬率最高的一個:16 × 8 = 128 張,接住了幾百個角色。

89 處地景
#

圖 4 · 大觀園與兩府(89 處抽 12)
十二張場景圖,包含大觀園入口、賈赦房、賈珍房、蘆雪庵、寧國府庭院、榮國府祠堂、榮國府書房、僕役住處、瀟湘館、杏葉渚、尤氏房、真如福地
圖說:不是「一個古代庭園」重複用十二次 —— 蘆雪庵是雪、杏葉渚是夏荷、僕役住處有通鋪跟炭爐。地點在原著裡各自有性格,畫面上就得看得出來。

120 回,一回一張
#

圖 5 · 回目插畫(120 回抽 8)
八張回目插畫,分別為第 1、16、31、46、61、76、91、106 回,題材從山石通靈、雪中庭院、刺繡、聚賭,到荷塘、月下獨坐、雨中灑掃、殘荷
圖說:等距抽的八回。從第 1 回的青埂峰頑石到第 106 回的殘荷 —— 同一套筆法,但季節、光線、人氣多寡一路在走。

講一點做法
#

一、先定裝,再演戲。 每個角色的第一張是「定裝照」:姿勢、服裝、配飾、背景一次決定完。之後七種表情都掛在它下面。這個順序決定了整批的一致性上限 —— 定裝照錯了,八張一起錯。

實際的兩段流程長這樣(本機 GPU 跑 ComfyUI,沒有雲端 API):

基準立繪七種表情
起點空白 latent 512×768讀回基準立繪那張 PNG
取樣全圖,denoise 1.0只在臉的框內,denoise 0.5
臉怎麼框——YOLOv8 臉部偵測 → 外擴後裁下、放大、重繪、羽化貼回
步數8 步8 步

denoise 0.5 是整件事的樞紐:留一半原圖。全部重畫(1.0)臉會換人,動太少(0.2 以下)表情推不動。而且重繪只發生在框裡,框外連碰都沒碰到 —— 那才是圖 2 裡髮絲和衣領一個像素都沒變的原因,不是我調得好。

框的處理也有講究:偵測到的臉往外擴一圈、再以三倍範圍裁下來送進模型,最後羽化幾個像素貼回去。擴太少會在下巴留下接縫,擴太多就等於重畫整張。

二、表情提示詞是「加一句」,不是「換一段」。 七種表情的正面提示詞,前面那一整段角色描述(年紀、五官、服裝、畫風)一字不改,只在尾巴接一句表情:

(基準句原封不動)… high-fidelity masterpiece
                  ↑ 到這裡都一樣
, slightly misty eyes, gentle melancholy, restrained sorrow,
  dignified sadness, subtle downcast gaze, composed grief

而且寫的是動作不是形容詞。「悲傷的」會得到一張表情曖昧的臉;「眼睫微濕、視線下垂、神色克制」會得到你要的那張。模型對形容詞的解讀空間太大,對動作的解讀空間小。

三、負面提示詞要擋掉的是「其他情緒」和「用力過猛」。 每一種表情的負面詞都在基準負面詞後面追加兩類東西:其他六種情緒(免得模型混著給),以及那一種情緒的誇張版——

crying  的負面: …, smiling, happy, laughing,
                 exaggerated crying, open mouth wailing, childish expression
angry   的負面: …, smiling, happy, laughing, crying,
                 exaggerated rage, bared teeth, open mouth screaming, childish tantrum

第二類才是關鍵。生圖模型對「哭」的預設是嚎啕大哭、對「怒」的預設是齜牙咧嘴 —— 那是表情符號的語彙,不是這批畫的語彙。這些人是世家子弟,情緒要收著。「不要什麼」在這裡比「要什麼」還吃力氣。

四、八種是從調用端反推的,不是憑感覺定的。 敘事引擎要能自動選表情,它能從一句台詞判斷出來的粒度就是這八種。多做第九種,程式沒有足夠資訊去選它,做出來只會躺在資料夾裡。先看調用端能分辨到什麼,再決定素材要做幾種。

五、素材庫跟 app 分開放。 833 張、快 400 MB,塞進 app bundle 會直接撞到 App Store 的體積限制。所以它是一個獨立的素材倉庫,app 在需要時才去拉。這也讓補圖、換圖不必重新送審。

六、丟掉過一整批。 更早有一版是「20 個角色 × 3 種表情」,走的是工筆半身像加題款、靠服裝主色維持辨識度那條路。它能用,但兩個問題:三種表情不夠敘事引擎分派,而且同一個人在不同表情之間看得出來在飄。現在這批是重做的 —— 保留下來的只有角色名單。

沒講的部分
#

  • 62 個角色的定裝提示詞本身 —— 每個人的「身分區塊」怎麼寫,才在八張之間撐得住
  • 臉框在什麼髮型與角度上會偵測失敗,以及那時候的退路
  • 16 個原型的維度是怎麼收斂成這 16 格的
  • 120 回插畫怎麼從回目文字產生構圖,以及季節與時辰的連續性怎麼維持
  • 實際良率、重跑一張的成本、以及哪一種表情最難生(不是哭)
  • 敘事引擎那端怎麼決定某一句台詞該配哪一種表情

這些參數不是我記在腦子裡的 —— 每張 PNG 都把當初的完整流程寫在自己的中繼資料裡,所以兩年後看到一張圖,直接從檔案讀回模型、種子、提示詞、denoise。這頁的每個數字都是這樣挖出來的,不是憑印象寫的。


這批角色也撐起了一套 LINE 貼圖。想看同一套「先鎖常數、再放變數」用在完全不同的題材上,素材圖庫是同一個想法跑 2,545 次的樣子。