reckfellENGINE

Растительность: ветер и материал листьев

Полный шейдер, маска ветра в цветах вершин, отсечение альфы, границы и диагностика.

На этой странице

Этот пример добавляет ветер и отсечение прозрачных участков листа к готовой 3D-модели. Он не создаёт геометрию дерева. Для рабочего ассета нужны нормальные ствол, ветви, листва, UV, материалы и уровни детализации; шейдер не исправит плохую модель или освещение.

Подготовьте модель

  1. Разделите кору и листву на поверхности с отдельными материалами. Коре обычно нужен непрозрачный материал; текстура листьев должна содержать полезную альфу.
  2. В редакторе модели покрасьте красный канал цвета вершин: 0 у закреплённых точек, плавно до 1 у подвижных концов. Чёрный удерживает вершину; белый даёт полную амплитуду. UV и цвет вершин — разные данные.
  3. Экспортируйте геометрию с UV, нормалями и цветами вершин. После импорта проверьте реальное движение: если цвета потеряны, вся поверхность может колебаться одинаково. Для диагностики временно используйте wind_amplitude = 0.3 и затем верните небольшое значение.
  4. Назначайте материал листвы только её поверхности. MaterialOverride у MeshInstance3D перекрывает материалы всех поверхностей: так легко случайно превратить кору в листья. Для отдельной поверхности используйте SetSurfaceOverrideMaterial с её индексом.

Для начала возьмите одну ветку с листьями, направленный свет и нейтральное окружение. Убедитесь, что геометрия и текстура выглядят правильно без ветра. Масштаб модели должен соответствовать проекту.

Полный шейдер листьев

Создайте Shader с этим кодом и ShaderMaterial, который его использует. Назначьте RGBA-текстуру в leaf_albedo. Ветер смещает вершины в мировой плоскости XZ, а не вдоль локальных осей дерева.

Шейдер
shader_type spatial;
render_mode cull_disabled, world_vertex_coords;

uniform sampler2D leaf_albedo : source_color, hint_default_white, filter_linear_mipmap, repeat_disable;
uniform float wind_amplitude : hint_range(0.0, 1.0) = 0.06;
uniform vec2 wind_direction = vec2(1.0, 0.0);
uniform float wind_frequency = 0.8;
uniform float wind_speed = 1.2;
uniform float wind_phase = 0.0;
uniform float alpha_cutoff : hint_range(0.0, 1.0) = 0.5;
uniform float backlight_strength : hint_range(0.0, 1.0) = 0.1;

void vertex() {
    vec2 direction = wind_direction / max(length(wind_direction), 0.0001);
    float phase = dot(VERTEX.xz, vec2(0.8, 0.6)) * wind_frequency;
    float weight = clamp(COLOR.r, 0.0, 1.0);
    float offset = sin(phase + TIME * wind_speed + wind_phase) * wind_amplitude * weight;
    VERTEX.xz += direction * offset;
}

void fragment() {
    vec4 leaf = texture(leaf_albedo, UV);
    ALBEDO = leaf.rgb;
    ALPHA = leaf.a;
    ALPHA_SCISSOR_THRESHOLD = alpha_cutoff;
    ROUGHNESS = 1.0;
    BACKLIGHT = leaf.rgb * backlight_strength;
}
  • COLOR.r — только маска подвижности; он не умножается на ALBEDO и не окрашивает листья в красный.
  • wind_amplitude задаёт максимальное смещение в мировых единицах при весе 1. Длина wind_direction не усиливает движение; нулевое направление его отключает.
  • wind_frequency меняет пространственную частоту. Это множитель координат в фазе синуса, а не длина волны в метрах.
  • wind_speed меняет скорость фазы, wind_phase — её сдвиг в радианах. Для проверки замрите время ветра через wind_speed = 0; при wind_frequency = 0 фазы +π/2 и −π/2 дают противоположные крайние положения.
  • ALPHA_SCISSOR_THRESHOLD вырезает прозрачные области. Это жёсткий порог, не полупрозрачность стекла. Мягкие края требуют отдельного решения и проверки выбранного сглаживания.
  • cull_disabled показывает обе стороны. В текущем Forward+ нормаль обратной стороны уже разворачивается до пользовательского fragment; повторное NORMAL = -NORMAL испортит освещение.
  • BACKLIGHT добавляет простой вклад света с обратной стороны. Это художественная настройка, не физическая модель толщины листа. Начинайте с малого значения.

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

