Рендеринг на основе моделей

Хотя Ren'Py в основном используется с двумерными прямоугольными изображениями, что характерно для визуальных новелл, под капотом у него есть модельный рендерер, предназначенный для использования возможностей современных ГП (GPU). Это позволяет реализовать ряд визуальных эффектов, которые были бы невозможны в ином случае.

Внимание: это одна из самых продвинутых функций, доступных в Ren'Py. Во многих случаях нет необходимости понимать, как работает рендеринг на основе моделей — такие функции, как matrixcolor и поддержка Live2D, можно использовать, не вникая в принципы работы модельного рендеринга, и в будущем будет добавлено больше таких функций. Эта документация предназначена для очень продвинутых разработчиков, а также для тех, кто хочет внести свой вклад в сам Ren'Py.

Рендеринг на основе моделей — одна из самых сложных функций в Ren'Py, и эта документация может быть трудна для понимания без предварительного изучения руководств по OpenGL, OpenGL ES, GLSL и GLSL ES. Более того, поскольку некоторые части моделей передаются напрямую драйверам вашего ГП, которые могут принимать ошибочные данные, важно проводить тестирование на различных типах оборудования.

Модели, Render'ы и операции отрисовки

Фундаментальный элемент, который Ren'Py отрисовывает на экране, — это модель. Модель состоит из следующих частей:

  • Меш (сетка) из одного или нескольких треугольников. Треугольник состоит из трёх вершин (углов), каждая из которых содержит позицию в двух- или трёхмерном пространстве и может содержать дополнительную информацию, чаще всего текстурные координаты.

  • Ноль или более текстур, причём точное количество ограничено ГП, на которых может работать ваша игра. Все ГП должны поддерживать как минимум три текстуры на модель. Текстура — это прямоугольник с данными изображения, загруженный в ГП либо напрямую, либо с помощью операции рендеринга в текстуру.

  • Список имён частей шейдера. Ren'Py использует эти части для создания шейдеров — программ, выполняемых на ГП для рендеринга модели. Именам частей шейдера можно добавить префикс «-», чтобы предотвратить использование этой части.

  • Uniform-переменные. Uniform — это дополнительные данные, которые остаются неизменными для всей модели. Например, когда модель представляет сплошной цвет, этот цвет является uniform-переменной.

  • GL-свойства. GL-свойства — это флаги, которые дополнительно управляют рендерингом, такие как режимы минификации/магнификации и цветовая маска.

Поскольку Ren'Py обычно отрисовывает на экране более одного элемента, он создаёт дерево объектов Render. Эти объекты Render могут иметь в качестве дочерних элементов модели или другие Render'ы. (Объект Render также можно превратить в модель, как описано ниже.) Объект Render содержит:

  • Список дочерних элементов, включая двумерное смещение, применяемое к каждому из них.

  • Матрицу, описывающую, как дочерние элементы преобразуются в трёхмерном пространстве.

  • Списки имён частей шейдера, uniform-переменных и GL-свойств, которые применяются к моделям во время отрисовки.

  • Флаги, определяющие, следует ли обновлять отсекающий полигон в пространстве отображения.

Ren'Py отрисовывает экран, выполняя обход дерева объектов Render в глубину, пока не встретит модель. Во время этого обхода Ren'Py обновляет матрицу трансформации положения модели, отсекающий полигон, а также списки частей шейдера, uniform-переменных и GL-свойств. Когда в процессе обхода встречается модель, на ГП активируется соответствующая шейдерная программа, передаётся вся информация, и происходит операция отрисовки.

Где создаются модели

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

Изображения и манипуляторы изображений

Они создают модель с мешем, содержащим два треугольника, которые покрывают прямоугольник изображения. Меш содержит текстурные координаты. Модель использует шейдер «renpy.texture».

Solid()

Отображаемый объект Solid создаёт меш, содержащий два треугольника, без текстурных координат. Модель использует шейдер «renpy.solid», а цвет передаётся в uniform-переменную u_renpy_solid_color.

Dissolve(), ImageDissolve(), AlphaDissolve(), Pixellate(), AlphaMask(), Flatten()

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

Live2D

