모델 기반 렌더링
Ren'Py는 주로 비주얼 노벨에서 흔히 쓰는 2차원 사각형 이미지를 다루지만, 내부적으로는 최신 GPU의 기능을 활용하는 모델 기반 렌더러를 갖추고 있습니다. 이를 통해 다른 방식으로는 구현하기 어려운 여러 시각 효과를 만들 수 있습니다.
이것은 Ren'Py의 가장 고급 기능 중 하나입니다. 대부분은 모델 기반 렌더링의 내부 작동 원리를 이해할 필요가 없습니다. matrixcolor나 Live2D 지원은 원리를 몰라도 사용할 수 있으며, 이런 기능은 앞으로 더 추가될 예정입니다. 이 문서는 매우 숙련된 제작자와 Ren'Py 자체에 기능을 추가하려는 개발자를 대상으로 합니다.
모델 기반 렌더링은 매우 고급 기능이므로 OpenGL, OpenGL ES, GLSL, GLSL ES 설명서를 먼저 읽지 않으면 이해하기 어려울 수 있습니다. 또한 모델의 일부는 GPU 드라이버로 직접 전달되며 드라이버가 잘못된 입력을 받아들일 수도 있으므로 여러 종류의 하드웨어에서 확인해야 합니다.
모델, Render, 그리기 연산
Ren'Py가 화면에 그리는 기본 단위는 Model입니다. 모델은 다음 요소로 이루어집니다.
하나 이상의 삼각형으로 이루어진 메시. 삼각형에는 세 정점이 있으며, 각 정점은 2차원 또는 3차원 위치와 추가 정보를 담을 수 있습니다. 가장 흔한 추가 정보는 텍스처 좌표입니다.
0개 이상의 텍스처. 허용되는 정확한 개수는 게임을 실행할 GPU에 따라 제한됩니다. 모든 GPU는 모델당 최소 세 텍스처를 지원해야 합니다. 텍스처는 직접 불러오거나 텍스처로 렌더링하여 GPU에 올린 이미지 데이터를 담은 사각형입니다.
셰이더 파트 이름 리스트. Ren'Py는 이 파트들로 모델을 렌더링하는 GPU 프로그램인 셰이더를 만듭니다. 파트 이름 앞에 "-"를 붙이면 해당 파트를 사용하지 않도록 할 수 있습니다.
유니폼 값. 유니폼은 모델 전체에서 동일한 추가 데이터입니다. 예를 들어 모델이 단색을 나타내면 그 색상이 유니폼입니다.
GL 속성. 축소/확대 모드나 색상 마스크처럼 렌더링 방식을 추가 제어하는 플래그입니다.
Ren'Py는 보통 화면에 여러 요소를 그리므로 Render 객체의 트리를 만듭니다. Render 객체는 Model이나 다른 Render를 자식으로 가질 수 있습니다. 아래 설명처럼 Render 자체를 Model로 바꿀 수도 있습니다. Render에는 다음이 포함됩니다.
각 자식에 적용할 2차원 오프셋을 포함한 자식 리스트.
자식을 3차원 공간에서 어떻게 변환할지 나타내는
Matrix.모델을 그릴 때 적용하는 셰이더 파트 이름, 유니폼, GL 속성의 리스트.
그리기 공간의 클리핑 다각형을 갱신할지 결정하는 플래그.
Ren'Py는 Render 트리를 깊이 우선으로 탐색하며 Model을 만나면 그립니다. 탐색 중 모델의 위치 변환 행렬, 클리핑 다각형, 셰이더 파트, 유니폼, GL 속성의 목록을 갱신합니다. Model을 만나면 GPU에서 알맞은 셰이더 프로그램을 활성화하고 모든 정보를 전달하여 그리기 연산을 수행합니다.
모델이 생성되는 위치
Ren'Py는 일반 동작 과정에서 모델을 자동으로 만듭니다. 모델은 그리기 연산에 대응하고 셰이더가 적용되는 단위이므로, 어디서 만들어지는지 이해하면 도움이 됩니다.
- 이미지와 이미지 매니퓰레이터
이미지의 사각형을 덮는 삼각형 두 개의 메시로 모델을 만듭니다. 메시에는 텍스처 좌표가 있으며, 모델은 "renpy.texture" 셰이더를 사용합니다.
Solid()Solid 디스플레이어블은 삼각형 두 개로 이루어지고 텍스처 좌표가 없는 메시를 만듭니다. "renpy.solid" 셰이더를 사용하며 색상은
u_renpy_solid_color유니폼에 넣습니다.Dissolve(),ImageDissolve(),AlphaDissolve(),Pixellate(),AlphaMask(),Flatten()이 트랜스폼과 디스플레이어블은 각 목적에 필요한 메시, 셰이더, 유니폼으로 Model을 만듭니다.
- Live2D
Live2D 디스플레이어블은 렌더링 시 여러 Model을 만들 수 있으며, 일반적으로 레이어당 하나를 만듭니다.
Transform()과 ATLTransform은
mesh가 True이거나blur를 사용하면 모델을 만듭니다. 자식들을 텍스처로 렌더링하고 첫 번째 텍스처의 메시를 모델의 메시로 사용합니다.모든 트랜스폼이 Model을 만드는 것은 아닙니다.
blur나alpha를 사용하는 일부 트랜스폼은 Render에 셰이더와 유니폼만 추가합니다. 기하 구조에만 영향을 주는 트랜스폼도 있습니다.RenderTransform은
mesh속성이 True이면 모델을 만듭니다. Render의 자식들을 텍스처로 렌더링하고 첫 번째 텍스처의 메시를 모델의 메시로 사용합니다.
앞으로 Ren'Py에는 모델을 만드는 방법이 더 추가될 예정입니다.
셰이더 프로그램 생성
Ren'Py는 먼저 셰이더 파트 이름 리스트를 모아 셰이더 프로그램을 생성합니다. 이 리스트에는 "renpy.geometry", Render에서 가져온 파트들, 그릴 Model의 파트들이 들어갑니다.
그다음 중복 파트를 제거합니다. "-"로 시작하는 파트는 자신과 "-"를 뗀 이름의 파트를 모두 제거합니다. 따라서 "-renpy.geometry"는 자신과 "renpy.geometry"를 모두 제거합니다.
이후 파트 리스트에서 변수, 함수, 정점 셰이더 파트, 프래그먼트 셰이더 파트의 목록을 얻어 셰이더 소스 코드를 만듭니다. 정점 및 프래그먼트 셰이더 파트는 우선순위 숫자가 낮은 것부터 높은 것 순서로 포함됩니다.
따라서 어떤 셰이더가 만든 변수든 같은 파트 리스트의 다른 셰이더 조각에서 접근할 수 있습니다. Python 함수처럼 셰이더 사이의 간섭을 막아 주는 스코프는 없습니다.
Ren'Py는 사용된 모든 셰이더 파트 조합을 game/cache/shaders.txt에 캐시하고 시작할 때 불러옵니다. 셰이더 사용 방식이 크게 바뀌면 이 파일을 수정하거나 삭제하여 유효한 데이터로 다시 만들게 해야 합니다.
사용자 정의 셰이더 만들기
renpy.register_shader를 호출하고 GLSL 셰이더의 일부를 전달하면 새 셰이더 파트를 만들 수 있습니다.
파트 이름은 일반적으로 "mygame.recolor", "mylibrary.warp" 같은 "namespace.part" 형태여야 합니다. "renpy.", "live2d.", _로 시작하는 이름은 Ren'Py 용도로 예약되어 있습니다.
- renpy.register_shader(name, **kwargs)
셰이더 파트를 등록합니다. 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 (실수 2개의 튜플)
vec3 (실수 3개의 튜플)
vec4 (실수 4개의 튜플)
int (Python 정수)
ivec2 (정수 2개의 튜플)
ivec3 (정수 3개의 튜플)
ivec4 (정수 4개의 튜플)
bool (Python 불리언)
bvec2 (불리언 2개의 튜플)
bvec3 (불리언 3개의 튜플)
bvec4 (불리언 4개의 튜플)
type[n] (위 형식 중 하나인 [type] 요소 [n]개의 배열. 예를 들어 int[5]는 정수 5개의 배열입니다.)
mat2 (
Matrix)mat3 (
Matrix)mat4 (
Matrix)sampler2D (디스플레이어블. 디스플레이어블을 나타내는 문자열이나 Render도 가능합니다.)
유니폼 변수는 u_, attribute는 a_, varying 변수는 v_로 시작해야 합니다. u_renpy_, a_renpy, v_renpy로 시작하는 이름과 아래의 표준 변수는 예약되어 있습니다.
우선순위의 일반적인 기준은 다음과 같습니다. 100에서 기하 구조를 설정하고, 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="""
float gradient_done = v_gradient_done;
gl_FragColor *= mix(u_gradient_left, u_gradient_right, gradient_done);
""")
이후 트랜스폼으로 사용자 정의 셰이더를 적용할 수 있습니다.
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 버전은 플랫폼에 따라 다르지만, 가장 제약이 큰 모바일과 웹에서는 GLSL ES 1.00을 사용합니다. Ren'Py는 변수를 highp 정밀도로 선언하며, 이는 2-62부터 262까지의 부동소수점 수를 지원합니다. 0이 아닌 부동소수점 수의 절댓값은 2-62부터 262 사이여야 하고, 정수는 -216부터 216 사이여야 합니다. 실제 하드웨어는 더 넓은 범위를 지원하는 경우가 많지만 보장되지는 않습니다.
사용자 정의 셰이더 디버깅을 돕는 변수가 있습니다.
- define config.log_gl_shaders = False
참이면 시작 시 GLSL 셰이더 프로그램의 소스 코드를 log.txt에 기록합니다.
셰이더 파트의 지역 변수
변수 이름에 u__, a__, v__, l__ 접두사를 붙이면 셰이더 지역 변수로 선언할 수 있습니다. 이중 밑줄 부분은 셰이더 이름의 점을 모두 밑줄로 바꾼 문자열로 채워집니다. 예를 들어 이름이 example.gradient이면 u__는 u_example_gradient_로 바뀝니다.
주로 대부분의 유니폼이 셰이더 지역 변수인 텍스트 셰이더에서 사용합니다. 셰이더 내부의 지역 변수도 l__로 선언해야 합니다.
트랜스폼과 모델 기반 렌더링
모델 기반 렌더링은 ATL과 Transform()에 다음 속성을 추가합니다.
- mesh
- 형식:
None, True 또는 튜플
- 기본값:
None
None이 아니면 이 Transform을 모델로 렌더링합니다. 다음과 같이 처리됩니다.
메시를 만듭니다. 두 요소 튜플이면 x, y 방향의 메시 점 개수로 해석합니다. 각 방향은 최소 2여야 합니다. True이면 자식에서 메시를 가져옵니다.
트랜스폼의 자식을 텍스처로 렌더링합니다.
renpy.texture 셰이더를 추가합니다.
기본적으로 메시에서 사용하는 텍스처에 밉맵을 만들지 않습니다. 밉맵이 필요하면
gl_mipmap을 True로 설정해야 합니다.
- mesh_pad
- 형식:
None 또는 튜플
- 기본값:
None
None이 아니면 2개 또는 4개 요소의 튜플입니다. mesh가 True이고 이 값을 지정하면 메시가 사용하는 텍스처의 크기에 여백을 추가합니다. 두 요소이면 오른쪽과 아래쪽, 네 요소이면 왼쪽, 위쪽, 오른쪽, 아래쪽에 여백을 적용합니다.
gl_pixel_perfect와 함께 사용해 텍스트를 메시로 렌더링할 수 있습니다. Ren'Py는 화면 해상도로 텍스트를 렌더링하므로 메시에 적용할 텍스처의 경계를 넘을 수 있습니다. 몇 픽셀의 여백을 추가하면 텍스처가 커져 모든 픽셀이 표시됩니다. 예를 들어 다음은transform adjust_text: mesh True mesh_pad (10, 0) gl_pixel_perfect True shader "shaders.adjust_text"
셰이더에 전달하는 텍스처에 텍스트의 모든 픽셀이 포함되도록 보장합니다.
- shader
- 형식:
None, str 또는 str 리스트
- 기본값:
None
None이 아니면 이 Render에서 Model을 만들 때 적용하거나, 이 Render를 통해 도달하는 Model들에 적용할 셰이더 파트 이름 또는 이름 리스트입니다.
- blend
- 형식:
None 또는 str
- 기본값:
None
None이 아니라면 문자열이어야 합니다.
config.gl_blend_func에서 이 문자열을 찾아 gl_blend_func 속성 값을 얻습니다. 다른 혼합 모드를 사용하는 데 쓰입니다.기본적으로 "normal", "add", "multiply", "min", "max" 혼합 모드를 지원합니다.
또한 u_로 시작하되 u_renpy로 시작하지 않는 유니폼을 Transform 속성으로 사용할 수 있습니다. GL 속성은 gl_로 시작하는 트랜스폼 속성으로 제공됩니다. 예를 들어 color_mask는 gl_color_mask가 됩니다.
혼합 함수
- define config.gl_blend_func = { ... }
혼합 모드 이름을 혼합 함수에 연결하는 딕셔너리입니다. 아래의 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는 미리 곱한 알파를 사용하므로 픽셀이 불투명하지 않으면 일부 결과가 직관과 다를 수 있습니다. GPU에서는 색상 (r, g, b, a)를 (r * a, g * a, b * a, a)로 표현하고 혼합 함수도 이 색상을 사용합니다. 따라서 완전히 불투명하지 않은 요소를 그리면 그림 프로그램에서 같은 혼합 모드를 사용한 결과와 다를 수 있습니다.
실수, 샘플러, 벡터 유니폼
모든 모델에서 다음 유니폼을 사용할 수 있습니다.
vec2 u_model_sizeRen'Py에 전달한 모델의 너비와 높이입니다. 크기를 제공하는 2D 모델에서만 사용할 수 있으며, 3D 모델에서는 (0, 0)입니다.
float u_lod_bias텍스처 조회에 적용할 세부 수준 편향입니다. Transform에서 설정할 수 있습니다.
config.gl_lod_bias에서 가져오는 기본값은 -0.5이며, 항상 한 단계 큰 수준을 골라 축소하는 쪽으로 편향시킵니다.float u_time프레임의 시간입니다. 에포크가 정의되지 않았으므로 초당 1씩 증가하는 숫자로 취급하는 것이 좋습니다. 86400으로 나눈 나머지이므로 하루에 한 번 0.0으로 돌아갑니다.
vec4 u_random0.0부터 1.0 사이의 난수 네 개입니다. 매우 높은 확률로 프레임마다 다릅니다.
vec4 u_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게임 실행 해상도에서 창의 그리기 영역 크기입니다. 단위는 픽셀입니다. 예를 들어 1280x720 게임을 1980x1080으로 확대하면 (1920, 1080)입니다.
sampler2D tex0,sampler2D tex1,sampler2D tex2텍스처가 있으면 대응하는 샘플러를 이 변수에 넣습니다.
vec2 res0,vec2 res1,vec2 res2텍스처가 있으면 그 크기를 이 변수에 넣습니다. 디스크에서 불러왔으면 이미지 파일의 크기이며, 텍스처로 렌더링한 뒤에는 렌더링된 텍스처가 차지한 그리기 픽셀 수입니다.
샘플러 유니폼에 __res를 붙이면 기반 텍스처 크기를 담은 vec2를 얻습니다. 예를 들어 u_markup__res는 u_markup 텍스처의 크기입니다.
vec4 유니폼에 __premul을 붙이면 미리 곱한 색상을 담은 vec4를 얻습니다. 예를 들어 u_color__premul은 빨강, 초록, 파랑 채널에 알파 채널을 곱한 vec4입니다.
행렬 유니폼
모든 모델에서 다음 유니폼을 사용할 수 있습니다. 모델을 렌더링할 때 perspective 속성이 있는 트랜스폼 하나만 사용한다고 가정합니다. perspective 트랜스폼을 여러 개 사용하면 가장 안쪽의 원근 설정이 월드 공간과 뷰 공간을 정의합니다.
mat4 u_projection뷰 공간 좌표를 OpenGL 뷰포트로 변환하는 행렬입니다. Ren'Py가 전달하며,
perspective속성이 있는 트랜스폼이 해당 효과를 포함하도록 갱신합니다.mat4 u_view정점 좌표를 월드 공간에서 뷰 공간으로 변환하는 행렬입니다. 기본값은 단위 행렬이지만
perspective트랜스폼으로 설정하면 위치, 회전, 크기 조절의 효과가 포함됩니다.mat4 u_model정점 좌표를 모델 공간에서 월드 공간으로 변환하는 행렬입니다.
mat4 u_projectionviewu_projection * u_view를 담은 행렬입니다. GPU로 전달할 유니폼 수와 셰이더 작업량을 줄이기 위해 존재합니다.mat4 u_transformu_projectionview * u_model과 같습니다. 정점 좌표를 OpenGL 뷰포트로 직접 변환합니다. GPU로 전달할 유니폼 수와 셰이더 작업량을 줄이고 이전 Ren'Py 버전과 호환하기 위해 존재합니다.
이 외에도 행렬에 접미사를 붙이면 특정 함수를 적용한 행렬을 합성할 수 있습니다.
__inverse행렬 뒤에 붙이면 역행렬을 반환합니다. 예를 들어
u_projection__inverse는 투영 행렬의 역행렬입니다.__transpose행렬 뒤에 붙이면 전치 행렬을 반환합니다. 예를 들어
u_view__transpose는 뷰 행렬의 전치 행렬입니다.__inverse_transpose행렬 뒤에 붙이면 전치 행렬의 역행렬을 반환합니다. 예를 들어
u_model__inverse_transpose는 모델 행렬을 전치한 행렬의 역행렬입니다. 법선 변환에 유용합니다.
정점 속성
모든 모델에서 다음 정점 속성을 사용할 수 있습니다.
vec4 a_position렌더링하는 정점의 위치입니다. 텍스처 왼쪽 위 모서리를 기준으로 한 가상 픽셀 좌표입니다.
텍스처가 있으면 다음 속성도 사용할 수 있습니다.
vec2 a_tex_coord텍스처 안에서 이 정점이 투영되는 좌표입니다.
법선이 있으면 다음 속성도 사용할 수 있습니다.
vec3 a_normal렌더링하는 정점의 법선입니다.
접선이 있으면 다음 속성도 사용할 수 있습니다.
vec3 a_tangent렌더링하는 정점의 접선입니다.
vec3 a_bitangent렌더링하는 정점의 종법선입니다.
GL 속성
GL 속성은 OpenGL이나 모델 기반 렌더러의 전역 상태를 바꿉니다. Transform이나 Render.add_property()에서 사용할 수 있습니다.
gl_blend_func지정한다면 여섯 요소 튜플이어야 합니다. 그리는 픽셀과 대상 픽셀을 혼합하는 식과 매개변수를 설정합니다.
구체적으로 (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픽셀의 빨강, 초록, 파랑, 알파 채널에 대응하는 불리언 네 개의 튜플이어야 합니다. 채널이 True이면 그리기 연산이 해당 채널에 기록하고, 그렇지 않으면 기록하지 않습니다.
gl_cull_faceNone이 아니면 카메라 반대쪽을 향한 삼각형을 제거하는 면 컬링을 활성화합니다. 앞면과 뒷면을 알아야 하므로 정점의 감기는 순서로 앞면을 결정합니다. "cw"는 시계 방향, "ccw"는 반시계 방향입니다. +Y가 아래인 Ren'Py 좌표계에서 판단하므로 OpenGL 좌표계와 반대입니다.
gl_depthTrue이면 깊이 버퍼를 비우고 이 디스플레이어블과 자식의 깊이 렌더링을 활성화합니다.
투명 픽셀도 그리면 깊이 버퍼가 갱신됩니다. 따라서 투명 이미지에 사용하면 예상치 못한 문제가 생길 수 있습니다. 대신
show문의zorder와behind절을 고려하세요.gl_pixel_perfectTrue이면 첫 정점이 화면 픽셀에 맞도록 메시를 이동합니다. 주로 텍스트를 선명하게 유지하는 데 사용합니다.
다음 속성은 mesh를 설정한 Transform이나 Model()에서 텍스처를 생성할 때만 적용됩니다. Model에서는 property 메서드에 지정할 수 있습니다.
gl_drawable_resolutionTrue이거나 설정하지 않으면 게임을 표시하는 창과 같은 해상도로 텍스처를 렌더링합니다. False이면 디스플레이어블의 가상 해상도로 렌더링합니다.
gl_anisotropic지정하면 메시에 적용할 텍스처를 이방성 필터링으로 생성할지 결정합니다. 이방성은 X, Y 확대율이 다를 때 여러 텍셀, 즉 텍스처 픽셀을 샘플링하는 기능입니다.
기본값은 True입니다. Pixellate 화면 전환 같은 일부 효과에서는 Ren'Py가 False로 설정합니다.
gl_mipmap지정하면 메시에 전달할 텍스처에 밉맵을 생성할지 결정합니다. 기본값은 True입니다.
gl_texture_wrap지정하면 메시에 적용할 텍스처의 반복 방식을 결정합니다. 두 요소 튜플이며, 첫 요소는 GL_TEXTURE_WRAP_S, 두 번째는 GL_TEXTURE_WRAP_T를 설정합니다. 통상 생성한 텍스처의 X, Y축입니다.
값은 renpy.uguu에서 가져온 OpenGL 상수여야 합니다.
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에서는 이 네 개만 사용할 수 있지만 Render.add_property에는 "texture_wrap_tex4"나 "texture_wrap_myuniform"도 전달할 수 있습니다.
GLTFModel 디스플레이어블
GLTFModel은 GLTF 형식의 3D 모델을 불러오는 디스플레이어블입니다. 다른 프로그램에서 만든 3D 모델을 Ren'Py에 표시할 때 사용해야 합니다.
- class GLTFModel(filename: 'str', shader: 'str | tuple[str]' = (), tangents: 'bool' = False, zoom: 'float' = 1.0, report: 'bool' = False, **kwargs)
GLTF 형식의 3D Model을 불러오는 디스플레이어블입니다. 여러 3D 도구가 이 형식을 지원합니다. Ren'Py는 Open Asset Importer(assimp) 라이브러리로 GLTF 모델을 불러옵니다.
Ren'Py의 2D 배치 시스템에서 GLTFModel의 너비와 높이는 0입니다. 기본적으로 파일에 저장된 크기로 불러옵니다. 필요하면 zoom으로 크기를 조절할 수 있습니다.
여러 모델을 사용하면 카메라에
gl_depth True를 지정해 깊이 검사를 활성화해야 합니다. 현재 Ren'Py는 모델 컬링을 하지 않으므로 전체를 렌더링할 수 있을 만큼 단순한 모델을 사용해야 합니다.- filename
표시할 모델의 파일명입니다.
- shader
사용할 셰이더 이름 문자열 또는 문자열 튜플입니다.
- tangents
True이면 메시에 접선을 포함합니다.
- zoom
모델에 적용할 확대율입니다. 많은 모델이 기본적으로 -1부터 1 범위를 사용하므로, 보이게 하려면 꽤 큰 값이 필요할 수 있습니다.
- report
참이면 모델에서 사용하는 유니폼을 포함한 보고서를 로그에 출력합니다.
GLTF 형식의 3D Model을 불러오는 디스플레이어블입니다. 여러 3D 도구가 이 형식을 지원합니다. Ren'Py는 Open Asset Importer(assimp) 라이브러리로 GLTF 모델을 불러옵니다.
Ren'Py의 2D 배치 시스템에서 GLTFModel의 너비와 높이는 0입니다. 기본적으로 파일에 저장된 크기로 불러옵니다. 필요하면 zoom으로 크기를 조절할 수 있습니다.
여러 모델을 사용하면 카메라에
gl_depth True를 지정해 깊이 검사를 활성화해야 합니다. 현재 Ren'Py는 모델 컬링을 하지 않으므로 전체를 렌더링할 수 있을 만큼 단순한 모델을 사용해야 합니다.- filename
표시할 모델의 파일명입니다.
- shader
사용할 셰이더 이름 문자열 또는 문자열 튜플입니다.
- tangents
True이면 메시에 접선을 포함합니다.
- zoom
모델에 적용할 확대율입니다. 많은 모델이 기본적으로 -1부터 1 범위를 사용하므로, 보이게 하려면 꽤 큰 값이 필요할 수 있습니다.
- report
참이면 모델에서 사용하는 유니폼을 포함한 보고서를 로그에 출력합니다.
사용하면서 확인한 사항은 다음과 같습니다.
일부 모델은 아주 작아서 100배나 1000배로 확대해야 쓸 수 있습니다.
모델별로 생성하는 유니폼이 다를 수 있습니다.
report=True로 GLTF 모델을 만든 뒤 log.txt에서 크기와 사용 가능한 항목을 확인할 것을 권장합니다. 이후 크기를 조절하고 알맞은 셰이더를 만들 수 있습니다.
제약
현재 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="""
// The direction of the light.
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;
}
""")
광원 하나를 기준으로 확산 및 정반사 조명을 적용하는 매우 단순한 셰이더입니다. 더 나은 셰이더가 만들어질 때까지 실험용으로 사용하도록 설계되었습니다.
GLTFModel은 투명 표면을 잘 지원하지 않습니다. 위의 discard 명령이 투명도를 흉내 내지만 순서가 조금 어긋나 표시 오류가 생길 수 있습니다.
Model 디스플레이어블
Model 디스플레이어블은 모델 기반 렌더러용 모델을 만드는 팩터리 역할을 합니다. Python에서 모델을 만들 때 사용할 수 있는 이전 API입니다.
- class Model(size=None, **properties)
Ren'Py가 모델 기반 렌더러용 2D 또는 3D 모델을 만들게 하는 디스플레이어블입니다. 여기 지정한 셰이더나 이를 감싸는 Transform 또는 Displayable이 선택한 셰이더로 한 번에 그립니다.
- size
None이 아니라면 Model 크기를 정하는 너비, 높이 튜플이어야 합니다. 지정하지 않으면 제공된 영역 크기를 사용합니다. 텍스처의 fit 매개변수가 우선합니다.
mesh 메서드를 호출하지 않았고 텍스처가 하나 이상 있으면 Ren'Py의 텍스처 불러오기 방식에 맞게 a_position과 a_tex_coord를 설정하는 메시를 만듭니다. 그 밖에는 a_position만 설정하는 메시를 사용합니다.
이 클래스의 모든 메서드는 호출된 디스플레이어블을 반환하므로 연속 호출할 수 있습니다.
- child(displayable, fit=False)
texture 메서드와 같지만 focus와 main을 참으로 설정합니다.
- grid_mesh(width, height)
균등 간격의 점으로 width x height 격자를 만들고, 각 점을 가로세로에서 가장 가까운 점과 연결한 뒤 각 사각형을 삼각형으로 나누는 메시를 생성합니다.
- width, height
가로세로 방향의 점 개수입니다. 2 이상의 정수입니다.
- property(name, value)
GL 속성 값을 설정합니다.
- name
"gl_" 접두사를 포함한 GL 속성 이름 문자열입니다.
- value
GL 속성 값입니다.
- shader(shader)
모델에 셰이더를 추가합니다.
- shader
이 모델에서 사용할 셰이더 이름 문자열입니다.
- texture(displayable, focus=False, main=False, fit=False, texture_wrap=None)
지정한 디스플레이어블을 렌더링하여 모델에 텍스처를 추가합니다. 첫 번째는
tex0, 두 번째는tex1이며 이후도 같은 방식입니다.- focus
참이면 디스플레이어블에 포커스 이벤트를 전달합니다. 모델 기준 좌표와 디스플레이어블 기준 좌표가 1:1로 대응한다고 가정합니다.
- main
참이면 이 디스플레이어블의 주 자식으로 표시하여 디스플레이어블 검사기로 확인할 수 있게 합니다.
- fit
참이면 Model 크기를 디스플레이어블 크기로 정합니다. 텍스처 하나에서만 참일 수 있습니다.
- texture_wrap
None이 아니라면 이 텍스처에 적용할 gl_texture wrap GL 속성입니다.
- uniform(name, value)
셰이더에 전달할 유니폼 값을 설정합니다.
- name
"u_" 접두사를 포함한 유니폼 이름 문자열입니다.
- value
유니폼 값입니다. 실수, 실수 2개·3개·4개의 튜플 또는 Matrix입니다.
Model 디스플레이어블 예제
Model 디스플레이어블을 ATL 트랜스폼과 내장 셰이더와 함께 사용해 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 안에 모델을 만들기 때문입니다.
애니메이션 셰이더
u_time으로 애니메이션을 만드는 셰이더를 사용할 때는, 화면의 모든 셰이더가 표시하는 프레임마다 실행되더라도 Ren'Py의 FPS는 일정하지 않다는 점에 유의해야 합니다. 다시 그릴 디스플레이어블이 없으면 최소 프레임 속도인 5 FPS로 내려갑니다.
ATL 트랜스폼에서 애니메이션 셰이더를 사용하고 트랜스폼 자체가 다시 그리기를 일으키지 않으면, 다른 화면 객체가 움직일 때만 셰이더가 제대로 움직이고 그 밖에는 끊길 수 있습니다. 이 경우 빈 ATL 반복문을 넣어 다시 그리기를 강제할 수 있습니다.
transform fancy_shader:
shader 'my_fancy_shader'
pause 0
repeat
pause 0은 가능한 한 빠르게 프레임을 순환합니다. pause에 다른 값을 지정해 최소 프레임 속도를 정할 수도 있습니다. 예: pause 1.0/30.
셰이더 파트
Ren'Py에서 사용하는 셰이더 파트 목록은 기본 셰이더 파트를 참고하세요.