了解!
ここでは「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からアニメーション行列を取り出して描画する方法」をやってみたい場合、より詳細なステップ付きで提供できます!
どこから取りかかってみたいですか?(カメラ中心変更の続き?アニメーションの具体実装?)