Отображаемые объекты Live2D могут создавать несколько моделей при рендеринге, обычно по одной модели на каждый слой.

Transform() и ATL

Transform создаёт модель, если mesh равно True, или если используется blur. В этом случае дочерние элементы Transform'а рендеринются в текстуры, причём меш первой текстуры используется для меша, связанного с моделью.

Не каждый трансформ создаёт модель. Некоторые трансформы просто добавляют шейдеры и uniform-переменные к Render'у (например, трансформы, использующие blur или alpha). Другие трансформы просто влияют на геометрию.

Render

Render создаёт модель, если его атрибут mesh равен True. В этом случае дочерние элементы Render'а рендерятся в текстуры, причём меш первой текстуры используется для меша, связанного с моделью.

Ожидается, что в будущем Ren'Py добавит больше способов создания моделей.

Генерация шейдерных программ

Ren'Py генерирует шейдерную программу, сначала собирая список имён частей шейдера. Этот список состоит из «renpy.geometry», списка частей шейдера, взятых из Render'ов, и списка частей шейдера, найденных в отрисовываемой модели.

Затем из списка частей шейдера удаляются дубликаты. Если часть шейдера начинается с «-», она удаляется из списка вместе с той же частью без ведущего «-». (Так, «-renpy.geometry» приведёт к удалению и себя, и «renpy.geometry».)

Затем Ren'Py берёт список частей шейдера и извлекает списки переменных, функций, частей вершинного шейдера и частей фрагментного шейдера. Они, в свою очередь, используются для генерации исходного кода шейдеров, причём части вершинного и фрагментного шейдеров включаются в порядке от меньшего к большему приоритету.

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

Ren'Py хранит кэш всех комбинаций частей шейдера, которые когда-либо использовались, в game/cache/shaders.txt и загружает его при запуске. Если произошли серьёзные изменения в использовании шейдеров, этот файл следует отредактировать или удалить, чтобы он был создан заново с корректными данными.

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

Новые части шейдера можно создать, вызвав функцию renpy.register_shader и предоставив части шейдеров на языке GLSL.

Как правило, имена частей шейдера должны иметь формат «пространство_имён.часть», например, «mygame.recolor» или «mylibrary.warp». Имена, начинающиеся с «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] (массив из [n] элементов типа [type] (любого из вышеперечисленных типов) (например, int[5] будет массивом из 5 целых чисел))

  • mat2 (Матрица)

  • mat3 (Матрица)

  • mat4 (Матрица)

  • sampler2D (Displayable, включая строки, которые дают Displayable, или Render)

Uniform-переменные должны начинаться с u_, атрибуты — с 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

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

Версия GLSL. Поддерживаемая версия GLSL зависит от платформы, но наиболее ограничивающими являются мобильные и веб-платформы, которые используют GLSL ES 1.00. Ren'Py объявляет переменные с высокой точностью (highp), что поддерживает числа с плавающей запятой в диапазоне от 2-62 до 262. Ненулевые числа с плавающей запятой должны иметь величину от 2-62 до 262, а целые числа должны быть в диапазоне от -216 до 216. Реальное оборудование часто поддерживает более широкий диапазон, но это не гарантируется.

Существует переменная, которая может помочь в отладке пользовательских шейдеров:

define config.log_gl_shaders = False

Если True, исходный код шейдерных программ GLSL будет записываться в файл log.txt при запуске.

Локальные переменные части шейдера

Переменные можно объявить локальными для шейдера, используя префиксы u__, a__, v__ или l__. В этом случае двойные подчёркивания заменяются именем шейдера, в котором все точки заменены на подчёркивания. Например, если имя шейдера — example.gradient, префикс u__ будет заменён на u_example_gradient_.

Основное применение этого — с шейдерами текста, где большинство uniform-переменных являются локальными для шейдера. Также локальные переменные внутри шейдера следует объявлять с префиксом l__.

Трансформы и рендеринг на основе моделей

Рендеринг на основе моделей добавляет следующие свойства в ATL и Transform():

mesh
Type:

None или True или кортеж

Default:

None

