作者:Jorge Canton Ferrero
排版:Alan Wang
这是来自 Plain Concepts 的 Jorge Canton 的客座博客文章。Jorge 是技术初创公司 Syderis 的联合创始人,12 年来一直致力于计算机图形、游戏引擎和图形工具领域的开发。目前,他担任 Plain Concepts 的研究总监,积极参与 Evergine 的开发,Evergine 是一款专为行业应用量身定制的尖端 3D 图形引擎。
您在使用电子商务平台的时候,曾产生过希望能够使用 3D 图像而不是静态图像展示并从任何角度查看产品的想法吗?您曾经在查看大型购物中心的地图的时候,是否有过如果可以用可探索的 3D 地图,导航会更容易的念头?在本文中,我们将学习如何使用 .NET MAUI 实现所有这些以及更多的功能。
Evergine 是一款于 2012 年用 C# 开发的跨平台 3D 引擎。Evergine 因无缝集成功能而闻名并成为工业项目的首选。它可以轻松地集成到现有项目中,也可以与其他技术配合使用。借助 Evergine,您可以制作与各种平台兼容的应用程序,包括 Windows、Linux、Android、iOS、Hololens、Meta Quest/Quest2/Quest Pro、Pico 和 Web。
Evergine 还拥有与各种 UI 技术的无缝集成,包括 WPF、Forms、SDL、UWP、Html/Javascript、WinUI,现在甚至还包括 .NET MAUI。我们致力于跟踪最新的 .NET 版本和工具,为我们的客户提供最佳的体验。
Evergine 的主要功能包括:
Evergine 的使用是完全免费的,没有许可费和版税,适用于商业和非商业项目。Evergine 的商业模式是围绕其提供的附加服务进行收费,例如:
价格可在官方网站上查询。
在最新的 Evergine 版本中,引入了新的 .NET MAUI 项目模板。有了这个模板,Evergine 可以让您创建一个标准的 .NET MAUI 项目,其中包含 EvergineView 控件,该控件可以无缝集成到应用程序的任何视图中。EvergineView 控件充当 Evergine 生成的 3D 场景的画布,您可以使用 Evergine Studio 配置它。
EvergineView 是一个抽象的自定义 .NET MAUI 控件,使您能够跨 Windows、Android 和 iOS 平台无缝工作。
public class EvergineView : View
{
public static readonly BindableProperty ApplicationProperty =
BindableProperty.Create(nameof(Application), typeof(EvergineApplication), typeof(EvergineView), null);
public static readonly BindableProperty DisplayNameProperty =
BindableProperty.Create(nameof(DisplayName), typeof(string), typeof(EvergineView), string.Empty);
public EvergineApplication Application
{
get { return (EvergineApplication)this.GetValue(ApplicationProperty); }
set { this.SetValue(ApplicationProperty, value); }
}
public string DisplayName
{
get { return (string)this.GetValue(DisplayNameProperty); }
set { this.SetValue(DisplayNameProperty, value); }
}
public event EventHandler PointerPressed;
public event EventHandler PointerMoved;
public event EventHandler PointerReleased;
internal void StartInteraction() => this.PointerPressed?.Invoke(this, EventArgs.Empty);
internal void MovedInteraction() => this.PointerMoved?.Invoke(this, EventArgs.Empty);
internal void EndInteraction() => this.PointerReleased?.Invoke(this, EventArgs.Empty);
}
另一方面,渲染实现是使用每个平台的自定义处理程序创建的。以下代码是 Android 处理程序的示例。
public partial class EvergineViewHandler : ViewHandler<EvergineView, AndroidSurfaceView>
{
private AndroidSurface androidSurface;
private AndroidWindowsSystem windowsSystem;
public EvergineViewHandler(IPropertyMapper mapper, CommandMapper commandMapper = null)
: base(mapper, commandMapper)
{ }
public static void MapApplication(EvergineViewHandler handler, EvergineView evergineView)
{
handler.UpdateApplication(evergineView, evergineView.DisplayName);
}
internal void UpdateApplication(EvergineView view, string displayName)
{
if (view.Application is null) { return; }
// Register Windows system
view.Application.Container.RegisterInstance(this.windowsSystem);
// Creates XAudio device
var xaudio = new global::Evergine.OpenAL.ALAudioDevice();
view.Application.Container.RegisterInstance(xaudio);
System.Diagnostics.Stopwatch clockTimer = System.Diagnostics.Stopwatch.StartNew();
this.windowsSystem.Run(
() =>
{
this.ConfigureGraphicsContext(view.Application as SneakerApp.MyApplication, this.androidSurface);
view.Application.Initialize();
},
() =>
{
var gameTime = clockTimer.Elapsed;
clockTimer.Restart();
view.Application.UpdateFrame(gameTime);
view.Application.DrawFrame(gameTime);
});
}
protected override AndroidSurfaceView CreatePlatformView()
{
this.windowsSystem = new AndroidWindowsSystem(this.Context);
this.androidSurface = this.windowsSystem.CreateSurface(0, 0) as AndroidSurface;
return this.androidSurface.NativeSurface;
}
private void ConfigureGraphicsContext(MyApplication application, Surface surface)
{
var graphicsContext = new VKGraphicsContext();
graphicsContext.CreateDevice();
SwapChainDescription swapChainDescription = new SwapChainDescription()
{
SurfaceInfo = surface.SurfaceInfo,
Width = surface.Width,
Height = surface.Height,
ColorTargetFormat = PixelFormat.R8G8B8A8_UNorm,
ColorTargetFlags = TextureFlags.RenderTarget | TextureFlags.ShaderResource,
DepthStencilTargetFormat = PixelFormat.D24_UNorm_S8_UInt,
DepthStencilTargetFlags = TextureFlags.DepthStencil,
SampleCount = TextureSampleCount.None,
IsWindowed = true,
RefreshRate = 60,
};
var swapChain = graphicsContext.CreateSwapChain(swapChainDescription);
swapChain.VerticalSync = true;
var graphicsPresenter = application.Container.Resolve<GraphicsPresenter>();
var firstDisplay = new global::Evergine.Framework.Graphics.Display(surface, swapChain);
graphicsPresenter.AddDisplay("DefaultDisplay", firstDisplay);
application.Container.RegisterInstance(graphicsContext);
surface.OnScreenSizeChanged += (_, args) =>
{
swapChain.ResizeSwapChain(args.Height, args.Width);
};
}
}
此外,Evergine 自定义处理程序的注册是在项目的 MauiProgram.cs 文件中执行的。
public static class .NET MAUIProgram
{
public static .NET MAUIApp CreateMauiApp()
{
var builder = .NET MAUIApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseMauiEvergine()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
#if DEBUG
builder.Logging.AddDebug();
#endif
return builder.Build();
}
}
最后,3D 场景可能非常复杂,可能包含各种资源,例如纹理、材质和模型等。Evergine 可以方便地将所有 3D 场景资源打包并传输到设备上。为了实现这一目标,Evergine 模板包含了特定的目标,将 3D 内容标记为 MauiAsset ,并将其注入到 .NET MAUI 目标管道中。
从官方网站下载并安装 Evergine 后,您首先要了解 Evergine 启动器。它使您能够管理不同的版本、使用各种项目模板创建新项目、访问示例项目以及查找支持和文档的链接。
如果您想要从 Evergine 启动器启动新项目,请导航至“My Projects”部分,然后单击“Add New Project”按钮
此时项目配置窗口就会打开,允许您选择项目名称、项目的磁盘位置以及所需的 Evergine 版本。此外,您还可以选择新的 .NET MAUI 模板。
单击“Create”按钮后,Evergine Studio 就会打开。然后,您可以将任何基本对象添加到场景中,并附加一个 Spinner 组件,其值为{x:1, y:2, z:3},以实现对基本对象的旋转。
如果您想要访问 .NET MAUI 解决方案,只需从 Evergine Studio 中的“File”菜单将其打开即可。
当您在 Visual Studio 中启动 .NET MAUI 解决方案时,您会发现该解决方案中有两个集成项目。第一个项目是您在所有模板之间共享的 Evergine 项目,第二个是 .NET MAUI 项目,它引用了 Evergine 项目。
在 .NET MAUI 项目中,您将会找到 Platform 文件夹,其中包含特定于平台的资源,例如 Android Manifest 和 Info.plist 文件。在 Evergine 文件夹中,您将看到 EvergineView 控件。该控件可以轻松集成到您的 XAML 页面中,使您能够包含 Evergine 画布来渲染 3D 场景。
如果您想要在各种平台上部署您的项目,请使用 Visual Studio 中的“Run/Deploy”按钮。请注意,对于 iOS 部署,您需要在 Visual Studio 和 Mac 之间建立连接,并将 iOS 设备(iPad 或 iPhone)链接到您的 Mac。
在 .NET MAUI 解决方案中成功部署项目后,您将获得类似于上述示例的结果。下图展示了 .NET MAUI 中的基本 XAML 页面,其中包含一个 Label 和一个 EvergineView。这只是一个示例,但您可以自由地利用最新的 .NET 技术和 Evergine 来制作出色的项目。
您可以探索我们的展示应用程序,它演示了如何将移动应用程序 UI 与 3D 内容无缝融合,并有效地在 Evergine 和 .NET MAUI UI 之间进行通信。
存储库:https://github.com/EvergineTeam/EverSneaks
存储库:https://github.com/jsuarezruiz/netmaui-carrental-app-challenge
在 Evergine 官方网站中,您可以找到很多有关入门 Evergine 的有趣资源,以下是最重要的资源。
目前 .NET MAUI 模板使用 .NET 7稳定版。在11 月份 .NET 8 稳定版发布后,为了使用最新的改进和功能,我们会将 .NET MAUI 模板更新为 .NET 8 稳定版。
我们坚信社区可以利用这些技术打造出极具吸引力的应用程序。我们迫不及待地想要见证您将 3D 与 .NET MAUI 相结合,从而创造出令人激动的作品的过程!
我们非常期待您的反馈。祝您在使用 Evergine 和 .NET MAUI 编码时愉快!