SurfaceTool: атрибуты, нормали и tangents
Порядок SetUV/SetColor/AddVertex, сглаживание, индексация и Commit без скрытого накопления.
Запустить цветную поверхность
Добавьте GeometryStage3D.cs и SurfaceToolLab3D.cs. Прикрепите SurfaceToolLab3D к Entity3D-корню отдельного C#-уровня 1.0.0, задайте viewport 640×420, соберите и нажмите F6. Получится квадрат с градиентом вершинных цветов, нормалями и tangents. Material включает VertexColorUseAsAlbedo.
GeometryStage3D.cs
using Reckfell;
public static class GeometryStage3D
{
public static StandardMaterial3D Material(bool vertexColors = false, bool unshaded = false)
{
return new StandardMaterial3D
{
AlbedoColor = vertexColors ? Colors.White : new Color(0.24f, 0.44f, 1),
VertexColorUseAsAlbedo = vertexColors,
ShadingMode = unshaded ? BaseMaterial3D.ShadingModeEnum.Unshaded : BaseMaterial3D.ShadingModeEnum.PerPixel,
Roughness = 0.8f
};
}
public static void Setup(Entity3D root, string ru, string en)
{
root.AddChild(new WorldEnvironment { Environment = new Reckfell.Environment
{
BackgroundMode = Reckfell.Environment.BGMode.Color,
BackgroundColor = new Color(0.035f, 0.04f, 0.05f),
AmbientLightSource = Reckfell.Environment.AmbientSource.Color,
AmbientLightColor = Colors.White, AmbientLightEnergy = 0.5f
}});
root.AddChild(new DirectionalLight3D { RotationDegrees = new Vector3(-55, -25, 0), LightEnergy = 1.2f });
root.AddChild(new MeshInstance3D
{
Mesh = new BoxMesh { Size = new Vector3(8, 0.05f, 6) }, Position = new Vector3(0, -0.12f, 0),
MaterialOverride = new StandardMaterial3D { AlbedoColor = new Color(0.10f, 0.11f, 0.13f), Roughness = 1 }
});
var camera = new Camera3D { Position = new Vector3(4, 4.8f, 6), Current = true, Fov = 45 };
root.AddChild(camera); camera.LookAt(new Vector3(0, 0.35f, 0), Vector3.Up);
var canvas = new CanvasLayer(); root.AddChild(canvas);
var label = new Label { Position = new Vector2(24, 20), Text = TranslationServer.GetLocale().StartsWith("ru") ? ru : en };
label.AddThemeFontSizeOverride("font_size", 22); canvas.AddChild(label);
}
}SurfaceToolLab3D.cs
using Reckfell;
public partial class SurfaceToolLab3D : Entity3D
{
public ArrayMesh Generated { get; private set; } = null!;
public static ArrayMesh CreateQuad()
{
Vector3[] points = [new(-1, 0, -1), new(1, 0, -1), new(1, 0, 1), new(-1, 0, 1)];
Vector2[] uv = [new(0, 0), new(1, 0), new(1, 1), new(0, 1)];
Color[] colors = [new(0.24f, 0.44f, 1), new(0.5f, 0.65f, 1), Colors.White, new(0.24f, 0.44f, 1)];
int[] indices = [0, 1, 2, 0, 2, 3];
using var tool = new SurfaceTool();
tool.Begin(Mesh.PrimitiveType.Triangles);
tool.SetSmoothGroup(0);
foreach (int index in indices)
{
tool.SetColor(colors[index]);
tool.SetUV(uv[index]);
tool.AddVertex(points[index]);
}
tool.GenerateNormals();
tool.Index();
tool.GenerateTangents();
return tool.Commit();
}
public override void _Ready()
{
GeometryStage3D.Setup(this, "SurfaceTool: атрибуты до AddVertex", "SurfaceTool: attributes before AddVertex");
Generated = CreateQuad();
AddChild(new MeshInstance3D { Mesh = Generated, MaterialOverride = GeometryStage3D.Material(vertexColors: true) });
}
}Порядок вызовов
- Begin задаёт тип примитивов.
- SetColor, SetUV, SetNormal и другие Set-методы задают атрибуты следующей вершины.
- AddVertex фиксирует позицию вместе с текущими атрибутами.
- После построения вызовите GenerateNormals для Triangles, если не передавали корректные нормали.
- Index объединяет совпадающие полные наборы атрибутов. GenerateTangents требует UV, нормали и треугольники.
- Commit создаёт ArrayMesh либо добавляет поверхность в переданный existing.
Формат включённых атрибутов фиксируется началом выдачи вершин. Установите нужные атрибуты до первой AddVertex; впервые добавлять цвет после нескольких бесцветных вершин нельзя. Значение атрибута сохраняется до следующего Set, поэтому пропущенный SetUV может незаметно повторить предыдущее UV.
Гладкая и плоская поверхность
SetSmoothGroup(0) в примере позволяет сглаживание в одной группе. Разные группы разделяют расчёт нормалей; uint.MaxValue отключает сглаживание для плоских граней. Задавайте группу до относящихся к ней вершин и генерируйте нормали после построения. Для квадрата обе грани лежат в одной плоскости, поэтому нормали Up совпадают.
Совпавшая позиция ещё не означает одинаковую вершину: UV, цвет, нормаль и другие данные могут различаться. Index сохраняет такие разрывы. В примере после индексации остаются четыре вершины и шесть индексов; не ожидайте того же сокращения у модели со швами.
Tangents описывают направление UV для работы с tangent-space normal map. Генерация не заменяет корректную UV-развёртку и может разделять данные по швам. Проверены длина tangent-массива и его знак; произвольная UV-топология требует собственной проверки.
Не накапливать поверхности
Commit(existing) добавляет поверхность, что подтверждено отдельным вызовом в стенде. Он не заменяет прошлую геометрию и не является автоматической оптимизацией каждого кадра. Используйте Clear для нового построения самого SurfaceTool; для замены поверхности подготовьте собственный новый Mesh или явно управляйте поверхностями ArrayMesh.
Здесь проверены порядок атрибутов, цвета, нормали, tangents, индексирование, добавление поверхности и реальная отрисовка Windows/OpenGL. Создание коллайдера, экспорт и ускорение на конкретном GPU — отдельные задачи.