基於模型的渲染器 link
儘管Ren’Py在視覺小說中主要使用二維矩形圖形,但為了利用各類主流GPU的功能特性其實Ren’Py內建了一個基於模型的渲染器。 使用該渲染器可以實現很多視覺特效。
事先預警,該渲染器是Ren’Py最新添加的功能。很多情況下,不需要理解基於模型渲染器在後台的工作原理,就可以使用
新增的特性,比如 matrixcolor
和Live2D的支持。Ren’Py後續也會添加類似的功能。
本頁文件主要面向最前沿的創作者,以及嘗試在Ren’Py中添加新功能的開發人員。
基於模型渲染器是Ren’Py中最新的功能特性,如果事先沒有看過OpenGL、OpenGL ES、GLSL和GLSL ES手冊的話,理解本頁文件可能十分困難。 此外,不同圖形處理單元驅動對輸入模型數據的要求可能略有不同,需要事先檢查硬體資訊。
模型,渲染和繪圖操作 link
Ren’Py繪製到螢幕上的內容,本質上是一個模型。該模型包含以下內容:
由一個或多個三角形組成的網格(mesh)。 一個三角形包含三個頂點(vertice)。 每個頂點包含二維或三維空間中的位置資訊,還可能包含其他資訊,比如最常見的是紋理(texture)坐標。
0個或若干個紋理(texture),最大數量受限於遊戲實際運行平台的圖形處理單元(GPU)。所有的圖形處理單元都至少需要支持每個模型3個紋理。 紋理是一個含有圖像數據的矩形,可以直接或渲染加工後,載入進圖形處理單元中。
著色器(shader)名稱的一個列表。Ren’Py使用該列表創建著色器。著色器是在圖形處理單元中運行的程序,用於渲染模型。 著色器名稱前綴為 “-” 表示禁用此著色器。
uniform型變數值。uniform型變數保存添加到模型中的數據。比如,要使某個模型顯示為某個純色,指定的顏色就需要是一個uniform型。
GL特性(property)。GL特性是控制渲染方式的標識型數據,比如模型的縮放模式以及顏色遮色片(mask)。
Ren’Py通常會在螢幕上同時繪製好幾樣東西,並創建一棵 Render
對象樹。
樹上的Render對象的子對象可能是模型或其他Render對象。(後面會提到,Render對象也可以轉為模型。)
一個Render對象包含:
子對象列表,包括每個子對象的二維偏移量。
一個
Matrix
對象,用於描述各子對象變換到三維空間的方法。模型繪製時需要用到的著色器名稱、uniform變數和GL特性列表。
判斷可繪制空間分割多邊形是否更新的一些標識(flag)。
Ren’Py使用深度優先算法遍歷Render對象樹,遍歷搜索到模型對象才繪製畫面。 執行遍歷時,Ren’Py會更新一個矩陣,該矩陣對模型的位置,分割多邊形,著色器、uniform變數和GL特性列表進行轉換。 當遍歷的某一步中搜索到模型對象時,圖形處理單元上對應的著色器程序將啟用,並傳入(矩陣內)所有資訊,最後執行繪圖操作。
模型對象在哪裡創建的 link
Ren’Py在常規操作中將自動創建模型對象。 理解模型創建細節的主要用處是,主動使用繪圖操作生成模型對象以及直接在模型對象上的應用著色器。
- 圖像和圖像處理器(Image Manipulator)
這兩類創建的模型是由兩個三角形組成的網格(mesh),兩個三角形正好覆蓋原矩形圖像。 該網格包含紋理坐標。創建的模型使用 “renpy.texture” 著色器。
Solid()
純色可視組件創建的模型是由兩個三角形組成的網格(mesh),但沒有紋理坐標。 創建的模型使用 “renpy.solid” 著色器,該著色器使用的顏色資訊儲存在
u_renpy_solid_color
uniform變數中。Dissolve()
,ImageDissolve()
,AlphaDissolve()
,Pixellate()
,AlphaMask()
,Flatten()
以上這些變換和可視組件創建的模型對象可以根據實際需要包含對應的網格、著色器和uniform變數。
- Live2D
Live2D型可視組件在渲染時可能會創建多個模型對象,即每個圖層一個模型對象。
Transform()
和 ATLTransform對象在
mesh
為True或blur
特性時創建一個模型對象。 模型創建後,Transform對象的所有子對象都將渲染為紋理,第一個紋理的網格作為整個模型的網格。並非所有的Transform都會創建模型對象。一些Transform值只是在渲染器中簡單添加著色器和uniform變數(比如使用
blur
和alpha
的Transform)。 其他Transform只影響幾何體(geometry)。Render
當Transform對象的
mesh
屬性(attribute)為True時,將創建模型對象。 這種情況下,Render對象的所有子對象將被渲染為紋理,第一個紋理的網格作為整個模型的網格。
未來Ren’Py將添加更多創建模型的方法。
生成著色器程序 link
Ren’Py生成著色器程序的第一步是識別著色器名稱列表。該列表包含 “renpy.geometry”,即從Render對象獲取的著色器列表,以及模型對象繪製過程中用到的其他著色器。
接著所有著色器程序將被複製一份。以“-”開頭的著色器將會從複製後的列表中刪除,以及同名但開頭不是“-”的著色器也刪除。 (名為“-renpy.geometry”的著色器會導致自身和“renpy.geometry”都被刪除)
接著,Ren’Py將根據列表中的著色器名,檢索變數、函數、頂點著色器(vertex shade)和片元(fragment shader)列表, 並按優先度數值從小到大的順序依次生成著色器原始碼。優先度數值定義在頂點著色器和片元著色器中。
這同時意味著,某一個著色器創建的變數,可以被同一個著色器列表中的其他著色器訪問到。 與Python函數不同,著色器之間沒有作用域的概念。
Ren’Py會將使用過的所有著色器組合快取在 game/cache/shaders.txt 文件中,並在啟動時載入這個文件。 如果使用著色器方面有比較大改動,就需要編輯清空或刪除這個文件。這樣就可以重新生成有效數據。
創建自訂著色器 link
透過調用 renpy.register_shader 函數可以基於GLSL規範創建新的著色器。
著色器名的格式必須是“命名空間.著色器名稱”,比如“mygame.recolor”和“mylibrary.warp”。 Ren’Py已經占用了“renpy.”和“live2d.”兩個命名空間,所有以下劃線“_”開頭的命名空間也是預留的不可使用。
- renpy.register_shader(name, **kwargs) link
該函數註冊一個著色器名。其使用入參 name 和其他關鍵字參數:
- name
指定著色器名稱的字串。以下劃線或“renpy.”開頭的名稱已經被Ren’Py預留。
- variables
著色器使用的各個變數。每行一個變數,儲存類型(uniform、attribute或varying)後面跟變數類型、變數名稱,結尾用分號。舉例:
variables=''' uniform sampler2D tex0; attribute vec2 a_tex_coord; varying vec2 v_tex_coord; '''
- vertex_functions
如果給定,該字串會用作頂點著色器函數。
- fragment_functions
如果給定,該字串會用作片元著色器函數。
著色器函數相關的兩個關鍵字入參應該以
vertex_
或fragment_
開頭,結尾帶一個整數表示優先度,比如“fragment_200”和“vertex_300”。 這些優先度數值會決定著色器的應用方式,優先度數值低的函數會插入到優先度數值高的函數前面執行。
Ren’Py只支持以下變數類型:
float (Python中的浮點數)
vec2 (兩個浮點數組成的向量)
vec3 (三個浮點數組成的向量)
vec4 (四個浮點數組成的向量)
int (Python整型)
ivec2 (二元整型向量)
ivec3 (三元整型向量)
ivec4 (四元整型向量)
bool (Python布爾型)
bvec2 (二元布爾型向量)
bvec3 (三元布爾型向量)
bvec4 (四元布爾型向量)
type[n] ([n]個類型為[type]元素組成的向量 (例如,int[5]表示5個整型元素組成的數組))
mat2 (
Matrix
類)mat3 (
Matrix
類)mat4 (
Matrix
類)sampler2D (一個可視組件或指向可視組件的字串,或者一個Render對象)
uniform變數開頭必須為 u_,attribute變數開頭必須為 a_,varying變數開頭必須為 v_。 以 u_renpy_、 a_renpy 和 v_renpy 開頭的變數都是Ren’Py預留變數名,不能用在自訂著色器中。
概覽優先度的情況,優先度100設置幾何體(geometry),優先度200決定初始片元色彩(gl_FragColor),更高數值優先度才能實際影響和改變片元色彩。
這裡有一個自訂著色器樣例,實現模型的色彩漸變:
init python:
renpy.register_shader("example.gradient", variables="""
uniform vec4 u_gradient_left;
uniform vec4 u_gradient_right;
uniform vec2 u_model_size;
varying float v_gradient_done;
attribute vec4 a_position;
""", vertex_300="""
v_gradient_done = a_position.x / u_model_size.x;
""", fragment_300="""
gl_FragColor *= mix(u_gradient_left, u_gradient_right, v_gradient_done);
""")
自訂著色器可以用作一個變換(transform):
transform gradient:
shader "example.gradient"
u_gradient_left (1.0, 0.0, 0.0, 1.0)
u_gradient_right (0.0, 0.0, 1.0, 1.0)
show eileen happy at gradient
正如開頭所說,example.gradient著色器中的變數 gradient_done
可以被同一個列表中的其他著色器讀寫。
可以使用這個特點,在指定的著色器系統中使用一些可選著色器,但也可能導致兩個獨立著色器之間產生命名衝突。
GLSL版本 不同平台能夠支持的GLSL版本不盡相同,最嚴格的還是手機和Web平台,只能使用GLSL ES 1.00。 Ren’Py定義變數的精度非常高,浮點數範圍從 2-62 到 262, 整數範圍從 -216 到 216。 硬體通常可以支持更大的數值範圍,但沒什麼必要。
還有一個變數可用於自訂著色器的debug:
- define config.log_gl_shaders = False link
若該配置項為True,GLSL著色器程序的原始碼會在啟動階段寫入 log.txt 文件中。
著色器本地變數 link
著色器使用的本地變數可以用 u__
、a__
、v__
或 l__
作為前綴。
接著,所有的雙下劃線都會用著色名補充完整,著色器名中的“.”則會再替換為單下劃線。
例如,著色器叫 example.gradient
,前綴 u__
會被替換為 u_example_gradient_
。
該功能的主要用於 文本著色器 ,大多數uniform變數都是著色器本地變數。
另外,著色器內部定義的變數都需要使用前綴 l__
。
Transform類和基於模型的渲染 link
基於模型的渲染功能在ATL和 Transform()
類中添加了下面兩個特性:
- mesh link
- Type:
None 或 True 或 元組
- Default:
None
若該值不是None,Transform對象將作為模型渲染。同時意味著:
將創建一個網格。如果值是一個2元元組,將分別使用兩個數值作為網格的x和y方向大小(任意方向至少為2)。如果值是True,根據子對象創建網格。
該變換的子對象將渲染為紋理。
默認情況下,網格使用的問題不會生成mipmap。如果需要mipmap,可以將
gl_mipmap
設置為True。
- mesh_pad link
- Type:
None 或 元組
- Default:
None
若該值不是None,其可能是2元或4元元組。 如果mesh的值是True,mesh_pad表示網格紋理的四邊留白大小。 2元元組分別對應紋理的右側和底部留白,4元元組分別對應紋理的左、頂、右、底留白。
該特性可以與 pixel_perfect 一起使用,將文本渲染為網格。 在Ren’Py中,文本渲染與螢幕解析度有關,可能出現超出紋理無法覆蓋網格的情況。 添加一些留白會讓紋理稍微大一點,可以完整顯示所有像素。例如:
transform adjust_text: mesh True mesh_pad (10, 0) gl_pixel_perfect True shader "shaders.adjust_text"
可以確保傳入著色器的紋理包含文本的所有像素。
- shader link
- Type:
None 或 字串 或 字串列表
- Default:
None
若該值不是None,根據字串或字串列表將對應的著色器應用到Render對象(如果創建了模型對象)或在Render對象樹上該Render對象分支後面的所有模型。
- blend link
- Type:
None 或 str
- Default:
None
若該值不是None,其應該是一個字串。根據該字串在
config.gl_blend_func
搜索對應的遮罩函數gl_blend_func特性後,用作圖像遮罩模式。預設的遮罩模式包括“normal”(正常或覆蓋)、“add”(相加)、“multiply”(相乘或正片疊底)、“min”(最小值)和“max”(最大值)。
以 u_ 而非 u_renpy 開頭的uniform型變數可以當作Transform的特性(property)來使用。 以 gl_ 開頭的GL特性(property)變數可以當作Transform的特性(property)來使用。 例如,想使用GL中的 color_mask 特性,在Transform中需要改為 gl_color_mask。
遮罩函數 link
- define config.gl_blend_func = { ... } link
一個字典型數據,用作遮罩模式名與遮罩函數的映射關係。 遮罩模式會賦值給 gl_blend_func ,詳見一下表。
預設的遮罩模式有:
gl_blend_func["normal"] = (GL_FUNC_ADD, GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_FUNC_ADD, GL_ONE, GL_ONE_MINUS_SRC_ALPHA)
gl_blend_func["add"] = (GL_FUNC_ADD, GL_ONE, GL_ONE, GL_FUNC_ADD, GL_ZERO, GL_ONE)
gl_blend_func["multiply"] = (GL_FUNC_ADD, GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA, GL_FUNC_ADD, GL_ZERO, GL_ONE)
gl_blend_func["min"] = (GL_MIN, GL_ONE, GL_ONE, GL_MIN, GL_ONE, GL_ONE)
gl_blend_func["max"] = (GL_MAX, GL_ONE, GL_ONE, GL_MAX, GL_ONE, GL_ONE)
由於Ren’Py會使用alpha預乘,導致某些像素的最終結果是半透明的。 在GPU中,顏色(r, g, b, a)會變成(r * a, g * a, b * a, a),並傳給遮罩函數。 這種機制可能會導致渲染出的顏色與繪圖軟體中的結果不一致。
float、sampler和vector類型uniform link
以下uniform型變數對所有模型都可用。
vec2 u_model_size
模型的寬度和高度,會傳給Ren’Py。該變數僅對2D模型生效,3D模型固定為(0, 0)。
float u_lod_bias
紋理尋找時細節等級(lod)的bias偏移。可以在一個變換對象中設置。
config.gl
配置項是其預設值,通常設置為-0.5。這樣設置會讓Ren’Py使用更高的採樣級別,然後縮小。float u_time
該幀的時間。由於Epoch(譯者註:1970年1月1日00:00:00 UTC)可能沒有明確定義,所以最好把這個值看作是以秒為單位的數值,並按86400取模,每過一天都歸零。
vec4 u_random
4個介於0.0到1.0之間的隨機數。每幀生成的隨機數都不同(儘管可能比較相近)。
vec4 u_viewport
該項指定當前繪製的視口(viewport)。 u_viewport.xy表示從視口左下角為原點的坐標。 u_viewport.pq表示視口的寬度和高度。
vec2 u_virtual_size
遊戲的虛擬解析度(
config.screen_width
,config.screen_height
)。 從gl_Position轉化為虛擬坐標可以使用如下代碼:v_position = u_virtual_size * vec2(gl_Position.x * .5 + .5, -gl_Position.y * .5 + .5)
vec2 u_drawable_size
窗口的可繪製區域尺寸,單位是像素,一般等於遊戲運行時的解析度。 例如1280×720解析度的遊戲拉伸到1920×1080運行,那麼此項的值就是(1920, 1080)。
sampler2D tex0
,sampler2D tex1
,sampler2D tex2
如果紋理可用,對應的sampler2D類型數據可以存入這些變數。
vec2 res0
,vec2 res1
,vec2 res2
如果紋理可用,紋理的尺寸資訊將存入這些變數。當紋理是從磁碟載入時,這些數值就是圖片文件的尺寸。 在渲染為紋理後,這些數值是實際可繪製像素的最小外接矩形的尺寸。
若某個sampler型uniform變數可用,並且後綴為 __res
,該變數會指定一個vec2向量,包含紋理尺寸資訊。
例如,u_markup__res
中包含紋理 u_markup
的尺寸。
若某個vec4向量可用,並且後綴為 __premul
,該變數會指向一個vec4向量,包含預乘之後的顏色資訊。
例如,u_color__premul
表示一個經過alpha預乘後的RGBA顏色值。
矩陣類型uniform link
以下uniform型變數對所有模型都可用。假設使用 perspective
特性渲染某個模型時只會應用單個變換。
perspective
中使用多個變換的情況下,最內層的透視變換將設置世界空間(world space)和視圖空間(view space)。
mat4 u_projection
該變數是一個矩陣,將視圖空間坐標轉換到OpenGL的視口(viewport)。 該變數初始值由Ren’Py生成,可以由帶
perspective
特性的變換修改。 結合起來決定最終投影效果。mat4 u_view
該變數是一個矩陣,將模型頂點坐標從世界空間轉換到視圖空間。 該變數預設值是單位矩陣,可以由帶
perspective
特性的變換修改。 結合起來決定平移、旋轉和縮放效果。mat4 u_model
該變數是一個矩陣,將模型頂點坐標從模型空間轉換到世界空間。
mat4 u_projectionview
該矩陣包含
u_projection * u_view
。其會最小化發送給GPU處理和在著色器中使用的uniform變數。mat4 u_transform
該矩陣與
u_projectionview * u_model
相同。其將頂點坐標直接傳給OpenGL視口。 其會最小化發送給GPU處理和在著色器中使用的uniform變數,並盡可能相容舊版本Ren’Py。mat4 u_transform
此變換(transform)將項目虛擬像素轉換為OpenGL的視口(viewport)。
除了以上方法,Ren’Py也提供了一些矩陣的常用操作,可以通過後綴方式直接應用到矩陣。
__inverse
矩陣的逆矩陣。例如,
u_projection__inverse
表示投影矩陣的逆矩陣。__transpose
矩陣的轉置矩陣。例如,
u_view__transpose
表示視圖矩陣的轉置矩陣。__inverse_transpose
某個矩陣先轉置,再求逆矩陣。例如,
u_model__inverse_transpose
表示模型矩陣轉置後的逆矩陣。 該項在歸一化變換的值時很有用。
attribute link
以下attribute型變數對所有模型都可用。
vec4 a_position
待渲染頂點位置資訊。該值表示虛擬像素,以紋理左上角為原點。
如果紋理可用,還有下面的attribute型變數:
vec2 a_tex_coord
頂點相對紋理內部的坐標。
如果法線(normal)可用,還有下面的attribute型變數:
vec3 a_normal
待渲染頂點法線資訊。
如果切線(tangent)可用,還有下面的attribute型變數:
vec3 a_tangent
待渲染頂點切線資訊。
vec3 a_bitangent
待渲染頂點副切線資訊。
GL特性(property) link
GL特性會更改OpenGL或基於模型渲染器的全局狀態。
這些特性可以與Transform對象一起使用,或者 Render.add_property()
函數一起使用。
gl_blend_func
該特性應是一個6元元組,用作一個方程的計算。此方程會根據遮罩像素、被遮罩像素等資訊算出最終結果。 分別用作功能調節像素、原像素、遮罩像素和功能條件像素相關參數。
一個例子是(rgb_equation, src_rgb, dst_rgb, alpha_equation, src_alpha, dst_alpha)。 調用方式如下:
glBlendEquationSeparate(rgb_equation, alpha_equation) glBlendFuncSeparate(src_rgb, dst_rgb, src_alpha, dst_alpha)
這些函數的功能請參考OpenGL文件。 OpenGL常量可以從renpy.uguu中引入:
init python: from renpy.uguu import GL_ONE, GL_ONE_MINUS_SRC_ALPHA
更通用的建議方式是使用
blend
變換特性。gl_color_mask
該特性應是一個布爾型4元元組,分別對應像素中的4個通道(紅、綠、藍和alpha)。只有當元組中對應通道的元素值為True時,繪圖操作才會實際繪製像素的顏色值。
gl_cull_face
若非None,該特性將啟用“面剔除(face culling)”,移除背對相機的三角形。 為了實現這個目標,我們需要區分正面與背面。正面可以根據組成改三角面所有頂點圍繞順序,比如“cw”(順時針)或“ccw”(逆時針),來判斷。 得到的結果會經過Ren’Py坐標系統轉換(Y軸反向),因為Ren’Py與OpenGL的坐標系統不同。
gl_depth
若為True,將會清理深度快取,然後啟用該可視組件和其子組件的深度渲染。
注意,繪製任意像素甚至是透明像素,都會更新深度快取。因此,對包含半透明像素的圖片使用該特性可能會導致無法預料的問題。 (替代方案為,
show
語句中使用zorder
和behind
從句。)gl_pixel_perfect
只有創建網格時該特性才會生效。若該值是True,Ren’Py會把網格的第一個頂點與螢幕某個像素對齊。 該特性常用於文本內容的銜接,確保文字的清晰度。
gl_drawable_resolution
若為True或未設置,紋理將以遊戲窗口的相同解析度渲染。若為False,紋理將以可視組件的虛擬解析度渲染。
gl_anisotropic
該特性決定了,應到網格上的紋理是否創建各向異性(anisotropy)。 各向異性是一種功能特性,能讓紋理在X和Y方向的縮放係數不同時,採樣出多個紋理元素(texel)。
該項預設為True。Ren’Py將其設置為False,為了避免對其他效果產生影響,比如Pixellate(像素化)轉場。
gl_mipmap
該項決定紋理是否提供了網格用於創建mipmap。預設值為True.
gl_texture_wrap
該項決定了將紋理映射到網格的方式。其值應該是一個2元元組, 第一個元素用於設置GL_TEXTURE_WRAP_S,第二個元素用於設置GL_TEXTURE_WRAP_T, 這兩項通常用作紋理的X和Y軸映射方式。
這些OpenGL常量應從renpy.uguu中引入:
init python: from renpy.uguu import GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT, GL_REPEAT
該項可以針對不同紋理設置不同的值。gl_texture_wrap_tex0 控制第一紋理,gl_texture_wrap_tex1 控制第二紋理, gl_texture_wrap_tex2 控制第三紋理,gl_texture_wrap_tex3 控制第四紋理。在Transform對象中只能使用這4個默認紋理。 如果需要同時使用更多紋理則可以使用Render.add_property添加“texture_wrap_tex4”或者“texture_wrap_myuniform”等。
GLTF模型可視組件 link
GLTF模型可視組件可以讓創作者載入GLTF檔案格式的3D模型。 如果創作者在其他程序中創建了3D模型,並希望在Ren’Py中顯示,可以使用下面的類。
- class GLTFModel(renpy.display.displayable.Displayable) link
此類是一個可視組件,能夠載入GLTF格式的3D模型。很多3D製作軟體都可以導出GLTF格式的模型。 Ren’Py使用 開放的資產導入庫(assimp) 載入GLTF模型。
為了不影響Ren’Py的2D布局系統,GLTFModel對象的寬和高都是0。默認情況下,模型的初始大小由載入文件中的資訊決定。 若有需要,可以使用 zoom 縮放。
同時使用多個模型時,應對相機應用
gl_depth True
特性,以啟用深度測試(depth test)。 Ren’Py目前還未實現渲染時的模型剔除(culling),最好盡可能使用足夠簡單的模型。- filename
模型檔案名。
- shader
該參數是一個字串或字串元組,表示用到的著色器名稱。
- tangents
若為True,表示網格中包含切線資訊。
- zoom
一個模型縮放係數。很多模型原生的尺寸數據範圍使用-1到1,所以可能需要將該係數設置得很大才能看到模型。
- report
若為True,模型相關資訊會列印到日誌中。其中包括渲染模型用到的uniform變數。
其他注意事項:
遊戲模型非常小,需要放到100倍甚至1000倍才能看到。
各模型內部的uniform變數可能各不相同。
我們推薦創建GLTF模型對象時將report設置為True,查看log.txt查看模型尺寸資訊。 然後再根據這些資訊縮放模型並創建合適的著色器。
限制 link
GLTFModel對象未包含一個默認著色器,創作者需要自己定義並添加。下面是一個可用於GLTFModel的著色器樣例:
init python:
renpy.register_shader("example.lighting", variables="""
uniform mat4 u_model__inverse_transpose;
uniform vec4 u_color_diffuse;
uniform vec4 u_color_specular;
uniform sampler2D u_tex_diffuse;
varying vec3 v_normal;
varying vec2 v_tex_coord;
attribute vec3 a_normal;
attribute vec2 a_tex_coord;
""", vertex_201="""
v_normal = (u_model__inverse_transpose * vec4(a_normal, 1.0)).xyz;
v_tex_coord = a_tex_coord;
""", fragment_201="""
// 光的方向。
vec3 lightDir = normalize(vec3(0.0, -1000.0, 1000.0));
vec3 normal = normalize(v_normal);
float lambertian = max(dot(normal, lightDir), 0.0);
vec4 diffuse_color = texture2D(u_tex_diffuse, v_tex_coord.xy);
diffuse_color *= vec4(lambertian * u_color_diffuse.rgb * u_color_diffuse.a, u_color_diffuse.a);
vec3 viewDir = normalize(vec3(0.0, 0.0, -1.0));
vec3 halfDir = normalize(lightDir + viewDir);
float specular = pow(max(dot(normal, halfDir), 0.0), 4.0);
vec4 specular_color = vec4(u_color_specular.rgb * u_color_specular.a * specular, u_color_specular.a * specular);
gl_FragColor = diffuse_color + specular_color;
if (gl_FragColor.a < 0.9) {
discard;
}
""")
這是一個非常簡單的著色器,計算出單一光源在模型表面的漫反射(diffuse)和鏡面反射(specular), 主要用於測試,可以用更好的著色器替換。
(譯者註:上面的著色器中,使用了“蘭伯特(Lambert)光照模型”計算漫反射,使用了“布林-馮(Blinn-Phong)光照模型”計算鏡面反射。若有興趣可根據對應關鍵字搜索擴展資訊。)
GLTFModel還不支持半透明表面。因為模擬半透明的discard命令執行順序有誤,會導致渲染瑕疵。
模型可視組件 link
模型可視組件(model displayable)就像是使用基於模型渲染器創建並使用模型的一個工廠。
- class Model(size=None) link
該類是一種可視組件,讓Ren’Py使用基於模型渲染器創建一個2D或3D模型,並根據指定渲染器繪製模型, 或放入指定的Transform(或Displayable)對象中。
- size
若非None,該參數應是一個分辨便是寬度和高度的二元元組,用於模型的尺寸。 若沒有指定,模型的尺寸將與其占用的區域一致。合適的參數也將影響模型使用到紋理的大小。
如果沒有調用 mesh 方法,且存在至少一個紋理,Ren’Py將根據載入紋理時的方式設置網格的 a_postion 和 a_tex_coord 值。 否則,將只設置網格的a_postion。
該類對象的所有方法被調用後,都將返回對象自身,因此可以連鎖調用。
- grid_mesh(width, height) link
生成一個width×height的空間點網格,將所有點在水平和垂直方向上距離最近的其他點連接生成矩形,然後將矩形沿對角線切割生成三角形網格。
- width, height
水平和垂直方向上點的數量,都必須是不小於2的整數。
- texture(displayable, focus=False, main=False, fit=False) link
通過指定可視組件,為該模型添加紋理。 第一張紋理會成為
tex0
,第二張紋理則是tex1
,以此類推。- focus
若為True,獲得焦點事件將傳給可視組件。 默認作為入參的可視組件坐標以1:1映射到紋理。
- main
若為True,作為入參的可視組件將作為模型的主要子組件,可以被可視組件檢測器探知。
- fit
若為True,根據可視組件的尺寸創建模型。 只有在單一紋理的情況下才可以設置為True。
- child(displayable, fit=False) link
該方法與texture方法相同,除了 focus 和 main 參數已被設置為True。
- shader(shader) link
為模型添加著色器(shader)。
- shader
表示著色器名稱的字串,並將應用到模型上。
- uniform(name, value) link
設置著色器中用到的各uniform型變數的值。
- name
一個字串,表示uniform型變數的名稱,前綴是“u_”。
- value
uniform型變數的值。可以是浮點型數值,2元、3元、4元數組,或者矩陣。
- property(name, value) link
設置GL特性(property)的值。
- name
一個字串,表示GL特性的名稱,前綴是“gl_”。
- value
GL特性的值。
模型可視組件樣例 link
模型可視組件可用在ATL變換中間,並使用內建著色器(shader)創建溶解(dissolve)變換效果。
transform dt(delay=1.0, new_widget=None, old_widget=None):
delay delay
Model().texture(old_widget).child(new_widget)
shader [ 'renpy.dissolve' ]
u_renpy_dissolve 0.0
linear delay u_renpy_dissolve 1.0
將Model對象作為某個可視組件的子組件時,與 mesh
不相容,因為兩者都會在Ren’Py內部創建模型。
帶動畫的著色器 link
當著色器依靠 u_time
來實現動畫是,就要特別注意了。
Ren’Py並沒有固定的刷新率,就算滿屏的各個著色器都在每幀顯示不同內容,
如果沒有可視組件申請重繪,依然可能會把刷新率降低到5fps。
當在ATL變換中使用帶動畫的著色器時,可能會出現卡頓。 但同時螢幕上的其他動畫卻運行順暢,原因可能就是使用著色器的變換沒有申請重繪。 為了解決此類問題,可以寫一個空的ATL循環,強制重繪:
transform fancy_shader:
shader 'my_fancy_shader'
pause 0
repeat
pause 0
會盡可能快地循環繪製。創作者可以修改 pause
時間,來設置自己需要的最小刷新率,比如 pause 1.0/30
。
Shader Parts link
Ren’Py使用的著色器請詳見 默認著色器名稱。
默認著色器名稱 link
renpy.geometry (priority 100) link
變數列表:
uniform mat4 u_transform;
attribute vec4 a_position;
頂點著色器:
gl_Position = u_transform * a_position;
renpy.blur (priority 200) link
變數列表:
uniform sampler2D tex0;
attribute vec2 a_tex_coord;
varying vec2 v_tex_coord;
uniform float u_renpy_blur_log2;
頂點著色器:
v_tex_coord = a_tex_coord;
片元著色器:
gl_FragColor = vec4(0.);
float renpy_blur_norm = 0.;
for (float i = 0.; i < u_renpy_blur_log2 + 5.; i += 1.) {
float renpy_blur_weight = exp(-0.5 * pow(u_renpy_blur_log2 - i, 2.));
gl_FragColor += renpy_blur_weight * texture2D(tex0, v_tex_coord.xy, i);
renpy_blur_norm += renpy_blur_weight;
}
gl_FragColor /= renpy_blur_norm;
renpy.dissolve (priority 200) link
變數列表:
uniform float u_lod_bias;
uniform sampler2D tex0;
uniform sampler2D tex1;
uniform float u_renpy_dissolve;
attribute vec2 a_tex_coord;
varying vec2 v_tex_coord;
頂點著色器:
v_tex_coord = a_tex_coord;
片元著色器:
vec4 color0 = texture2D(tex0, v_tex_coord.st, u_lod_bias);
vec4 color1 = texture2D(tex1, v_tex_coord.st, u_lod_bias);
gl_FragColor = mix(color0, color1, u_renpy_dissolve);
renpy.imagedissolve (priority 200) link
變數列表:
uniform float u_lod_bias;
uniform sampler2D tex0;
uniform sampler2D tex1;
uniform sampler2D tex2;
uniform float u_renpy_dissolve_offset;
uniform float u_renpy_dissolve_multiplier;
attribute vec2 a_tex_coord;
varying vec2 v_tex_coord;
頂點著色器:
v_tex_coord = a_tex_coord;
片元著色器:
vec4 color0 = texture2D(tex0, v_tex_coord.st, u_lod_bias);
vec4 color1 = texture2D(tex1, v_tex_coord.st, u_lod_bias);
vec4 color2 = texture2D(tex2, v_tex_coord.st, u_lod_bias);
float a = clamp((color0.a + u_renpy_dissolve_offset) * u_renpy_dissolve_multiplier, 0.0, 1.0);
gl_FragColor = mix(color1, color2, a);
renpy.solid (priority 200) link
變數列表:
uniform vec4 u_renpy_solid_color;
片元著色器:
gl_FragColor = u_renpy_solid_color;
renpy.texture (priority 200) link
變數列表:
uniform float u_lod_bias;
uniform sampler2D tex0;
attribute vec2 a_tex_coord;
varying vec2 v_tex_coord;
頂點著色器:
v_tex_coord = a_tex_coord;
片元著色器:
gl_FragColor = texture2D(tex0, v_tex_coord.xy, u_lod_bias);
renpy.matrixcolor (priority 400) link
變數列表:
uniform mat4 u_renpy_matrixcolor;
片元著色器:
gl_FragColor = u_renpy_matrixcolor * gl_FragColor;
renpy.alpha (priority 500) link
變數列表:
uniform float u_renpy_alpha;
uniform float u_renpy_over;
片元著色器:
gl_FragColor = gl_FragColor * vec4(u_renpy_alpha, u_renpy_alpha, u_renpy_alpha, u_renpy_alpha * u_renpy_over);