Создание материала из C#

Этот вспомогательный метод принимает готовые Shader и Texture2D. Он проверяет тип шейдера и назначает текстуру по точному имени uniform.

C#
using System;
using Reckfell;

public static class FoliageMaterial
{
    public static ShaderMaterial Create(Shader shader, Texture2D leafTexture)
    {
        ArgumentNullException.ThrowIfNull(shader);
        ArgumentNullException.ThrowIfNull(leafTexture);
        if (shader.GetMode() != Shader.Mode.Spatial)
            throw new ArgumentException("A spatial leaf shader is required.", nameof(shader));
        var material = new ShaderMaterial { Shader = shader };
        material.SetShaderParameter("leaf_albedo", leafTexture);
        return material;
    }
}

Результат FoliageMaterial.Create(shader, texture) назначьте в материал поверхности листвы. Для MeshInstance3D, содержащего только листву, допустим MaterialOverride. Пути загрузки зависят от вашего проекта: не копируйте выдуманные пути к ассетам. Shader.GetMode проверяет объявленный тип, но не доказывает успешную компиляцию GPU-кода.

Границы, свет и цена кадра

GPU-деформация не меняет AABB исходного Mesh и автоматически не обновляет коллизию. Без запаса в границах листва может исчезать у края камеры или в теневом проходе. Увеличьте ExtraCullMargin либо задайте CustomAabb, который покрывает все положения. В текущей реализации запас применяется к локальным границам до трансформации: при единичном масштабе запас не меньше wind_amplitude покрывает смещение этого примера; при уменьшенном или неравномерном масштабе пересчитайте локальный запас. Не раздувайте границы всего леса без измерения стоимости отсечения.

Проверьте тень на земле при движении ветки. Для корректной демонстрации используйте динамический источник с включёнными тенями; статическая запечённая тень не будет повторять колебания. Этот проход проверок отдельно не подтверждает качество теней или запекания.

Начните с небольшого количества растений. Большие перекрывающиеся карточки листвы увеличивают число обрабатываемых пикселей; двусторонность и тени тоже имеют цену. Уменьшайте пустую площадь карточек, готовьте LOD и проверяйте время GPU на целевом устройстве. MultiMesh уменьшает накладные расходы большого числа экземпляров, но не устраняет стоимость самих листьев, прозрачных областей и теней.

Если результат неправильный

СимптомЧто проверить
Белые прямоугольники вместо листьевНазначена ли RGBA-текстура в leaf_albedo; есть ли альфа и корректные UV
Двигаются корни или весь объект одинаковоКрасный канал вершин и сохранение цветов при экспорте
Ветер идёт поперёк ожидаемого направленияwind_direction задан в мировых XZ; поворот объекта не поворачивает направление ветра
Листва исчезает у края экранаГраницы с учётом максимальной деформации, масштаб и параметры видимости
Обратная сторона неожиданно темнееДублирование разворота NORMAL, направление света и BACKLIGHT; двусторонность сама по себе не добавляет свет
Мерцание силуэта вдалиMipmaps и фильтрация текстуры, порог альфы, LOD и сглаживание
Кора стала прозрачнойМатериал назначен через общий MaterialOverride вместо поверхности листвы

Проверено на текущем Windows Forward+/Vulkan: полный шейдер и C#-метод, отсечение альфы, обе стороны, маски 0/0.5/1, ограничение маски, направление и фаза, реальное движение TIME, градиент и мировое смещение при переносе, повороте и неравномерном масштабе. Малые тестовые карточки не являются изображениями растений или демонстрацией качества графики. Импорт настоящей модели, интерфейс редактора, нормали при сильном изгибе, качество теней, лес с LOD, производительность и другие бэкенды отдельно не проверялись.

Смежные задачи: пространственные шейдеры, язык шейдеров, MultiMesh, GeometryInstance3D.

Документация Reckfell EngineНаверх ↑
БЫСТРЫЙ ПЕРЕХОДНачало работыEntity и иерархияC# & .NET 10Сцены и префабы
Руководства и полный API Reckfell K
reckfellENGINE
RECKFELL ACCOUNT

Вход в аккаунт

Войдите в аккаунт Reckfell.

Один аккаунт для всей платформы Reckfell.