了解!
ここでは「C#で書けて、Linuxでも動く、簡単な3Dモデルビューア」を紹介します。
C#でクロスプラットフォームなGUI+3Dをやるなら、以下の組み合わせが最有力です👇
✅ 構成:C# + Avalonia UI + OpenTK
| 技術 | 役割 |
|---|---|
| C# | 開発言語 |
| Avalonia UI | Linux/Windows/macOS対応のGUIフレームワーク |
| OpenTK | OpenGLラッパー(3D描画) |
| AssimpNet(任意) | 3Dモデル読み込み用ライブラリ(.obj, .glb, etc.) |
🚀 開発環境セットアップ
必要ツール:
- .NET SDK(例:
dotnet 8.0) - 任意のIDE:VS Code / JetBrains Rider / Visual Studio
dotnet new install Avalonia.Templates dotnet new avalonia.app -n Simple3DViewer cd Simple3DViewer
🎮 最小構成サンプル(OpenGLで三角形を描画)
MainWindow.axaml(UI定義)
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Simple3DViewer.MainWindow"
Width="800" Height="600"
Title="3D Viewer">
<Grid>
<local:GLControl />
</Grid>
</Window>
GLControl.cs(OpenTK描画)
using Avalonia.OpenGL;
using Avalonia.OpenGL.Controls;
using OpenTK.Graphics.OpenGL;
public class GLControl : OpenGlControlBase
{
protected override void OnOpenGlInit(GlInterface gl, int fb)
{
GL.ClearColor(0.1f, 0.1f, 0.2f, 1.0f);
}
protected override void OnOpenGlRender(GlInterface gl, int fb)
{
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
// 三角形描画
GL.Begin(PrimitiveType.Triangles);
GL.Color3(1.0, 0.0, 0.0); GL.Vertex2(-0.6, -0.4);
GL.Color3(0.0, 1.0, 0.0); GL.Vertex2(0.6, -0.4);
GL.Color3(0.0, 0.0, 1.0); GL.Vertex2(0.0, 0.6);
GL.End();
}
}
✅ ビルドと実行
dotnet run
✅ WindowsでもLinuxでも、同じコードでビルド&実行可能!
🔁 今後追加できる機能例
| 機能 | 方法 |
|---|---|
| 3Dモデル読み込み | AssimpNet で .obj, .glb 読み込み |
| マウスで回転・ズーム | OpenTKでカメラ制御追加 |
| ライト・マテリアル | OpenGLで基本ライティングを追加 |
| GUIボタンで操作 | AvaloniaのUI部品と連携可 |
📦 モデル読み込みの例(AssimpNet)
dotnet add package AssimpNet
var importer = new Assimp.AssimpContext();
var scene = importer.ImportFile("sample.obj", Assimp.PostProcessSteps.Triangulate);
foreach (var mesh in scene.Meshes)
{
// mesh.Vertices[i], mesh.Normals[i] などから描画用データ作成
}
✨ 補足
- Avalonia + OpenTK の組み合わせは、WPFのようなGUIとOpenGLの自由度を両立できる貴重な選択肢。
- 特に、WPFを触っていた人には自然なXAMLベースで移行しやすいです。
もし興味があれば:
.glbファイルの読み込み+マウス操作のコード- GUI操作でモデル選択する機能
- 回転・ズーム対応のサンプル
なども順に追加できます!
C# + Avalonia + OpenTK で、 .glb ファイルの読み込み+マウス操作のコード
了解!では、C# + Avalonia + OpenTK で .glb ファイルを読み込み、マウスで回転・ズームできる簡単な3Dビューアのコード構成を紹介します。
🎯 やりたいことの概要
.glb(GLTFバイナリ)ファイルを読み込み- 3D描画 → OpenTK(OpenGL)
- マウス操作 → カメラ回転&ズーム
- UI → Avalonia(GUI)
🧱 使うライブラリ
dotnet add package OpenTK dotnet add package AssimpNet dotnet add package Avalonia
🔧 補足:AssimpNet は .glb ファイルの読み込みをサポートしています(内部的に Assimp を使用)
📁 構成イメージ
Simple3DViewer/ ├── Program.cs ├── MainWindow.axaml ├── MainWindow.xaml.cs ├── GLControl.cs ← OpenGL描画+マウス操作 ├── GltfLoader.cs ← .glbファイル読み込み
🧩 コアパート①:GLControl(描画 + マウス操作)
public class GLControl : OpenGlControlBase
{
private float angleX = 0f, angleY = 0f;
private float zoom = -3f;
private List<Vector3> vertices = new();
public void LoadModel(string path)
{
var context = new AssimpContext();
var scene = context.ImportFile(path, PostProcessSteps.Triangulate);
vertices.Clear();
foreach (var mesh in scene.Meshes)
{
foreach (var v in mesh.Vertices)
vertices.Add(new Vector3(v.X, v.Y, v.Z));
}
InvalidateVisual(); // 再描画
}
protected override void OnOpenGlInit(GlInterface gl, int fb)
{
GL.Enable(EnableCap.DepthTest);
}
protected override void OnOpenGlRender(GlInterface gl, int fb)
{
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.LoadIdentity();
GL.Translate(0, 0, zoom);
GL.Rotate(angleX, 1, 0, 0);
GL.Rotate(angleY, 0, 1, 0);
GL.Begin(PrimitiveType.Triangles);
GL.Color3(0.8f, 0.8f, 1f);
foreach (var v in vertices)
{
GL.Vertex3(v);
}
GL.End();
}
protected override void OnPointerMoved(PointerEventArgs e)
{
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
{
var delta = e.GetPosition(this);
angleX += (float)delta.Y * 0.1f;
angleY += (float)delta.X * 0.1f;
InvalidateVisual();
}
}
protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
{
zoom += (float)e.Delta.Y * 0.1f;
InvalidateVisual();
}
}
🧩 コアパート②:MainWindow.axaml(ボタン付き)
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Simple3DViewer"
x:Class="Simple3DViewer.MainWindow"
Width="800" Height="600"
Title="GLB Viewer">
<DockPanel>
<Button DockPanel.Dock="Top" Click="OnOpenFile">Open .glb File</Button>
<local:GLControl Name="glView" />
</DockPanel>
</Window>
🧩 コアパート③:MainWindow.xaml.cs(ファイル読み込み)
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Platform.Storage;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void OnOpenFile(object sender, RoutedEventArgs e)
{
var files = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{
Title = "Select .glb File",
AllowMultiple = false,
FileTypeFilter = new[] { new FilePickerFileType("GLB") { Patterns = new[] { "*.glb" } } }
});
if (files.Count > 0)
{
glView.LoadModel(files[0].Path.LocalPath);
}
}
}
✅ ビルドして試す
dotnet run
- 起動後、
Open .glb Fileボタンで.glbファイルを選択 - 3Dモデルが表示され、マウスドラッグで回転、ホイールでズーム可能
🔄 拡張アイデア
| 機能 | 方法 |
|---|---|
| 面ごとの法線・色 | mesh.Normals, mesh.Colors を活用 |
| アニメーション再生 | glTFのアニメーション情報をパース |
| テクスチャ表示 | mesh.Material.TextureDiffuse を読み取り OpenGLに渡す |
このテンプレートでかなり柔軟な3Dビューアが作れます。
もし「テクスチャ付きで表示したい」「GLBだけじゃなくOBJも読みたい」などあれば、それもすぐ追加できます!
次に進めたい部分はどこでしょう?たとえば:
- アニメーション再生
- カメラのリセット・回転中心変更
✅ ① カメラのリセット
目的: 「元の視点に戻す」ボタンなどで、カメラの角度・ズームを初期値に戻す。
🔧 追加コード(GLControl)
public void ResetCamera()
{
angleX = 0f;
angleY = 0f;
zoom = -3f;
InvalidateVisual();
}
🔘 UIでの呼び出し(MainWindow.xaml.cs)
private void OnResetCamera(object sender, RoutedEventArgs e)
{
glView.ResetCamera();
}
✅ ② 回転中心の変更
問題点: 現在は「ワールド原点 (0,0,0)」を中心に回転している
やりたいこと: 読み込んだモデルの中心を算出して、そこを中心にカメラ回転
🔧 対応方法
モデル中心(重心)を求めて、OpenGL変換時に GL.Translate(-center) で調整
private Vector3 modelCenter = Vector3.Zero;
public void LoadModel(string path)
{
// ...(Assimp読み込み)
vertices.Clear();
Vector3 sum = Vector3.Zero;
foreach (var mesh in scene.Meshes)
{
foreach (var v in mesh.Vertices)
{
var vec = new Vector3(v.X, v.Y, v.Z);
vertices.Add(vec);
sum += vec;
}
}
modelCenter = sum / vertices.Count;
InvalidateVisual();
}
protected override void OnOpenGlRender(GlInterface gl, int fb)
{
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.LoadIdentity();
GL.Translate(0, 0, zoom);
GL.Rotate(angleX, 1, 0, 0);
GL.Rotate(angleY, 0, 1, 0);
GL.Translate(-modelCenter.X, -modelCenter.Y, -modelCenter.Z);
// ...描画処理
}
これで、モデルの中心を原点に合わせて回転する動作になります。
✅ ③ アニメーション再生(GLBの)
.glb ファイルはアニメーション(ボーン/キーフレーム)を含む場合があります。
📦 AssimpNetでの取得例:
var scene = context.ImportFile(path, PostProcessSteps.Triangulate);
if (scene.HasAnimations)
{
var anim = scene.Animations[0]; // 最初のアニメーション
float duration = anim.DurationInTicks;
float ticksPerSecond = anim.TicksPerSecond > 0 ? anim.TicksPerSecond : 25;
float totalTime = duration / ticksPerSecond;
// → 再生用のタイマー開始
}
⏱ アニメーション再生(Timer利用)
- フレーム毎に
currentTime += deltaTime - ボーンマトリクスを補間・適用
- OpenGLで再描画
Assimpでのアニメーションはボーン階層付きスキニングが必要なので、完全な実装は少し重めですが、簡易バージョンなら可能です。
👇 まとめ
| やりたいこと | 方法 |
|---|---|
| カメラリセット | 角度・ズーム変数を初期値に戻す |
| 回転中心変更 | モデル中心(重心)を求めて補正 |
| アニメーション再生 | scene.Animations を参照して、毎フレーム補間 |
もし「スキニングやボーンアニメーションの具体例」や「Assimpからアニメーション行列を取り出して描画する方法」をやってみたい場合、より詳細なステップ付きで提供できます!
どこから取りかかってみたいですか?(カメラ中心変更の続き?アニメーションの具体実装?)
