立方体を二つ配置して回転させてみます。ライブラリを用いずに OpenGL API を直接利用します。
立方体の頂点バッファ (ローカル座標系)
立方体の頂点情報を CPU で用意して GPU の頂点バッファに送ります。座標は同次座標系で表現すると行列による演算が簡単になります。
頂点配列オブジェクトの作成 glGenVertexArrays
頂点配列オブジェクトのバインド glBindVertexArray
頂点バッファオブジェクトの作成 glGenBuffers
立方体を描く場合、頂点バッファを節約するために以下のように二つ用意するとよいです。同じ GL_ARRAY_BUFFER
は複数回 GL_ELEMENT_ARRAY_BUFFER
で参照されます。
- 8 つの頂点座標を格納する頂点バッファ
GL_ARRAY_BUFFER
- 8 つの頂点座標の組み合わせを格納する頂点バッファ
GL_ELEMENT_ARRAY_BUFFER
作成時は区別されません。
頂点バッファオブジェクトのバインド glBindBuffer
GL_ARRAY_BUFFER
としてバインドします。
頂点バッファオブジェクトにデータを設定 glBufferData
GL_ARRAY_BUFFER
のデータを設定します。
頂点バッファオブジェクトと頂点属性の対応関係を設定 glVertexAttribPointer
GL_ARRAY_BUFFER
のデータをバーテックスシェーダの in
変数で参照できるようにします。
glVertexAttribPointer
の引数について補足
0
position 頂点属性を指定しています。4
glBufferData
で設定した座標データは4次元です。GL_FLOAT
座標データの型です。0
glBufferData
で格納したデータに複数の頂点属性用のデータが入っている場合は変更します。0
glBufferData
で格納したデータに複数の頂点属性用のデータが入っている場合は変更します。
頂点属性を有効化 glEnableVertexAttribArray
インデックスとなる頂点バッファオブジェクトのバインドおよびデータ設定
GL_ELEMENT_ARRAY_BUFFER
としてバインド
GL_ELEMENT_ARRAY_BUFFER
のデータ設定
補足
後述のとおり、描画時は glDrawArrays ではなく glDrawElements を用います。
モデル変換 (ローカル座標系 → ワールド座標系)
STL ファイル等で表現されたオブジェクトのメッシュの各頂点は、オブジェクト内のローカル座標系における座標を持ちます。複数のオブジェクトを同じ環境に配置する場合、配置される環境のワールド座標系における、オブジェクトのメッシュの頂点の座標が必要になります。ローカル座標系からワールド座標系への変換をモデル変換とよびます。環境のことをシーンともよびます。
同次変換行列を立方体二つについてそれぞれ以下のように設定することにします。一つ目はローカル座標系の座標をそのままワールド座標系でも用います。二つ目はローカル座標系の座標を x 軸まわりに 45 度回転して更に x 軸方向に 1.0 だけ平行移動します。これらの行列は OpenGL のバーテックスシェーダに uniform 変数で設定します。
ビュー変換 (ワールド座標系 → 視点座標系)
OpenGL で二次元の画面に表示するにあたり、シーンにおけるカメラの視点が必要になります。カメラから見たときの視点座標系への変換をビュー変換とよびます。モデル変換とまとめてモデルビュー変換ともよびます。
ワールド座標系の座標を z 軸まわりに 45 度回転させて更に z 軸方向に -1 だけ平行移動したものを視点座標系とすると以下のような同次変換行列になります。OpenGL のバーテックスシェーダに uniform 変数で設定します。
投影変換 (視点座標系 → 正規化デバイス座標系)
視点座標系において最終的に画面に表示したい空間を切り出して、x,y,z
が [-1,1]
の立方体内に収まるように新たな正規化デバイス座標系 (Normalized Device Coordinate; NDC) へ変換します。正規化デバイス座標系はクリッピング座標系ともよびます。
- 視点座標系において切り出す空間を視体積 (View Volume) とよびます。
- 正規化デバイス座標系における
[-1,1]
の立方体を標準視体積 (クリッピング領域、クリッピング空間) とよびます。
クリッピング領域は立方体になっており z 方向の深度があります。クリッピング領域は次のステージで xy 平面に投影されます。投影される対象となるクリッピング領域内に収めるために投影変換を行います。投影変換には複数の種類があります。
- 直行投影 (Orthogonal Projection) → 視体積が直方体、遠くも近くも同じ大きさになるように変換します。
- 透視投影 (Perspective Projection) → 視体積が四角錐台、遠くのものが小さくなるように変換します。
直行投影と透視投影の変換行列は最終的に以下のようになります。OpenGL のバーテックスシェーダに uniform 変数で設定して利用します。
n
視点座標系における視体積の前方面について、z 軸方向の原点からの距離 (near)f
視点座標系における視体積の後方面について、z 軸方向の原点からの距離 (far)r
,l
,t
,b
視点座標系における視錐台の前方面について、辺の xy 座標 (right、left、top、bottom)
直行投影
特に $r+l=0$
、$t+b=0$
、$r=t$
となる、前方面と後方面が正方形で中心が z 軸上の場合を考えると以下のようになります。
透視投影
透視投影における視体積を特に視錐台 (View Frustum) とよびます。
特に $r+l=0$
、$t+b=0$
、$r=t$
となる、前方面と後方面が正方形で中心が z 軸上の場合を考えると以下のようになります。
ビューポート変換 (正規化デバイス座標系 → デバイス座標系) glViewport
ビューポート変換では正規化デバイス座標系のクリッピング空間を切り出して xy 平面のビューポートに投影します。ビューポートのサイズは glViewport で指定します。xy 平面内における座標系をデバイス座標系とよびます。
glViewport
の引数について
0
,0
描画するカラーバッファについて、描画範囲となる矩形の左下の座標です。100
,100
描画するカラーバッファについて、描画範囲となる矩形の幅と高さです。
第三引数と第四引数の値が異なる場合はビューポート変換によってアスペクト比が変化します。投影変換時に画面のアスペクト比を考慮しておくと、ビューポート変換でデバイスの画面に描画されたときにも縦横の比が保たれます。
描画
シェーダオブジェクト内にソースコードを設定 glShaderSource
glShaderSource
でシェーダオブジェクトにソースコードを設定します。
ソースコードのコンパイル glCompileShader
コンパイルが成功したかどうかの確認
プログラムオブジェクトへのシェーダオブジェクトのアタッチ glAttachShader
シェーダオブジェクトの削除フラグを設定 glDeleteShader
プログラムオブジェクトの変数設定およびリンク glBindAttribLocation、glBindFragDataLocation、glLinkProgram
プログラムをインストール glUseProgram
uniform 変数の location を取得 glGetUniformLocation
uniform 変数の設定および描画 glUniform、glDrawElements
ascontiguousarray
で配列の領域がメモリに連続して確保されることを保証してから glUniform
に配列のポインタを渡します。OpenGL の仕様上、行と列を転置したものを渡す必要があることにも注意します。
描画時は glDrawArrays ではなく glDrawElements を用います。glDrawElements
の第二引数 24 は GL_ELEMENT_ARRAY_BUFFER
頂点バッファの要素数です。
ビュー変換、投影変換
モデル変換、描画
立方体一つ目
立方体二つ目
描画結果の確認
直行投影
y軸が下を向いているため 45 度の回転が右回りになっています。
透視投影
後方の面が前方の面よりも小さく描画されていることが分かります。
隠面消去 glEnable
ビューポート変換時に二次元平面に投影されるにあたり、描画される順番によっては後方の部分が前方の描画結果を上書きしてしまうことがあります。これを回避するために隠面消去が必要になります。隠面消去の処理の一つにデプスバッファ法 (Zバッファ法) があります。その他の処理法に背面カリングなどがあります。
デプスバッファ法は OpenGL に組込まれており既定では無効になっています。有効化すると新たに深度情報を格納するデプスバッファが用意され、描画したポリゴンの深度情報が格納されていきます。
テクスチャの適用 (参考)
glEnable(GL_TEXTURE_2D)
で有効化してテクスチャを利用できます。
- テクスチャオブジェクトの生成 glGenTextures
- テクスチャオブジェクトのバインド glBindTexture
GL_MAX_TEXTURE_SIZE
を越えていないことを glGetIntegerv で確認- テクスチャ画像の設定 glTexImage2D
- テクスチャパラメータの設定 glTexParameter
- フラグメントシェーダで
texture()
に UV 座標を設定 - サンプラについて glGenSamplers
記事の執筆者にステッカーを贈る
有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。
さらに詳しく →Feedbacks
ログインするとコメントを投稿できます。
関連記事
- Python コードスニペット (条件分岐)if-elif-else sample.py #!/usr/bin/python # -*- coding: utf-8 -*- # コメント内であっても、ASCII外の文字が含まれる場合はエンコーディング情報が必須 x = 1 # 一行スタイル if x==0: print 'a' # 参考: and,or,notが使用可能 (&&,||はエラー) elif x==1: p...
- Python コードスニペット (リスト、タプル、ディクショナリ)リスト range 「0から10まで」といった範囲をリスト形式で生成します。 sample.py print range(10) # for(int i=0; i<10; ++i) ← C言語などのfor文と比較 print range(5,10) # for(int i=5; i<10; ++i) print range(5,10,2) # for(int i=5; i<10;...
- ZeroMQ (zmq) の Python サンプルコードZeroMQ を Python から利用する場合のサンプルコードを記載します。 Fixing the World To fix the world, we needed to do two things. One, to solve the general problem of "how to connect any code to any code, anywhere". Two, to wra...
- Matplotlib/SciPy/pandas/NumPy サンプルコードPython で数学的なことを試すときに利用される Matplotlib/SciPy/pandas/NumPy についてサンプルコードを記載します。 Matplotlib SciPy pandas [NumPy](https://www.numpy
- pytest の基本的な使い方pytest の基本的な使い方を記載します。 適宜参照するための公式ドキュメントページ Full pytest documentation API Reference インストール 適当なパッケージ