Если не None, этот Transform будет рендериться как модель. Это означает:

  • Будет создан меш. Если это кортеж из 2-х компонентов, он интерпретируется как количество точек в меше по осям x и y. (Каждое измерение должно быть не менее 2.) Если True, меш берётся из дочернего элемента.

  • Дочерний элемент этого трансформа будет отрендерен в текстуру.

  • Будет добавлен шейдер renpy.texture.

По умолчанию используемая мешем текстура не будет иметь мипмапов. Если мипмапы желательны, gl_mipmap следует установить в True.

mesh_pad
Type:

None или кортеж

Default:

None

Если не None, это может быть кортеж из 2-х или 4-х компонентов. Если mesh равно True и это свойство задано, оно применяет отступы к размеру текстур, используемых мешем. Кортеж из 2-х компонентов добавляет отступы справа и снизу, а кортеж из 4-х компонентов — слева, сверху, справа и снизу.

Это можно использовать совместно со свойством gl_pixel_perfect, чтобы рендерить текст в меш. В Ren'Py текст рендерится в разрешении экрана, что может привести к выходу за границы текстуры, которая будет наложена на меш. Добавление нескольких пикселей отступов делает текстуру больше, что позволит отобразить все пиксели. Например:

transform adjust_text:
    mesh True
    mesh_pad (10, 0)
    gl_pixel_perfect True
    shader "shaders.adjust_text"

Этот код гарантирует, что текстура, переданная шейдеру, будет содержать все пиксели текста.

shader
Type:

None или str или список str

Default:

None

Если не None, это имя части шейдера или список имён частей шейдера, которые будут применены к этому Render'у (если создана модель) или к моделям, достигаемым через этот Render.

blend
Type:

None или str

Default:

None

Если не None, это должна быть строка. Эта строка ищется в config.gl_blend_func, чтобы получить значение для свойства gl_blend_func. Используется для применения альтернативных режимов смешивания.

Режимы смешивания, поддерживаемые по умолчанию: «normal», «add», «multiply», «min» и «max».

Кроме того, uniform-переменные, начинающиеся с u_, но не с u_renpy, становятся доступными как свойства Transform. GL-свойства становятся доступными как свойства Transform, начинающиеся с 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 использует предумноженный альфа-канал, результаты некоторых из этих режимов могут быть неочевидными, когда пиксель не является полностью непрозрачным. В ГП цвет (r, g, b, a) представляется как (r * a, g * a, b * a, a), и функция смешивания использует эти предумноженные цвета. Результат может отличаться от того, что вы получаете в графических редакторах для этих режимов смешивания, когда рисуемый объект не полностью непрозрачен.

Uniform-переменные типа float, sampler и vector

Следующие uniform-переменные доступны для всех моделей.

vec2 u_model_size

Ширина и высота модели, как они были переданы в Ren'Py. Доступно только для 2D-моделей, для которых указан размер, и равно (0, 0) для 3D-моделей.

float u_lod_bias

Смещение уровня детализации, применяемое к выборкам из текстур. Может быть установлено в Transform'е. Значение по умолчанию, взятое из config.gl_lod_bias и равное -0.5, заставляет Ren'Py всегда выбирать следующий больший уровень и масштабировать его вниз.

float u_time

Время текущего кадра. Начальная точка (эпоха) не определена, поэтому лучше всего рассматривать это как число, которое увеличивается на единицу каждую секунду. Время берётся по модулю 86400, поэтому оно будет сбрасываться до 0.0 раз в день.

vec4 u_random

Четыре случайных числа от 0.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

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

Если доступна uniform-переменная типа sampler, то добавление суффикса __res даст vec2, содержащий размер базовой текстуры. Например, u_markup__res вернёт размер текстуры u_markup.

Если доступна uniform-переменная типа vec4, то добавление суффикса __premul даст vec4, содержащий предумноженный цвет. Например, u_color__premul вернёт vec4, где красный, зелёный и синий каналы умножены на альфа-канал.

Матричные uniform-переменные

Следующие uniform-переменные доступны всем моделям. Предполагается, что для рендеринга модели используется только один трансформ со свойством perspective. Если используется несколько трансформов со свойством perspective, самый внутренний трансформ с установленной перспективой определяет мировое пространство и пространство вида.

