텍스트 셰이더

Ren'Py는 텍스트 표시 방식을 제어하는 텍스트 셰이더 시스템을 제공합니다. 사용하면 모델 기반 렌더링으로 유니코드 문자마다 삼각형 두 개를 렌더링합니다. 제작자가 지정하거나 Ren'Py가 생성한 셰이더 파트를 모델에 적용하여 텍스트 표시 방식을 바꿀 수 있습니다.

텍스트 셰이더 문서는 세 부분으로 나뉩니다.

  1. 텍스트 셰이더 사용 방법

  2. Ren'Py에 포함된 텍스트 셰이더

  3. 새 텍스트 셰이더 제작 방법

텍스트 셰이더는 게임 제작자가 쉽게 사용할 수 있도록 설계되었지만, 직접 만들려면 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_main

1.0이면 본문 텍스트이고 0.0이면 본문의 외곽선이나 그림자입니다.

float u_text_max_depth

u_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 res0

tex0의 해상도로, 그리기 픽셀 단위입니다.

속성

텍스트를 그릴 때 각 꼭짓점은 글리프 하나에 대응합니다. 여러 글리프의 꼭짓점이 같은 위치를 공유할 수 있지만 셰이더에는 별개의 꼭짓점으로 전달됩니다.

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} 텍스트 셰이더를 테스트합니다."