3D-графика в Go

от автора

В большинстве случаев, когда речь заходит об использовании Go, вспоминается backend или DevOps и в самую последнюю очередь можно подумать об использовании Go для создания мобильных или десктопных приложений. Но в действительности, благодаря возможностям интеграции с нативными библиотеками (в том числе, OpenGL и OpenAL для пространственного звука) Go может использоваться и для создания игр (в том числе для мобильной платформы). В этой статье мы обсудим несколько библиотек, которые могут помочь в создании 3D-графики на Go и обсудим вопросы портирования приложений на мобильные платформы.

Любая 3D-игра или приложение с трехмерной визуализацией использует возможности, предоставляемые системными библиотеками для работы с графическим адаптером, который переводит команды из абстрактного API (например, OpenGL или Angle) в концепции, понятные графическому ускорителю (или эмулирует их программно на обычном процессоре). Библиотеки OpenGL основаны на использовании шейдеров — приложений на специальном языке GLSL (OpenGL Shading Language), которые выполняются непосредственно на графическом ускорителе. 3D-графика в OpenGL опирается на два или более шейдера (как минимум должен быть определен вершинный шейдер, который обеспечивает трансформацию 3D-координат в проекцию на экране с учетом матричных преобразований для камеры перспективы, расположения и ориентировки объекта и наблюдателя) и фрагментный шейдер (который обеспечивает вычисление или интерполяцию цвета каждой точки сцены с учетом освещения, теней, отражений и другого). В действительности, разработка на таком низком уровне требуется редко (и доступна, например, в GoMobile, когда мы должны определить код шейдеров самостоятельно) и обычно используются один из 3D-движков, которые скрывают всю сложность за простыми понятиями сцены:

  • камера (расположение наблюдателя, линии взгляда и ориентировки сцены, например «направление вверх»)

  • источники света (фоновое освещение, всенаправленные, точечные источники или прожекторы)

  • объекты сцены (представляются в виде 3d mesh, составленной из треугольников или многоугольников)

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

Для разработки 3D-визуализаций на Go (для desktop) можно использовать одну из следующих библиотек: Azul3D, Harfang3D и G3N:

Azul3D

Azul3D — довольно старый 3D-движок на OpenGL 2, который поддерживает создание визуализаций, трехмерный звук (через OpenAL), физические симуляции (в 2D через chipmunk и в 3D в Open Dynamics Engine ODE). Для реализации 3D-графики нужно самостоятельно определить шейдеры (файлы .vert и .frag), но при этом библиотека дает возможность вычисления необходимых матриц (например, камеры), определения Mesh. Для использования доступны следующие модули:

  • azul3d.org/engine/gfx/window — контекст отображения 3D-графики с поддержкой eventloop для обработки событий клавиатуры и мыши. Для запуска EventLoop используется функция window.Run(gfxLoop, nil). Для получения ожидающих обработки событий нужно использовать window.Poll, который принимает канал с событиями и функцию для обработки window.Event, которая может быть либо событием самого окна (например, window.FramebufferResized), нажатием клавиш (keyboard.Typed) и др. Также можно получать текущее состояние клавиатуры Keyboard() и мыши Mouse().

  • azul3d.org/engine/keyboard — обработка событий нажатия/отпускания клавиш

  • azul3d.org/engine/mouse — обработка событий мыши

  • azul3d.org/engine/gfx — непосредственно структуры для определения графики и загрузки шейдеров. gfx.Device определяет связь с виртуальным графическим адаптером, через который можно получать информацию об области отображения — Bounds(), отображать объекты сцены Draw(bounds, object, cam), управлять настройками OpenGL (например, включать Multisample Anti-Aliasing MSAA) и выполнять отрисовку сцены Render(). Также через gfx можно создавать объекты сцены gfx.NewObject(), определять mesh gfx.NewMesh(), цвета gfx.Color, трансформации gfx.NewTransform(). К объекту сцены присоединяется Mesh, Shader для визуализации (могут быть загружены через gfxutil.LoadShader() из azul3d.org/engine/gfx/gfxutil). 

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

