Текстовые шейдеры

Ren'Py содержит систему текстовых шейдеров, которая позволяет контролировать, как Ren'Py отображает текст. Когда эта система включена, она использует модели для рендеринга двух треугольников для каждого символа Unicode. Части шейдера, либо указанные создателем, либо сгенерированные Ren'Py, применяются к модели и могут изменять способ отображения текста.

Документация по текстовым шейдерам состоит из трех частей:

  1. Как использовать текстовые шейдеры

  2. Какие текстовые шейдеры включены в Ren'Py

  3. Как создавать новые текстовые шейдеры

Обратите внимание, что, хотя текстовые шейдеры предназначены для простого использования создателями игр, создание собственных текстовых шейдеров требует некоторых знаний GLSL (OpenGL Shading Language), а также моделей, и, следовательно, является более продвинутой функцией, чем большинство возможностей Ren'Py.

Использование текстовых шейдеров

Есть три способа использовать текстовые шейдеры:

Текстовый шейдер по умолчанию Первый способ — установить текстовый шейдер по умолчанию, используя config.default_textshader.

define config.default_textshader = "wave:10"

При такой установке текстовый шейдер будет использоваться для всего текста, который не указывает свой собственный текстовый шейдер. Он также будет комбинироваться с текстовыми шейдерами, которые включают шейдер по умолчанию, а таких большинство.

Как правило, шейдер по умолчанию должен отвечать за эффект медленного появления текста и не должен добавлять более сложных эффектов.

Стили Второй способ использовать текстовые шейдеры — это установить свойство стиля textshader, либо напрямую, либо одним из многих способов, которые Ren'Py предоставляет для настройки стилей.

style default:
    textshader "dissolve"

define goldfinger = Character("Goldfinger", what_textshader="linetexture:gold.png")

screen purchase():
    vbox:
        text "Buy the DLC for more!" textshader "wave"
        textbutton "Buy Now":
            text_textshader "wave" action iap.Purchase("dlc")

Текстовые теги Третий способ использовать текстовые шейдеры — это использовать соответствующий текстовый тег, чтобы изменить внешний вид части текста.

"What's this? A letter from {shader=zoom}out of nowhere{/shader}?"

Примечание Текстовый блок должен либо использовать текстовые шейдеры, либо нет — смешивание не поддерживается. Например, вам следует установить свойство стиля config.default_textshader или textshader, если вы используете текстовый тег, как в примере выше.

Задание текстовых шейдеров

Текстовые шейдеры задаются в виде строк, например:

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

Первая часть строки, до первого двоеточия, — это имя текстового шейдера. Остальная часть строки — это серия uniform-переменных, которые передаются в шейдер, разделённых двоеточиями. (Uniform-переменные — это параметры, которые передаются в шейдер и могут использоваться для управления его работой.)

Uniform-переменные можно указывать по имени с последующим знаком =, или же имя можно опустить, чтобы установить каждую uniform-переменную по порядку. (Опускать имя не поддерживается в Ren'Py 7.) Хотя внутренне все uniform-переменные начинаются с u_, для краткости u_ можно опустить.

Значением uniform-переменной может быть:

  • От 1 до 4 чисел, разделенных запятыми. Они могут использоваться с типами float, vec2, vec3 или vec4.

  • Цвет, начинающийся с #. (Например, #f00 или #ff0000 для красного.) Это создает vec4, соответствующий этому цвету. Этот цвет будет предварительно умножен на свой альфа-канал.

  • Отображаемый объект, который будет использоваться как текстура. Это создаёт sampler2D, который можно использовать для сэмплирования текстуры.

Значения uniform-переменных не могут быть выражениями или обращаться к переменным, хотя можно использовать интерполяцию текста для создания строки, которая может быть обработана как тег textshader или его параметр.

Наконец, текстовые шейдеры можно комбинировать друг с другом с помощью оператора |. Например:

"jitter:1.0, 3.0|wave"

Это применит к тексту и шейдер jitter, и шейдер wave. Это работает только в том случае, если шейдеры совместимы друг с другом и не используют одни и те же uniform-переменные (или используют uniform-переменную таким образом, который совместим друг с другом, в этом случае она принимает значение из последнего шейдера в списке).

Если для шейдера не установлено 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, символы мгновенно увеличатся до полного размера.

Использование этого шейдера предотвратит использование стандартного шейдера текста.

Создание текстовых шейдеров

Текстовые шейдеры — это программы на GLSL, которые выполняются на GPU. Эти шейдеры регистрируются с помощью функции 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)

Это создает textshader и регистрирует его под именем name.

Эта функция принимает следующие аргументы:

name

Это имя textshader'а. Оно также используется для регистрации части шейдера (shader part) с именем textshader.`name`.

shaders

Части шейдера (shader parts) для применения к тексту. Это может быть строка, или список, или кортеж строк. Это должна быть часть шейдера, зарегистрированная с помощью 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, при непосредственном использовании этого textshader'а, он будет объединен с config.default_textshader.

adjust_function

Функция, которая вызывается с объектом и uniforms, передаваемыми в textshader в качестве именованных аргументов. Эта функция может устанавливать поля extra_slow_time, extra_slow_duration, redraw и redraw_when_slow объекта.

doc

Строка, содержащая информацию для документации. Это в основном предназначено для системы документации Ren'Py.

Именованные аргументы, начинающиеся с u_, передаются в шейдер как uniforms, при этом строки, начинающиеся с #, интерпретируются как цвета. Большинство uniforms должны начинаться с u__, используя локальные переменные шейдера, чтобы предотвратить конфликты с другими шейдерами.

Именованный аргумент variables и все именованные аргументы, начинающиеся с fragment_ или vertex_, передаются в renpy.register_shader(), которая регистрирует часть шейдера.

Переменные в текстовых шейдерах

В дополнение к uniform-переменным, которые вы передали в текстовый шейдер (обычно начинающимся с u__), Ren'Py делает доступными для текстовых шейдеров следующие переменные. Чтобы использовать переменную в текстовом шейдере, её необходимо объявить в аргументе variables функции renpy.register_text_shader.

Кроме них, доступны uniform-переменные и атрибуты модели, из которых особенно полезны a_position, a_tex_coord, u_time и u_random.

Uniforms

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

Смещение текста от центра символа. Указывается в отрисовываемых пикселях (drawable pixels) в порядке 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

Выносной элемент (ascent) шрифта текущего глифа над базовой линией, в отрисовываемых пикселях.

vec2 a_text_center

Позиция центра базовой линии глифа в отрисовываемых пикселях. Это не центр прямоугольника, а точка на базовой линии и около центра символа.

float a_text_descent

Нижний выносной элемент (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.

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:

    "This is a test of the {shader=spin:0.5:-5}spin{/shader} text shader."
orphan: