Рендеринг на основе моделей
Хотя 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()и ATLTransform создаёт модель, если
meshравно True, или если используетсяblur. В этом случае дочерние элементы Transform'а рендеринются в текстуры, причём меш первой текстуры используется для меша, связанного с моделью.Не каждый трансформ создаёт модель. Некоторые трансформы просто добавляют шейдеры и uniform-переменные к Render'у (например, трансформы, использующие
blurилиalpha). Другие трансформы просто влияют на геометрию.RenderRender создаёт модель, если его атрибут
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, можно найти в Стандартные части шейдеров.