Harfang 3D

Harfang3D — библиотека для использования 3D-графики, совместимая с OpenGL, OpenGL ES (в том числе, Android), Metal, Vulkan, DirectX, также поддерживает API для систем виртуальной реальности (SteamVR c отслеживанием глаз, Oculus Rift, HTC Vive), физическое моделирование (столкновения, механические ограничения), трехмерный звук. Сама библиотека создана на C++, но есть поддержка интеграций с Python, Lua и Go. Использует модель описания сцены через добавление компонентов, которыми могут быть как mesh-объекты, так и источники света и др. При сборке SDK нужно указать -DHG_BUILD_HG_GO=ON (для компиляции Go-связывания).

Модуль импортируется из "github.com/harfang3d/harfang-go/v3" и предоставляет возможность загрузки asset’ов и сцены, манипуляции узлами, создания преднастроенных mesh (например, CreateSphereModel или CreateCubeModel), источников света (CreateSpotLight), камеры (CreateCamera), создания матриц и выполнения преобразований (например, TransformationMat4), реализации eventloop для реакции на действия пользователя. Пример кода для визуализации сцены:

import (    math hg "github.com/harfang3d/harfang-go/v3" )  func main() { hg.InputInit() hg.WindowSystemInit()  //подготовка окна для рисования var resX int32 = 1280 var resY int32 = 720 win := hg.RenderInitWithWindowTitleWidthHeightResetFlags("Harfang Sample", resX, resY, hg.RFMSAA4X) pipeline := hg.CreateForwardPipelineWithShadowMapResolution(4096)  //создание конвейера отрисовки res := hg.NewPipelineResources() vtxLayout := hg.VertexLayoutPosFloatNormUInt8() sphereMdl := hg.CreateSphereModel(vtxLayout, 0.1, 8, 16) sphereRef := res.AddModel("sphere", sphereMdl)  //будем использовать предкомпилированные шейдеры shader := hg.LoadPipelineProgramRefFromFile("resources_compiled/core/shader/default.hps", res, hg.GetForwardPipelineInfo()) //определение материала (для цвета задается RGB от 0 до 1) sphereMat := hg.CreateMaterialWithValueName0Value0ValueName1Value1(shader, "uDiffuseColor", hg.NewVec4WithXYZ(1, 0, 0), "uSpecularColor", hg.NewVec4WithXYZ(1, 0.8, 0))  //создания.и конфигурация фонового цвета scene := hg.NewScene() scene.GetCanvas().SetColor(hg.NewColorWithRGB(0.1, 0.1, 0.1)) scene.GetEnvironment().SetAmbient(hg.NewColorWithRGB(0.1, 0.1, 0.1))  //установка камеры cam := hg.CreateCamera(scene, hg.TransformationMat4(hg.NewVec3WithXYZ(15.5, 5, -6), hg.NewVec3WithXYZ(0.4, -1.2, 0)), 0.01, 100) scene.SetCurrentCamera(cam)  //добавление света hg.CreateSpotLightWithDiffuseDiffuseIntensitySpecularSpecularIntensityPriorityShadowTypeShadowBias(scene, hg.TransformationMat4(hg.NewVec3WithXYZ(-8.8, 21.7, -8.8), hg.Deg3(60, 45, 0)), 0, hg.Deg(5), hg.Deg(30), hg.ColorGetWhite(), 1, hg.ColorGetWhite(), 1, 0, hg.LSTMap, 0.000005)  //создаем объекты сцены (сферы) rows := [][]*hg.Transform{} for z := float32(-100.0); z < 100.0; z += 2.0 { row := []*hg.Transform{} for x := float32(-100.0); x < 100.0; x += 2.0 { node := hg.CreateObjectWithSliceOfMaterials(scene, hg.TranslationMat4(hg.NewVec3WithXYZ(x*0.1, 0.1, z*0.1)), sphereRef, hg.GoSliceOfMaterial{sphereMat}) row = append(row, node.GetTransform()) } rows = append(rows, row) } angle := 0.0 rect := hg.NewIntRectWithSxSyExEy(0, 0, resX, resY)  //основной цикл for !hg.ReadKeyboard().Key(hg.KEscape) && hg.IsWindowOpen(win) { dt := hg.TickClock() angle += float64(hg.TimeToSecF(dt))  //поворот объектов сцены (узлов) на угол for j, row := range rows { rowY := math.Cos(angle + float64(j)*0.1) for i, trs := range row { /*pos := trs.GetPos() pos.SetY(float32(0.1 * (rowY*math.Sin(angle+float64(i)*0.1)*6 + 6.5))) trs.SetPos(pos) */ p := hg.NewVec3() p, _ = trs.GetPosRot() p.SetY(float32(0.1 * (rowY*math.Sin(angle+float64(i)*0.1)*6 + 6.5))) trs.SetPos(p)  } }  //обновление сцены scene.Update(dt)  viewID := uint16(0) //отправка сцены в pipeline hg.SubmitSceneToPipelineWithFovAxisIsHorizontal(&viewID, scene, rect, true, pipeline, res)  //обновление экрана из буфера hg.Frame() hg.UpdateWindow(win) runtime.GC() }  hg.RenderShutdown() hg.DestroyWindow(win) }

Для подготовки ресурсов используются конверторы из 3D-форматов (включая Autodesk FBX) и компиляторы ресурсов (assetc, может быть загружен отсюда). Для определения шейдеров используется собственный язык, во многом совместимый с GLSL (за исключением нескольких важных отличий). 

G3N

Движок G3N написан полностью на Go и использует библиотеки OpenGL для Windows / Linux и MacOS. Также дополнительно поддерживается пространственный звук через OpenAL. Сцена описывается иерархическим графом из узлов (при этом трансформации узла применяются ко всему поддереву, что позволяет создавать сложные анимации сцены). Поддерживается загрузка 3D-моделей в формате obj (Wavefront) и glTF, а также генерация объектов (сфера, цилиндр, куб и другие), вывод текста и анимированных спрайтов (из spritesheet). Также могут использоваться преднастроенные или собственные материалы, загрузка текстур из PNG/JPEG файлов, добавление источников света (точечных и направленных). Доступна возможность определения собственных шейдеров (вершинных, фрагментных и геометрических). Также предоставляется модуль для создания пользовательских интерфейсов из готовых (или созданных программно) виджетов. В библиотеке доступны следующие модули:

  • github.com/g3n/engine/animation — определение анимации свойств узла (рассчитывает tween-значения для канала, который определяет тип интерполяции и изменяемое значение — положение, поворот, масштаб или морфинг между объектами)

  • github.com/g3n/engine/app — определение приложения (App), которое предоставляет доступ к событиям жизненного цикла (Subscribe) и изменений в области отображения (например, window.OnWindowSize) и позволяет запустить eventloop через App().Run(…). Также через функцию Gls() в App() можно непосредственно вызывать функции OpenGL (например, Clear для очистки сцены)

  • github.com/g3n/engine/audio — поддержка воспроизведения и 3D-позиционирования звука (через OpenAL), плеер создается через audio.NewPlayer из файла (Ogg Vorbis или WAV)

  • github.com/g3n/engine/camera — определение камеры (положения, направление взгляда и ориентация сцены), также можно присоединить управляемую камеру (camera.NewOrbitControl)

  • github.com/g3n/engine/core — основные абстракции сцены: core.NewNode() создает новый узел, в который могут быть добавлены новые узлы (например, Mesh, UI-виджет или источник света)

  • github.com/g3n/engine/geometry — создание геометрических фигур (например, geometry.NewTorus или geometry.NewSphere)

  • github.com/g3n/engine/gls — константы и функции OpenGL для использования совместно с App().Gls()

  • github.com/g3n/engine/graphic — создание Mesh для добавления на сцену graphic.NewMesh(geom, mat).

  • github.com/g3n/engine/gui — создание UI (например, gui.NewButton для определения кнопки)

  • github.com/g3n/engine/light — создание источников света (NewAmbient для фонового освещения, NewPoint — точечный источник, NewDirectional — направленный источник, NewSpot — всенаправленный источник). Для каждого источника можно задать цвет, светимость и свойства затухания (а для направленных — еще и основное направление).

  • github.com/g3n/engine/loader — загрузка модели из Wavefront / glTF или Collada. Например, для загрузки объекта из Wavefront можно использовать loader/obj obj.Decode(objpath, mtlpath).NewGroup() возвращает core.Node для добавления в сцену 

  • github.com/g3n/engine/material — определение материала из комбинации диффузного и зеркального рассеивания, прозрачности и текстуры.

  • github.com/g3n/engine/math32 — содержит константы (например, Pi), цвета (преднастроенные и определяемые программно) и математические функции (совместимые с OpenGL)

  • github.com/g3n/engine/renderer — выполняет отрисовку сцены renderer.Render(scene, cam) для указанной камеры

  • github.com/g3n/engine/text — отображение текста (загрузка шрифтов, генерация atlas для дальнейшего отображения на сцене)

  • github.com/g3n/engine/texture — определение текстур (Texture2D) и загрузчиков texture.NewTexture2DFromImage(filename), также позволяет создавать текстуру программно.

  • github.com/g3n/engine/util/helper — создание вспомогательных узлов (например отображение осей)

  • github.com/g3n/engine/window — константы и функции для работы с viewport и подписки на события окна.

Пример кода на G3N:

package main  import ( "github.com/g3n/engine/app" "github.com/g3n/engine/camera" "github.com/g3n/engine/core" "github.com/g3n/engine/geometry" "github.com/g3n/engine/gls" "github.com/g3n/engine/graphic" "github.com/g3n/engine/light" "github.com/g3n/engine/material" "github.com/g3n/engine/math32" "github.com/g3n/engine/renderer" "time" )   func main() { //приложение и сцена a := app.App() scene := core.NewNode()  //создание управляемой камеры cam := camera.New(1) cam.SetPosition(0, 0, 3) scene.Add(cam) camera.NewOrbitControl(cam)  //создание фигуры geom := geometry.NewTorus(1, .4, 12, 32, math32.Pi*2) mat := material.NewStandard(math32.NewColor("DarkGreen")) mesh := graphic.NewMesh(geom, mat) scene.Add(mesh)  //создание освещения (фонового и точечного) scene.Add(light.NewAmbient(&math32.Color{1.0, 1.0, 1.0}, 0.8)) pointLight := light.NewPoint(&math32.Color{1, 1, 0}, 5.0) pointLight.SetPosition(1, 0, 2) scene.Add(pointLight)  //изменение фона в OpenGL a.Gls().ClearColor(0.5, 0.5, 0.5, 1.0)  //запуск приложения и отрисовка кадра a.Run(func(renderer *renderer.Renderer, deltaTime time.Duration) { a.Gls().Clear(gls.DEPTH_BUFFER_BIT | gls.STENCIL_BUFFER_BIT | gls.COLOR_BUFFER_BIT) renderer.Render(scene, cam) }) }

Из рассмотренных библиотек для использования на мобильной платформе наиболее подходящей является Harfang 3D, поскольку она поддерживает взаимодействие с OpenGL ES и может быть собрана через gomobile для создания мобильного приложения. Однако сейчас эта возможность еще находится в стадии эксперимента и работает нестабильно. Но при этом создание 3D-визуализаций на Go для desktop-приложений доступно уже сейчас.


В январе состоится открытый вебинар онлайн-курса «Golang Developer. Professional», на котором руководитель курса проведет Mock-собеседование со студентом. Участники рассмотрят реальные вопросы, разберут комментарии по ответам; спикер поделится советами по прохождению собеседований. Если для кого-то актуально, зарегистрироваться на вебинар можно по ссылке.


ссылка на оригинал статьи https://habr.com/ru/articles/708170/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *