質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
OpenGL

OpenGLは、プラットフォームから独立した、デスクトップやワークステーション、モバイルサービスで使用可能な映像処理用のAPIです。

Xamarin

Xamarin(ザマリン)は、iPhoneなどのiOSやAndroidで動作し、C# 言語を用いてアプリを開発できるクロスプラットフォーム開発環境です。Xamarin Studioと C# 言語を用いて、 iOS と Android の両方の開発を行うことができます。

Q&A

解決済

1回答

2276閲覧

XamarinにてOpenTKで線描画がしたい

crew8573

総合スコア33

OpenGL

OpenGLは、プラットフォームから独立した、デスクトップやワークステーション、モバイルサービスで使用可能な映像処理用のAPIです。

Xamarin

Xamarin(ザマリン)は、iPhoneなどのiOSやAndroidで動作し、C# 言語を用いてアプリを開発できるクロスプラットフォーム開発環境です。Xamarin Studioと C# 言語を用いて、 iOS と Android の両方の開発を行うことができます。

0グッド

0クリップ

投稿2017/11/08 11:59

Xamarin.FormsにてOpenTKを使用して線描画がしたいです。

C++でOpenGLを使用していた頃は、以下のような形でシンプルに線を描画できていたのですが、
C#でOpenTKを使用してopenGLの機能を使おうとするとメソッドの形式等が変更が多すぎて実現できません。

lang

1 glColor3d(1.0, 0.0, 0.0); //「3」色で「d」ouble型 2 3 glBegin(GL_LINES); 4 glVertex2d(-0.9,-0.9); 5 glVertex2d(0.9,0.6); 6 glEnd();

上記のBeginやEnd, Vertex2dのようなメソッドが呼び出すことができず、
参考サイトを見たところDrawArraysというメソッドを使っておりました。

私がプロジェクトで参照しているdllはVisual Studio2017インストール時に
同時にインストールされたOpenTK-1.0.dllというものです。

開発するために参考にできるサイトはないでしょうか?
また、DrawArraysを利用した簡単な線描画(グラデーション線等)の方法を教えていただけないでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

残念ながら昔のOpenGLのように直感的に書くことは難しいかと思います。

参考サイトですが、おそらくXamarin.FormsとOpenTKの組み合わせに限定して情報を探そうとしても、ほとんど出てこなかったのではないでしょうか?
まずは何かしら描画させるところまで成功させて(ここまでが一番のハードルかもしれません)、あとは他の環境向けの情報をご自身の環境に合わせて読み替えるのがよさそうです。特に、ご質問者さんの環境とOpenGLのバージョンが一致する情報が有用かと思います。

Xamarinではないですが、OpenTKでキューブを表示 | Miyabiarts.netは参考になりそうに思いました。#region OpenTKも面白そうですが、残念ながら大部分の解説は古いOpenGLに基づいているようで、ご質問者さんの環境には応用しにくいでしょう。

ご参考になればと思いまして、OpenTK: C#でマルチプラットフォームのOpenGLコードを実装する - Qiitaで紹介されていたOpenGLViewを使ったパターンを試してみました。あいにくXamarinの使用経験がなく、Xamarinの流儀らしくない箇所もあるかもしれませんがご容赦ください。
Visual Studio Community 2017 for Macを使用し、iOS向けにビルド、iOSシミュレータ上で実行してみました。

C#

1using System; 2using System.Runtime.InteropServices; 3using OpenTK.Graphics.ES30; 4using Xamarin.Forms; 5 6namespace XamarinGLTest 7{ 8 public class MainPage : ContentPage 9 { 10 public MainPage() 11 { 12 this.Title = "OpenGL Test"; 13 14 var view = new OpenGLView { HasRenderLoop = true }; 15 var toggle = new Switch { IsToggled = true }; 16 var button = new Button { Text = "Display" }; 17 18 view.HeightRequest = 300; 19 view.WidthRequest = 300; 20 21 view.OnDisplay = this.OnDisplay; 22 23 toggle.Toggled += (s, a) => { 24 view.HasRenderLoop = toggle.IsToggled; 25 }; 26 button.Clicked += (s, a) => view.Display(); 27 28 var stack = new StackLayout 29 { 30 Padding = new Size(20, 20), 31 Children = { view, toggle, button } 32 }; 33 34 Content = stack; 35 } 36 37 private static readonly int SizeOfFloat = Marshal.SizeOf(typeof(float)); 38 39 private bool glIsInited; 40 private float[] lineData; 41 private int lineVAO; 42 private int lineVBO; 43 private int lineShader; 44 45 private void InitGL() 46 { 47 // バッファクリアの際の色を指定 48 GL.ClearColor(0.0f, 0.5f, 1.0f, 1.0f); 49 50 // バーテックスシェーダーを作成 51 // 内容は入力された頂点座標と頂点色をフラグメントシェーダーへ送るだけ 52 var lineVertShader = GL.CreateShader(ShaderType.VertexShader); 53 GL.ShaderSource(lineVertShader, 54@"#version 100 55 56attribute vec2 inPosition; 57attribute vec3 inColor; 58 59varying lowp vec3 v2fColor; 60 61void main() 62{ 63 v2fColor = inColor; 64 gl_Position = vec4(inPosition, 0.0, 1.0); 65} 66"); 67 GL.CompileShader(lineVertShader); 68 69 { 70 // 念のためバーテックスシェーダーがちゃんと作成できたかチェック 71 GL.GetShader(lineVertShader, ShaderParameter.CompileStatus, out var result); 72 if (result == (int)All.False) 73 { 74 Console.WriteLine(GL.GetShaderInfoLog(lineVertShader)); 75 throw new Exception("Error! Can't compile vertex shader."); 76 } 77 } 78 79 // フラグメントシェーダーを作成 80 // 内容は送られてきた色(自動的に頂点間で線形補間されている)をそのフラグメントの色とするだけ 81 var lineFragShader = GL.CreateShader(ShaderType.FragmentShader); 82 GL.ShaderSource(lineFragShader, 83@"#version 100 84 85varying lowp vec3 v2fColor; 86 87void main() 88{ 89 gl_FragColor = vec4(v2fColor, 1.0); 90} 91"); 92 GL.CompileShader(lineFragShader); 93 94 { 95 // 念のためフラグメントシェーダーがちゃんと作成できたかチェック 96 GL.GetShader(lineFragShader, ShaderParameter.CompileStatus, out var result); 97 if (result == (int)All.False) 98 { 99 Console.WriteLine(GL.GetShaderInfoLog(lineFragShader)); 100 throw new Exception("Error! Can't compile fragment shader."); 101 } 102 } 103 104 // バーテックスシェーダー、フラグメントシェーダーをまとめたシェーダープログラムを作る 105 this.lineShader = GL.CreateProgram(); 106 GL.AttachShader(this.lineShader, lineVertShader); 107 GL.AttachShader(this.lineShader, lineFragShader); 108 GL.LinkProgram(this.lineShader); 109 GL.DeleteShader(lineVertShader); 110 GL.DeleteShader(lineFragShader); 111 112 { 113 // 念のためシェーダープログラムがちゃんと作成できたかチェック 114 GL.ValidateProgram(this.lineShader); 115 GL.GetProgram(this.lineShader, ProgramParameter.ValidateStatus, out var succeeded); 116 if (succeeded == (int)All.False) 117 { 118 Console.WriteLine("\t{0}", GL.GetProgramInfoLog(this.lineShader)); 119 throw new Exception(string.Format("Program {0} is invalid.", this.lineShader)); 120 } 121 } 122 123 // 線のデータ 124 // とりあえず位置として2要素、色として3要素のデータを128頂点分格納できるようにした 125 this.lineData = new float[128 * (2 + 3)]; 126 127 // 頂点配列オブジェクトを作成 128 // このオブジェクトがこのあと設定する頂点属性云々といった情報を保管してくれる 129 GL.GenVertexArrays(1, out this.lineVAO); 130 GL.BindVertexArray(this.lineVAO); 131 132 // 頂点バッファオブジェクトを作成 133 // この中にlineDataの中身を適宜転送してやり、描画時OpenGLはこのバッファオブジェクトから位置やら色やらの情報を取っていく 134 GL.GenBuffers(1, out this.lineVBO); 135 // グラフィックスメモリ上に領域を確保 136 // lineDataの内容を転送しているが、この時点では中身は空 137 GL.BindBuffer(BufferTarget.ArrayBuffer, this.lineVBO); 138 GL.BufferData<float>(BufferTarget.ArrayBuffer, new IntPtr(SizeOfFloat * this.lineData.Length), this.lineData, BufferUsage.DynamicDraw); 139 140 { 141 // 位置座標属性 142 // 要素数2個のfloat、次の頂点までのストライドはsizeOfFloat * (2 + 3)バイト、オフセットは頂点属性の頭から0バイト 143 var attribLoc = GL.GetAttribLocation(this.lineShader, "inPosition"); 144 GL.EnableVertexAttribArray(attribLoc); 145 GL.VertexAttribPointer(attribLoc, 2, VertexAttribPointerType.Float, false, SizeOfFloat * (2 + 3), 0); 146 } 147 148 { 149 // 色属性 150 // 要素数3個のfloat、次の頂点までのストライドは同じくsizeOfFloat * (2 + 3)バイト、オフセットは頂点属性の頭からsizeOfFloat * 2バイト 151 var attribLoc = GL.GetAttribLocation(this.lineShader, "inColor"); 152 GL.EnableVertexAttribArray(attribLoc); 153 GL.VertexAttribPointer(attribLoc, 3, VertexAttribPointerType.Float, false, SizeOfFloat * (2 + 3), SizeOfFloat * 2); 154 } 155 156 GL.BindVertexArray(0); 157 158 this.glIsInited = true; 159 } 160 161 private void OnDisplay(Rectangle r) 162 { 163 if (!this.glIsInited) 164 { 165 this.InitGL(); 166 } 167 168 GL.Clear(ClearBufferMask.ColorBufferBit); 169 170 // どのシェーダープログラムを使うか、どの頂点配列を使うかといったことをこれらメソッドで指示する 171 // 今回のケースでは必ずしも毎フレーム行う必要はないですが、とりあえず書いておきました 172 GL.UseProgram(this.lineShader); 173 GL.BindVertexArray(this.lineVAO); 174 175 // lineDataの内容を更新する 176 // 形の変わらない3Dモデルなどでは毎フレームこんなことをする必要はないのですが... 177 var vertexCount = 0; 178 { 179 var a = this.lineData; 180 181 { 182 var o = vertexCount * (2 + 3); 183 // 頂点0の位置 184 a[o + 0] = -0.9f; 185 a[o + 1] = -0.9f; 186 // 頂点0の色 187 a[o + 2] = 1.0f; 188 a[o + 3] = 0.0f; 189 a[o + 4] = 0.0f; 190 vertexCount++; 191 } 192 193 { 194 var o = vertexCount * (2 + 3); 195 // 頂点1の位置 196 a[o + 0] = 0.9f; 197 a[o + 1] = 0.6f; 198 // 頂点1の色 199 a[o + 2] = 0.0f; 200 a[o + 3] = 0.0f; 201 a[o + 4] = 1.0f; 202 vertexCount++; 203 } 204 205 { 206 var o = vertexCount * (2 + 3); 207 // 頂点2の位置 208 a[o + 0] = 0.2f; 209 a[o + 1] = -0.5f; 210 // 頂点2の色 211 a[o + 2] = 0.0f; 212 a[o + 3] = 1.0f; 213 a[o + 4] = 0.0f; 214 vertexCount++; 215 } 216 217 { 218 var o = vertexCount * (2 + 3); 219 // 頂点3の位置 220 a[o + 0] = -0.2f; 221 a[o + 1] = 0.4f; 222 // 頂点3の色 223 a[o + 2] = 1.0f; 224 a[o + 3] = 1.0f; 225 a[o + 4] = 1.0f; 226 vertexCount++; 227 } 228 } 229 230 // グラフィックスメモリ上の頂点バッファオブジェクトに、更新したデータを転送する 231 GL.BindBuffer(BufferTarget.ArrayBuffer, this.lineVBO); 232 GL.BufferSubData<float>(BufferTarget.ArrayBuffer, IntPtr.Zero, new IntPtr(SizeOfFloat * (2 + 3) * vertexCount), this.lineData); 233 234 // 現在バインドされている頂点バッファから、頂点0番からvertexCount個分の頂点データを取ってきて描画する 235 // BeginModeを変えると点や三角形として描くこともできる 236 GL.DrawArrays(BeginMode.LineStrip, 0, vertexCount); 237 } 238 } 239} 240

やたら長くなって申し訳ないですが、肝になるのはInitGLOnDisplayでやっている部分です。
大まかな流れは、まず準備として

  • 描画用のシェーダーを作る
  • 描画したい物体の頂点データを詰めた配列を作る

を行っておき、あとは毎フレーム

  • 頂点データを書き換えたい場合は適宜書き換え、新しいデータを転送する
  • 描画コマンド(GL.DrawArraysなど)を使って物体を描く

といった感じです。上記コードはあくまで一例ですので、参考サイトによっては頂点データの作り方やその他細部に違いがあるでしょうが、大体のところは変わらないかと思います。

ちなみに実行時の様子はこうなりました。

スクリーンショット

投稿2017/11/12 07:59

Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

crew8573

2017/11/19 09:25

返信が遅くなってしまいすみません。 実装したら動かすことができました。 以前のように直感的なプログラムで書けると楽なんですけどね。 教えていただいた使い方で実装を進めてみます! ありがとうございました。
Bongo

2017/11/19 09:58

ご参考になりましたようで幸いです。 ある程度慣れてきたら、ややこしい部分を隠蔽した独自メソッドを作ってみてもいいかもしれませんね。 たとえば ・glColorもどき...引数の色をインスタンス変数に保管する ・glBeginもどき...引数のBeginModeをインスタンス変数に保管、およびvertexCountを0にリセットする ・glVertexもどき...引数の座標とインスタンス変数に保管しておいた色を使って、lineDataに1頂点分のデータを書き込んでvertexCountを1増やす ・glEndもどき...UseProgram、BindVertexArray、BindBufferを行い、lineDataをlineVBOに転送したのち、インスタンス変数に保管しておいたBeginModeとvertexCountを使ってDrawArraysを行う といった感じで作れそうです。 あるいは逆にもっとオブジェクト指向っぽくなるように、シェーダークラス、テクスチャクラス、形状データクラス...といったものを作ってみるのも面白いと思います。Disposeパターンと相性がよさそうですね。コンストラクタでGen系メソッドを使って必要なオブジェクトを生成し、DisposeでDelete系メソッドを使って作ったオブジェクトを削除する...といったところでしょうか。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問