mat4 u_projection

Это матрица, которая преобразует координаты из пространства вида во вьюпорт OpenGL. Она отправляется Ren'Py и обновляется трансформами со свойством perspective, чтобы инкапсулировать эффекты этого свойства.

mat4 u_view

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

mat4 u_model

Это матрица, которая преобразует координаты вершин из пространства модели в мировое пространство.

mat4 u_projectionview

Эта матрица содержит u_projection * u_view. Она существует для минимизации количества uniform-переменных, которые необходимо отправлять в ГП, и объёма работы, который нужно выполнять в шейдере.

mat4 u_transform

Это то же самое, что и u_projectionview * u_model. Это матрица, которая преобразует координаты вершин непосредственно во вьюпорт OpenGL. Она существует для минимизации количества uniform-переменных, которые необходимо отправлять в ГП, объёма работы, который нужно выполнять в шейдере, и для совместимости со старыми версиями Ren'Py.

В дополнение к этим методам, 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

Нормаль рендерящейся вершины.

Если касательные (tangents) доступны, то также доступны следующие атрибуты:

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

Ожидается кортеж из 4-х логических значений, соответствующих четырём каналам пикселя (красный, зелёный, синий и альфа). Если для данного канала установлено значение True, операция отрисовки будет записывать данные в этот канал пикселя. В противном случае — нет.

gl_cull_face

Если не None, это включает отсечение граней — процесс удаления треугольников, которые обращены от камеры. Для этого нам нужно знать, какая грань является передней, а какая — задней. Передняя грань определяется по порядку обхода вершин, который может быть «cw» (по часовой стрелке) или «ccw» (против часовой стрелки). Значения указаны в системе координат Ren'Py (с осью +Y, направленной вниз), которая противоположна системе координат OpenGL.

gl_depth

Если True, это очистит буфер глубины, а затем включит рендеринг с учётом глубины для этого отображаемого объекта и его дочерних элементов.

Обратите внимание, что отрисовка любого пикселя, даже прозрачного, обновит буфер глубины. В результате использование этого свойства с изображениями, имеющими прозрачность, может привести к неожиданным проблемам. (В качестве альтернативы рассмотрите параметры zorder и behind оператора show.)

gl_pixel_perfect

Если True, Ren'Py переместит меш так, чтобы первая вершина была выровнена по пикселю на экране. В основном это используется в сочетании с текстом, чтобы он оставался чётким.

Следующие свойства вступают в силу только при создании текстуры трансформом с установленным mesh или Model(), где они могут быть переданы через метод property.

gl_drawable_resolution

Если True или не установлено, текстура рендерится в том же разрешении, что и окно, в котором отображается игра. Если False, она рендерится в виртуальном разрешении отображаемого объекта.

gl_anisotropic

Если задано, определяет, создаются ли текстуры, применяемые к мешу, с анизотропией. Анизотропия — это функция, которая заставляет выбирать несколько текселей (пикселей текстуры) при разном масштабировании текстуры по осям X и Y.

По умолчанию установлено в True. Ren'Py устанавливает это в False для определённых эффектов, таких как переход Pixellate.

gl_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'ы доступны только эти четыре, возможно передать «texture_wrap_tex4» или «texture_wrap_myuniform» в Render.add_property.

Отображаемый объект GLTFModel

Отображаемый объект GLTFModel позволяет загружать 3D-модели в формате файлов GLTF. Его следует использовать, если у вас есть 3D-модель, созданная в другой программе, и вы хотите отобразить её в Ren'Py.

class GLTFModel(filename: 'str', shader: 'str | tuple[str]' = (), tangents: 'bool' = False, zoom: 'float' = 1.0, report: 'bool' = False, **kwargs)

Displayable, который загружает 3D-модель в формате GLTF. Этот формат поддерживается многими 3D-инструментами. Ren'Py использует библиотеку Open Asset Importer (assimp) для загрузки моделей GLTF.

Для целей 2D-системы компоновки Ren'Py GLTFModel имеет нулевую ширину и высоту. По умолчанию модель загружается с размером, указанным в файле, который ее содержит. При необходимости для масштабирования можно использовать zoom.

