文本著色器 link

Ren’Py中包含一個文本著色器(text shader)系統,可以控制文本的顯示效果。 啟用文本著色器系統後,會使用兩個三角面片 基於模型的渲染器 來渲染每一個Unicode字元。 創作者自訂或Ren’Py內建的著色器程序都可以透過這種方式應用在模型上,並控制文本顯示效果。

文本著色器的文件有三塊內容:

  1. 如何使用文本著色器

  2. Ren’Py內建哪些文本著色器

  3. 如何創建新的文本著色器

需要注意,文本著色器系統本身不複雜,遊戲創作者可以很容易掌握。 但製作符合自己要求的文本著色器需要很多基礎知識,比如GLSL(OpenGL Shading Language)和 基於模型的渲染器 等。 其中大部分基礎知識都與Ren’Py的功能特性無關。

使用文本著色器 link

總共有三種方法可以使用文本著色器:

默認文本著色器 第一種方法,使用 config.default_textshader 設置預設的文本著色器。

define config.default_textshader = "wave:10"

這樣設置後,指定的文本著色器將用於所有文本,除了某些額外指定了文本著色器的內容。 該設置項還可以合併多個文本著色器,其中大部分都是默認著色器。

默認文本著色器需要小心慢速文本的情況,並且不應產生副作用。

樣式 第二種方法,使用樣式特性 textshader。可以直接指定樣式特性,也可以使用Ren’Py提供的多種樣式設置方式。

style default:
    textshader "dissolve"

define goldfinger = Character("金手指", what_textshader="linetexture:gold.png")

screen purchase():
    vbox:
        text "購買DLC內容!" textshader "wave"
        textbutton "立刻購買":
            text_textshader "wave" action iap.Purchase("dlc")

文本標籤 第三種方法,使用 對應的文本標籤 修改部分文本的顯示效果。

"這是什麼?  {shader=zoom}突然出現{/shader} 的一封信?"

注意 一個文本段落要嘛都使用文本著色器,要嘛都不使用,不能混著用。 例如,應該通過設置 config.default_textshader 或樣式特性 textshader,再使用文本標籤顯示效果。

指定文本著色器 link

在腳本中可以使用字串來指定文本著色器:

"dissolve"
"jitter:u__jitter=1.0, 3.0"
"texture:gold.png"

字串的第一部分,即第一個冒號前的內容,表示文本著色器的名稱。 字串的其他部分,對應傳給著色器的一些uniform變數,使用冒號分隔。 (uniform變數是著色器程序的控制變數。)

uniform變數可以使用等號(=)賦值。也可以省略uniform變數名,按順序依次對所有uniform變數賦值。(Ren’Py 7不支持省略uniform變數名。) 所有內建變數名均以 u_ 開頭,處於簡潔也可以省略這個 u_ 。

uniform變數的值可能是以下類型:

  • 至少1個至多4個的數字,使用逗號分隔。分別用於float、vec2、vec3和vec4類型的變數。

  • 以#開頭,表示顏色。(例如,#f00和#ff0000都表示紅色。)該值會創建一個顏色對應的vec4向量。顏色會預乘上alpha通道的值。

  • 一個 可視組件,會用做一張紋理。紋理會創建一個sampler2D對象,並用於紋理採樣。

uniform值不能使用表達式或從某個變數讀取,但可以使用文本內插創建一個字串並計算出某個文本著色器的標籤或參數。

最後,文本著色器可以使用 | 符號聯用。例如:

"jitter:1.0, 3.0|wave"

上面這句腳本會在文本中同時應用jitter和wave兩個著色器,前提是兩個著色器互相相容,並且沒有同名的uniform變數 (或具有同名且同類型的uniform變數,能夠互相相容,取值固定使用著色器列表中最後一個同名uniform變數的值)。

除非文本著色器顯式將 include_default 設置為False,透過樣式或文本標籤應用文本著色器時都將聯用預設的文本著色器。

文本著色器回調 link

配置項 config.textshader_callbacks 可以設置回調函數,在啟用某個文本著色器之後就會調用設置的回調函數。 該功能可以基於個人設定(preference)訂製文本著色器。

default persistent.dissolve_text = True

init python:
    def get_default_textshader():
        if persistent.dissolve_text:
            return "dissolve"
        else:
            return "typewriter"

define config.default_textshader = "default"
define config.textshader_callbacks["default"] = get_default_textshader

內建的文本著色器 link

以下是Ren’Py內建的文本著色器:

dissolve() link

dissolve文本著色器可以將文本以溶解(dissolve)形式緩慢顯示出來。dissolve指定範圍的字元,按順序依次顯示,直到顯示完最後一個字元。

u__duration = 10.0

過渡效果持續時間。若設為0,立刻顯示沒有過渡效果。

flip() link

flip文本著色器會將文本在水平方向上翻轉。開頭的字元先翻轉,最後的字元最後翻轉。

u__duration = 10.0

過渡效果持續時間。若設為0,立刻翻轉沒有過渡效果。

jitter() link

jitter文本著色器會讓文本抖動,即根據文本原顯示位置添加一個隨機偏移量,且每幀的位置偏移量都重新計算。

