텍스트 셰이더
Ren'Py는 텍스트 표시 방식을 제어하는 텍스트 셰이더 시스템을 제공합니다. 사용하면 모델 기반 렌더링으로 유니코드 문자마다 삼각형 두 개를 렌더링합니다. 제작자가 지정하거나 Ren'Py가 생성한 셰이더 파트를 모델에 적용하여 텍스트 표시 방식을 바꿀 수 있습니다.
텍스트 셰이더 문서는 세 부분으로 나뉩니다.
텍스트 셰이더 사용 방법
Ren'Py에 포함된 텍스트 셰이더
새 텍스트 셰이더 제작 방법
텍스트 셰이더는 게임 제작자가 쉽게 사용할 수 있도록 설계되었지만, 직접 만들려면 OpenGL Shading Language인 GLSL과 모델 기반 렌더링 지식이 필요합니다. 따라서 Ren'Py의 다른 기능보다 고급 기능에 해당합니다.
텍스트 셰이더 사용
텍스트 셰이더를 사용하는 방법은 세 가지입니다.
기본 텍스트 셰이더 첫째는 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")
텍스트 태그 셋째는 appropriate text tag를 사용하여 텍스트 일부의 모양을 바꾸는 것입니다.
"이게 뭐지? {shader=zoom}느닷없이{/shader} 편지가 나타났어?"
참고 한 텍스트 블록에서는 셰이더 사용 여부를 통일해야 합니다. 혼합은 지원하지 않습니다. 예를 들어 위와 같은 텍스트 태그를 사용한다면 config.default_textshader 또는 textshader 스타일 속성도 설정해야 합니다.
텍스트 셰이더 지정
텍스트 셰이더는 다음과 같은 문자열로 지정합니다.
"dissolve"
"jitter:u__jitter=1.0, 3.0"
"texture:gold.png"
첫 콜론 앞의 첫 부분은 텍스트 셰이더 이름입니다. 나머지는 콜론으로 구분한 유니폼들입니다. 유니폼은 셰이더에 전달하여 동작을 제어하는 매개변수입니다.
유니폼 이름 뒤에 =를 붙여 지정하거나 이름을 생략하여 각 유니폼을 순서대로 설정할 수 있습니다. Ren'Py 7에서는 이름 생략을 지원하지 않습니다. 내부적으로 모든 유니폼 이름은 u_로 시작하지만 간결하게 쓰기 위해 생략할 수 있습니다.
유니폼 값은 다음 중 하나입니다.
쉼표로 구분한 숫자 1~4개. float, vec2, vec3, vec4 유형에 사용할 수 있습니다.
#로 시작하는 색상. 예를 들어 빨강은 #f00이나 #ff0000입니다. 해당 색상의 vec4를 생성하며 알파 채널을 미리 곱합니다.
텍스처로 사용할 디스플레이어블. 텍스처를 샘플링할 sampler2D를 만듭니다.
유니폼 값에는 표현식을 쓰거나 변수에 접근할 수 없습니다. 다만 텍스트 보간으로 textshader 태그나 매개변수로 평가할 문자열을 만들 수는 있습니다.
마지막으로 | 연산자로 텍스트 셰이더들을 조합할 수 있습니다. 예를 들어:
"jitter:1.0, 3.0|wave"
텍스트에 jitter와 wave 셰이더를 모두 적용합니다. 서로 호환되며 같은 유니폼을 사용하지 않아야 합니다. 같은 유니폼을 호환 가능한 방식으로 사용하는 경우에는 목록의 마지막 셰이더 값을 사용합니다.
include_default를 False로 설정한 셰이더가 아니라면 스타일이나 태그에 지정한 셰이더에 기본 셰이더를 조합합니다.
텍스트 셰이더 콜백
config.textshader_callbacks 변수로 텍스트 셰이더 적용 시 실행할 콜백을 설정할 수 있습니다. 환경설정에 따라 셰이더를 조정하는 데 사용할 수 있습니다.
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
내장 텍스트 셰이더
Ren'Py에는 다음 텍스트 셰이더들이 내장되어 있습니다.
- dissolve
dissolve 텍스트 셰이더는 텍스트의 앞부분부터 끝부분까지 천천히 디졸브하며 표시합니다.
- u__duration = 10.0
동시에 알파가 바뀌는 문자 수입니다. 0이면 물결이 한 번에 한 픽셀씩 텍스트 위를 지나갑니다.
이 셰이더를 사용하면 기본 텍스트 셰이더는 사용하지 않습니다.
- flip
flip 셰이더는 느린 텍스트를 가로로 뒤집으며 앞부분부터 끝부분까지 표시합니다.
- u__duration = 10.0
동시에 알파가 바뀌는 문자 수입니다. 0이면 문자가 즉시 뒤집힙니다.
이 셰이더를 사용하면 기본 텍스트 셰이더는 사용하지 않습니다.
- jitter
jitter 텍스트 셰이더는 원래 그릴 위치를 기준으로 텍스트를 무작위 위치로 이동합니다. 위치는 프레임마다 바뀝니다.
- u__jitter = (3.0, 3.0)
텍스트에 적용할 흔들림의 양으로, 픽셀 단위입니다.
- linetexture
한 줄씩 텍스트에 텍스처를 곱합니다. 텍스처는 텍스트 왼쪽에 맞추고 세로 중심은 텍스트 기준선에 맞춥니다. 따라서 텍스처 아래쪽 절반은 대부분 보이지 않습니다.
- u__texture = ...
텍스트에 곱할 텍스처입니다.
- u__scale = (1.0, 1.0)
텍스처 크기에 적용할 배율입니다. 예를 들어 (1.0, 0.5)는 높이를 절반으로 줄입니다.
- offset
offset 텍스트 셰이더는 텍스트를 고정된 양만큼 이동합니다.
- u__offset = (0.0, 0.0)
텍스트를 이동할 양으로, 가상 픽셀 단위입니다.
- slowalpha
slowalpha 셰이더는 typewriter나 dissolve 같은 다른 느린 텍스트 셰이더와 함께 사용하도록 설계되었습니다. 아직 나타나지 않은 텍스트를 완전히 숨기는 대신 u__alpha = 0.2의 알파값으로 그립니다.
- u__alpha = 0.2
아직 나타나지 않은 텍스트의 알파값입니다.
- texture
텍스트에 텍스처의 색상을 곱하는 texture 텍스트 셰이더입니다. 외곽선이나 위치가 이동된 텍스트에는 적용하지 않습니다. 텍스처는 텍스트의 왼쪽 위에 맞춥니다.
- u__texture = ...
텍스트에 곱할 텍스처입니다.
- typewriter
typewriter 텍스트 셰이더는 타자기로 입력하듯 한 글자씩 나타나게 하여 느린 텍스트를 표시합니다.
이 셰이더를 사용하면 기본 텍스트 셰이더는 사용하지 않습니다.
- wave
wave 텍스트 셰이더는 텍스트를 물결 모양으로 위아래로 움직입니다.
- u__amplitude = 5.0
텍스트가 위아래로 움직일 픽셀 수입니다.
- u__frequency = 2.0
초당 물결 수입니다.
- u__wavelength = 20.0
물결의 봉우리 사이에 있는 문자 수입니다.
- zoom
zoom 텍스트 셰이더는 느린 텍스트를 초기 크기 u__zoom = 0.0에서 원래 크기까지 확대하여 표시합니다.
- u__zoom = 0.0
문자가 처음 나타날 때 적용할 초기 확대·축소 배율입니다.
- u__duration = 10.0
동시에 알파가 바뀌는 문자 수입니다. 0이면 문자가 즉시 뒤집힙니다.
이 셰이더를 사용하면 기본 텍스트 셰이더는 사용하지 않습니다.
텍스트 셰이더 제작
텍스트 셰이더는 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)
텍스트 셰이더를 만들어 name이라는 이름으로 등록합니다.
이 함수는 다음 인수를 받습니다.
- name
텍스트 셰이더 이름입니다. textshader.`name`이라는 셰이더 파트를 등록할 때도 사용합니다.
- shaders
텍스트에 적용할 셰이더 파트입니다. 문자열 또는 문자열의 리스트나 튜플입니다.
renpy.register_shader()나 이 함수로 등록한 셰이더 파트여야 합니다. '-'로 시작하면 해당 파트를 목록에서 제거합니다. 예를 들어 '-textshader.typewriter'는 해당 파트를 제거합니다.이 함수로 등록한 셰이더 파트에는 textshader. 접두사가 붙습니다. 이 함수에서 사용할 때도 접두사를 지정해야 합니다.
- extra_slow_time
현재 초당 문자 수로 Ren'Py가 계산한 시간에 더하여 느린 텍스트 효과에 추가할 시간입니다. 문자 하나의 전환에 기본 시간보다 오래 걸리는 셰이더에 유용합니다. True이면 셰이더를 항상 갱신합니다.
- extra_slow_duration
extra_slow_time에 더하되, 문자당 시간을 곱하여 느린 텍스트 효과에 추가할 시간을 구합니다. 문자당 시간은 1 / 초당 문자 수입니다.
- redraw
느린 텍스트를 모두 표시하고 extra_slow_time도 지난 뒤 텍스트를 다시 그릴 때까지의 초 수입니다.
- redraw_when_slow
느린 텍스트를 표시하는 동안 다시 그릴 때까지의 초 수입니다.
- include_default
True이면 이 셰이더를 직접 사용할 때
config.default_textshader와 조합합니다.- adjust_function
객체 하나와 텍스트 셰이더에 전달되는 유니폼들을 키워드 인수로 받아 호출하는 함수입니다. 객체의 extra_slow_time, extra_slow_duration, redraw, redraw_when_slow 필드를 설정할 수 있습니다.
- doc
문서 정보를 담은 문자열입니다. 주로 Ren'Py 문서 시스템용입니다.
u_로 시작하는 키워드 인수는 셰이더에 유니폼으로 전달하며#로 시작하는 문자열은 색상으로 해석합니다. 다른 셰이더와 충돌하지 않도록 대부분의 유니폼은 셰이더 지역 변수를 사용하는u__로 시작해야 합니다.variables 키워드 인수와 fragment_ 또는 vertex_로 시작하는 모든 키워드 인수는 셰이더 파트를 등록하는
renpy.register_shader()에 전달합니다.
텍스트 셰이더의 변수
Ren'Py는 직접 전달한 유니폼(일반적으로 u__로 시작) 외에 다음 변수들을 제공합니다. 사용하려면 renpy.register_text_shader의 variables 인수에서 선언해야 합니다.
모델의 유니폼과 속성도 사용할 수 있으며, 특히 a_position, a_tex_coord, u_time, u_random이 유용합니다.
유니폼
float u_text_depth맨 위부터 센 텍스트의 깊이입니다. 본문은 0.0, 첫 외곽선이나 그림자는 1.0, 두 번째는 2.0이며 이후에도 같은 방식입니다.
float u_text_main1.0이면 본문 텍스트이고 0.0이면 본문의 외곽선이나 그림자입니다.
float u_text_max_depthu_text_depth의 최댓값입니다. 그릴 외곽선과 그림자 수입니다. u_text_depth가 이 값과 같으면 마지막 외곽선이나 그림자이므로 배경을 그릴 때 유용할 수 있습니다.
vec2 u_text_offset문자 중심에서의 텍스트 오프셋입니다. 그리기 픽셀 단위이며 x, y 순서입니다.
float u_text_outline텍스트 주위 외곽선의 너비입니다. 그리기 픽셀 단위이며 텍스트 가장자리에서 외곽선 가장자리까지의 거리입니다.
float u_text_slow_duration느린 텍스트를 표시할 때 문자 하나에 걸리는 시간입니다. 느린 텍스트가 아니면 0.0입니다.
float u_text_slow_time느린 텍스트 효과가 시작된 뒤 지난 초 수입니다. 느린 텍스트가 끝날 때까지만 증가한 뒤 최댓값에 도달합니다. 사용자가 클릭하여 느린 표시를 끝내도 최댓값이 됩니다. 느린 텍스트에만 사용해야 하며 시간에 따라 변하는 다른 효과에는 u_time을 사용합니다.
float u_text_to_drawable그리기 픽셀에 대한 가상 픽셀의 비율입니다. 가상 픽셀을 그리기 픽셀로 변환할 때 사용합니다.
float u_text_to_virtual가상 픽셀에 대한 그리기 픽셀의 비율입니다. 그리기 픽셀을 가상 픽셀로 변환할 때 사용합니다.
sampler2D tex0현재 깊이에 렌더링한 텍스트가 들어 있는 텍스처입니다.
vec2 res0tex0의 해상도로, 그리기 픽셀 단위입니다.
속성
텍스트를 그릴 때 각 꼭짓점은 글리프 하나에 대응합니다. 여러 글리프의 꼭짓점이 같은 위치를 공유할 수 있지만 셰이더에는 별개의 꼭짓점으로 전달됩니다.
float a_text_ascent현재 글리프 글꼴의 기준선 위 상승 높이로, 그리기 픽셀 단위입니다.
vec2 a_text_center글리프 기준선 중심의 위치로, 그리기 픽셀 단위입니다. 사각형 중심이 아니라 기준선 위에서 문자 중심 부근에 있는 점입니다.
float a_text_descent현재 글리프의 기준선 아래 하강 높이로, 그리기 픽셀 단위입니다.
float a_text_index그리는 글리프의 인덱스입니다. 첫 꼭짓점은 0이며 꼭짓점마다 1씩 늘어납니다.
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글리프를 포함하는 사각형입니다. 그리기 픽셀 단위의 x, y, width, height를 담은 vec4입니다.
res0으로 나누면 텍스처 좌표로 변환할 수 있습니다.float a_text_pseudo_glyph꼭짓점이 가상 글리프에 속하면 1.0, 아니면 0.0입니다. 자세한 내용은 아래를 참조하세요.
가상 글리프
Ren'Py는 각 줄의 시작과 끝을 덮는 가상 글리프를 생성합니다. 빈 줄이면 줄 전체를 덮는 가상 글리프 하나를 만듭니다. 현재 줄의 외곽선이 위아래 줄로 뻗을 수 있는 영역을 덮기 위해 필요합니다.
예제
텍스트가 나타날 때 회전시키는 텍스트 셰이더 예제입니다.
init python:
def adjust_extra_slow_time(ts, u__delay, **kwargs):
"""
Adjusts a text shader's extra slow time to support the spinning text shader.
"""
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} 텍스트 셰이더를 테스트합니다."