При использовании нескольких моделей свойство gl_depth True должно быть предоставлено камере, чтобы была включена проверка глубины (depth testing). В настоящее время Ren'Py не выполняет отсечение (culling) модели, поэтому важно использовать модели, достаточно простые для полного рендеринга.

filename

Имя файла модели для отображения.

shader

Строка или кортеж строк, содержащие имя используемого шейдера.

tangents

Если True, касательные (tangents) будут включены в меш.

zoom

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

report

Если true, отчет о модели будет выведен в лог. Он включает в себя uniform-переменные, используемые моделью.

Displayable, который загружает 3D-модель в формате GLTF. Этот формат поддерживается многими 3D-инструментами. Ren'Py использует библиотеку Open Asset Importer (assimp) для загрузки моделей GLTF.

Для целей 2D-системы компоновки Ren'Py GLTFModel имеет нулевую ширину и высоту. По умолчанию модель загружается с размером, указанным в файле, который ее содержит. При необходимости для масштабирования можно использовать zoom.

При использовании нескольких моделей свойство gl_depth True должно быть предоставлено камере, чтобы была включена проверка глубины (depth testing). В настоящее время Ren'Py не выполняет отсечение (culling) модели, поэтому важно использовать модели, достаточно простые для полного рендеринга.

filename

Имя файла модели для отображения.

shader

Строка или кортеж строк, содержащие имя используемого шейдера.

tangents

Если True, касательные (tangents) будут включены в меш.

zoom

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

report

Если true, отчет о модели будет выведен в лог. Он включает в себя uniform-переменные, используемые моделью.

Некоторые моменты, которые мы заметили:

  • Некоторые модели очень маленькие, и их нужно увеличивать в 100 или 1000 раз, чтобы они были полезны.

  • Uniform-переменные, генерируемые каждой моделью, могут отличаться.

Мы рекомендуем создавать модели GLTF с параметром report=True, а затем просматривать файл log.txt, чтобы узнать размер и доступные переменные. После этого можно будет масштабировать модель и создать подходящий шейдер.

Ограничения

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="""

        // 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 действует как фабрика для создания моделей для использования с модельным рендерером. Это старый API, который вы можете использовать, если хотите создавать модели в Python.

class Model(size=None, **properties)

Это displayable, который заставляет Ren'Py создать 2D- или 3D-модель для использования с рендерером на основе моделей. Модель будет отрисована за одну операцию с помощью шейдеров, указанных здесь или выбранных охватывающим Transform или Displayable.

size

Если не None, это должен быть кортеж (ширина, высота), который задает размер Модели. Если не задано, модель принимает размер предоставленной ей области. Параметр fit текстуры имеет приоритет.

Если не вызывается ни один метод меша, создается меш, который устанавливает a_position и a_tex_coord в соответствии с тем, как Ren'Py загружает текстуры, при условии, что предоставлена хотя бы одна текстура. В противном случае используется меш, который устанавливает только a_position.

Все методы этого класса возвращают displayable, на котором они были вызваны, что позволяет создавать цепочки вызовов.

child(displayable, fit=False)

Это то же самое, что и метод texture, за исключением того, что параметры focus и main установлены в true.

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)

Добавляет текстуру к этой модели путем рендеринга заданного displayable. Первая добавленная текстура будет tex0, вторая tex1 и так далее.

focus

Если true, события фокуса передаются displayable. Предполагается, что координаты относительно модели соотносятся 1:1 с координатами относительно displayable.

main

Если true, это отмечается как основной дочерний элемент этого displayable, что позволяет инспектировать его с помощью инспектора displayable.

fit

Если true, Модель получает размер displayable. Это может быть истинно только для одной текстуры.

texture_wrap

Если не None, это gl_texture wrap GL property, которое будет применено к этой текстуре.

uniform(name, value)

Устанавливает значение uniform-переменной, которая передается в шейдеры.

name

Строка, задающая имя uniform-переменной для установки, включая префикс "u_".

value

Значение uniform-переменной. Либо float, либо кортеж из 2, 3 или 4 элементов типа float, либо 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, можно найти в Стандартные части шейдеров.