u__jitter=(3.0, 3.0)

抖動幅度,單位為像素。

linetexture() link

將文本與某個紋理相乘,各行單獨計算。用到的紋理在水平方向會與文本左端對齊。 紋理的中心點在垂直方向上會與文本底部對齊,這也意味著紋理下半部分幾乎就看不到了。

u__texture = …

與文本相乘使用的紋理。

u__scale = (1.0, 1.0)

紋理的縮放係數。例如(1.0, 0.5)可以讓紋理變為原本的一半高度。

offset() link

offset文本著色器可以讓文本位置偏移一個固定的值。

u__offset = (0.0, 0.0)

文本偏移量,單位為像素。

slowalpha() link

slowalpha著色器用於配合另一個慢速文本著色器,比如typewriter或dissolve。 它可以讓未被其他文本著色器生效的文本內容以一個半透明的狀態顯示而不是完全不可見,即參數u__alpha的值。

u__alpha = 0.2

其他文本著色器還未生效部分的文本不透明度。

texture() link

texture文本著色器會將多行文本與某個紋理的顏色相乘。 它不對輪廓線(outline)和偏移(offset)生效。用到的紋理將與整段文本的左上角對齊。

u__texture = …

與文本相乘使用的紋理。

typewriter() link

typewriter文本著色器配合低速文本,可以讓字元逐個出現,模仿人類打字員的行為。

使用該著色器後,默認文本著色器將不生效。

wave() link

wave文本著色器可以讓文本向波浪一樣上下彈跳。

u__amplitude = 5.0

文本的上下位移幅度,單位為像素。

u__frequency = 2.0

移動頻率,表示每秒完成整個彈跳的次數。

u__wavelength = 20.0

整個波長範圍涉及的字元數。

zoom() link

zoom文本著色器可以讓低速文本從某個初始大小逐步放大到完整尺寸。初始大小的值由參數u__zoom決定,預設為0.0。

u__zoom = 0.0

字元顯示時的初始大小。

u__duration = 10.0

過渡效果持續時間。若設為0,立刻變大沒有過渡效果。

使用該著色器後,默認文本著色器將不生效。

創建文本著色器 link

文本著色器是運行在GPU上的GLSL程序。可以使用 renpy.register_text_shader 函數註冊文本著色器。

renpy.register_textshader(name, shaders=(), extra_slow_time=0.0, extra_slow_duration=0.0, redraw=None, redraw_when_slow=0.0, include_default=True, adjust_function=None, doc=None, **kwargs) link

該函數會創建一個文本著色器,並註冊著色器名 name

該函數使用下列入參:

name

文本著色器名。同時也會註冊一個名為 name 的著色器程序。

shaders

應用到文本的著色器程序。該項可以是一個字串,字串列表或字串元組。 該項中包含的著色器程序必須是通過 renpy.register_shader() 或本函數註冊過的著色器。 如果著色器名前帶一個‘-’,表示從著色器列表中移除對應的著色器程序。 (例如,“-textshader.typewriter”表示移除typerwriter著色器。)

注意,通過該函數註冊的著色器名自動添加了前綴“textshader.”,在作為參數時需要傳入完整的著色器名。

extra_slow_time

添加一個額外的時間。Ren’Py計算當前字元的效果時間時將加上該時間。 某些著色器做字元過渡效果時間不足時,該參數就可以用上。

extra_slow_duration

該參數也是一個時間值,會除以同時生效字元總數然後再加到 extra_slow_time 上。

redraw

所有低速文本顯示並且經過 extra_slow_time 時間後,再次重新繪製的時間間隔,單位為秒。

redraw_when_slow

文本已繪製並且顯示低速文本時,再次重新繪製的時間間隔,單位為秒。

include_default

若為True,將會聯用 config.default_textshader 中的著色器。

adjust_function

該參數是一個函數,透過某個對象調用。傳給文本著色器的uniform變數會先使用該函數處理。 該函數可以設置來源對象的 extra_slow_timeextra_slow_durationredrawredraw_when_slow 這4個欄位。

doc

包含文件資訊的一個字串。該參數為Ren’Py的文件系統所設計。

u_ 開頭的關鍵字入參會傳給著色器作為uniform變數,以 # 開頭的字串會識別為某種顏色。 大多數uniform變數都應該以 u__ 開頭,使用 著色器本地變數 防止與其他著色器發生變數名衝突。

名為 variables 的關鍵字入參和所有以 fragment_vertex_ 開頭的關鍵字入參都會傳給 renpy.register_shader() 函數, 並註冊對應的著色器。

文本著色器中的變數 link

除了創作者自己設計文本著色器時指定的uniform變數(通常以 u__ 開頭),Ren’Py還使下列變數可以直接在文本著色器中使用。 若要在文本著色器中使用某個變數,需要將變數名通過 variables 參數傳入 renpy.register_text_shader 函數。

除此之外,針對模型還可以使用 uniform和attribute變數, 常用的包括 a_positiona_tex_coordu_timeu_random

uniform變數 link

float u_text_depth

