前の記事では、Android の OpenGL と座標マッピングについて説明しました。OpenGL ES 環境では、投影とカメラビューを使用して、描画オブジェクトを目に見える実物により近い形で表示します。この表示方法は、描画オブジェクトの座標を数学的に変換することで行われます。ここでは、投影とカメラビューに関連する知識について説明します。コードの例の変更は、前の記事を参照してください。
主な内容は次のとおりです。
- 投影の種類
- 投影の定義
- カメラビューの定義
- 投影とカメラビューの適用
- 実行結果
投影の種類#
OpenGL には、主に 2 つの投影モードがあります。透視投影と直交投影です。それぞれの特徴は次のとおりです。
- 透視投影:人の目に合わせて、近くは大きく、遠くは小さく見える効果があります。
- 直交投影:すべてのオブジェクトが投影平面上で元のサイズを保ちます。
透視投影の視錐体は錐体であり、直交投影の視錐体は直方体です。透視投影と直交投影の図は次のとおりです。
それぞれの行列計算関数は次のとおりです。
// 透視投影行列
Matrix.frustumM(float[] m, int offset, float left, float right, float bottom, float top, float near, float far);
// 直交投影行列
Matrix.orthoM(float[] m, int offset, float left, float right, float bottom, float top, float near, float far);
上記の関数のパラメーターである m は、対応する投影行列データを格納するために使用され、near と far は視錐体の近面と遠いスクリーンまでの距離を表します。left、right、top、bottom は遠い面のパラメーターに対応しています。
投影の定義#
前のセクションで説明したように、ここでは透視投影を使用し、投影行列をMatrix.frustumM()
で埋めます。以下のようになります。
private val projectionMatrix = FloatArray(16)
override fun onSurfaceChanged(unused: GL10, width: Int, height: Int) {
GLES20.glViewport(0, 0, width, height)
val ratio: Float = width.toFloat() / height.toFloat()
Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1f, 1f, 3f, 7f)
}
上記のコードでは、投影行列projectionMatrix
が埋められており、その変化は次のアニメーションを参照してください。
カメラビューの定義#
カメラビューは、カメラの位置から物体を見る視点として考えることができます。Matrix.setLookAtM
メソッドを使用してビューマトリックスを埋めます。主なパラメーターはカメラの位置、ターゲットの位置、およびカメラの上方向ベクトルです。その後、投影行列とビューマトリックスをvPMatrix
として結合します。以下のようになります。
override fun onDrawFrame(gl: GL10?) {
// 現在のフレームを描画し、具体的な内容をレンダリング処理する
Log.d(tag, "onDrawFrame")
// カメラの位置(ビューマトリックス)を設定する
Matrix.setLookAtM(viewMatrix,0,
0.0f,0.0f,5.0f, // カメラの位置
0.0f,0.0f,0.0f, // ターゲットの位置
0.0f,1.0f,0.0f) // カメラの上方向ベクトル
// 投影とビューの変換を計算する
Matrix.multiplyMM(vPMatrix,0,projectionMatrix,0,viewMatrix,0)
// 具体的な描画
triangle.draw(vPMatrix)
}
上記の例では、カメラの位置の z 座標は near と far の間でなければならず、つまり 3 と 7 の間でなければなりません。範囲外の観察はできません。以下のアニメーションを参照してください。
投影とカメラビューの適用#
投影とビューの変換を適用するために、前の記事で変更したシェーダーコードは次のようになります。
// デフォルト
attribute vec4 vPosition;
void main() {
gl_Position = vPosition;
}
// 投影とビューの変換を使用
uniform mat4 uMVPMatrix;
attribute vec4 vPosition;
void main() {
gl_Position = uMVPMatrix * vPosition;
}
前のセクションで計算したvPMatrix
行列をシェーダーに渡すだけです。
fun draw(mvpMatrix: FloatArray) {
// attribute変数のハンドルを取得する
// get handle to vertex shader's vPosition member
positionHandle = GLES20.glGetAttribLocation(programHandle, "vPosition").also {
// enable vertex attribute, it is disabled by default
GLES20.glEnableVertexAttribArray(it)
GLES20.glVertexAttribPointer(
it,
COORDINATE_PER_VERTEX,
GLES20.GL_FLOAT,
false,
vertexStride,
vertexBuffer
)
}
// get handle to fragment shader's vColor member
colorHandler = GLES20.glGetUniformLocation(programHandle, "vColor").also {
GLES20.glUniform4fv(it, 1, color, 0)
}
// get handle to shape's transformation matrix
vPMatrixHandle = GLES20.glGetUniformLocation(programHandle, "uMVPMatrix")
// Pass the projection and view transformation to the shader
GLES20.glUniformMatrix4fv(vPMatrixHandle, 1, false, mvpMatrix, 0)
// draw triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount)
GLES20.glDisableVertexAttribArray(positionHandle)
}
これにより、投影とビューの変換を適用するために計算されたvPMatrix
行列がシェーダーに渡されます。
以上のように、コードの改造により投影とカメラビューを適用し、画面の向きを変更すると変形が解消されます。このような変形は、OpenGL でビデオのアスペクト比などをレンダリングする際にも応用できます。
実行結果#
前の記事の実行結果と比較することができます。実行結果は次のとおりです。
キーワード【OpenGL】を返信すると、ソースコードを取得できます。この記事で使用されているアニメーションは、キーワード【OTUTORS】を返信すると取得できます。