從最頂層算起,文本的深度(depth)。最頂層的文本深度為0.0,第一層輪廓線或投影的深度為1.0,第二層羅廓線或投影的深度為2.0,以此類推。

float u_text_main

若該變數值為1.0,則對應文本為主要文本。若該變數值為0.0,則對應文本為主要文本的輪廓線或投影。

float u_text_max_depth

u_text_depth的最大值,表示能繪製的輪廓線和投影總數量。 當u_text_depth等於u_text_max_depth時,意味著當前文本是最後一層輪廓線或投影,該功能在繪製背景時可能有用。

vec2 u_text_offset

文本相對字元中心點的偏移量。按照先在x軸方向偏移再y軸方向偏移的順序計算,單位為像素。

float u_text_outline

文本輪廓線寬度。輪廓線只考慮能繪製的像素點。該變數表示從文本外緣到輪廓線外援的距離,單位為像素。

float u_text_slow_duration

使用低速文本時,單個字元的效果時間。設置為0.0表示不顯示低速文本。

float u_text_slow_time

低速文本效果的時間戳,從效果生效開始計算,單位為秒。僅當低速文本效果結束時該值才會增加,並輸出最大值。 如果用戶點擊並結束了低速文本,也會輸出最大值。其應只能用於低速文本。

float u_text_to_drawable

虛擬像素向可繪製像素的轉換比例。

float u_text_to_virtual

可繪製像素向虛擬像素的轉換比例。

sampler2D tex0

在當前深度(depth)包含渲染文本的對應紋理。

vec2 res0

tex0的解析度,以可繪製像素計。

attribute變數 link

繪製文本時,每個字形(glyph)都有自身對應的頂點(vertex)。多個字形可能會有一些頂點的坐標是相同的,但依然會看作不同頂點傳入著色器。

(譯者註:baseline、ascent和descent是字母型文字才需要考慮的東西,純中文用戶完全不用管。)

float a_text_ascent

當前字形的字體在基線以上的ascent高度,以可繪製像素計。

vec2 a_text_center

字形基線的中心坐標,以可繪製像素計。該坐標並不是三角形中心,由基線與字元中心的距離決定。

float a_text_descent

The descent of the current glyph below the baseline, in drawable pixels. 當前字形的字體在基線以下的descent高度,以可繪製像素計。

float a_text_index

正在繪製字形的索引號。頂點索引從0開始,依次遞增。

vec2 a_text_min_time

所有字形的任意頂點需要顯示的最小時間。從左往右顯示時,即最左端頂點的顯示時間。 若要讓文本立刻顯示,但 u_text_slow_duration 的值又不是0.0,該項應設置為-3600.0。

vec2 a_text_max_time

所有字形的任意頂點需要顯示的最大時間。從左往右顯示時,即最右端頂點的顯示時間。 若要讓文本立刻顯示,但 u_text_slow_duration 的值又不是0.0,該項應設置為-3600.0。

float a_text_time

對應頂點的顯示時間。 若要讓文本立刻顯示,但 u_text_slow_duration 的值又不是0.0,該項應設置為-3600.0。

vec4 a_text_pos_rect

整個字形的包圍框,以可繪製像素計。該變數是一個vec4類型,分別表示包圍狂的x、y、寬度和高度,以可繪製像素計。 可以通過除以 res0 來獲得紋理坐標值。

偽字形 link

Ren’Py繪製帶輪廓線的文本時,會創建覆蓋文本開頭和結尾的偽字形(pseudo-glyph)。 如果遇到空行,則會創建覆蓋整行的偽字形。 這些偽字形用於計算輪廓線超出文字頂部和底部的區域。

樣例 link

這是一個文本著色器樣例,能讓文本顯示後轉動。

init python:

    def adjust_extra_slow_time(ts, u__delay, **kwargs):
        """
        調整文本著色器的額外顯示時間,以支持旋轉文本著色器。
        """
        ts.extra_slow_time = u__delay

    renpy.register_textshader(
        "spin",
        adjust_function = adjust_extra_slow_time,

        variables = """
        uniform float u__delay;
        uniform float u__offset;
        uniform float u_text_slow_time;
        attribute vec2 a_text_center;
        attribute float a_text_min_time;
        """,

        vertex_50 = """
        float l__angle = clamp((u_text_slow_time - a_text_min_time) / u__delay, 0.0, 1.0) * 2.0 * 3.1415926536;
        float l__sin = sin(l__angle);
        float l__cos = cos(l__angle);

        gl_Position.y -= u__offset;
        gl_Position.xy -= a_text_center;
        gl_Position = vec4(
            gl_Position.x * l__cos - gl_Position.y * l__sin,
            gl_Position.x * l__sin + gl_Position.y * l__cos,
            gl_Position.z,
            gl_Position.w
            );
        gl_Position.xy += a_text_center;
        gl_Position.y += u__offset;
        """,

        u__delay = 1.0,
        u__offset = 0,
    )

之後就可以在如下腳本中使用:

define config.default_textshader = "typewriter"

label start:

    "這是一個 {shader=spin:0.5:-5}旋轉{/shader} 文本著色器測試。"