少し変わった変換が必要な 3D モデリングを行っています (基本的に、回転の代わりに傾斜/スキュー変換を行う必要があります)。これまで OpenGL プログラミングを行ったことがないので、2D キャンバス描画ライブラリを使用し、概念実証のために独自の 3D -> 2D 変換マトリックスを作成しました。ワイヤーフレーム モデルを使用するとうまく機能します。
3D に変換するために、SDK サンプルの OpenGLS20Complete プログラムを使用して、正方形と三角形を描画しました。これを変更して draw メソッドをインターフェイス (IDrawable) として抽象化し、IDrawable のリストを処理できるようにしました。テストのために、正方形のリストを作成して 6 つの面を持つ立方体を作成しました。
上面が底面よりも大きいため、立方体が明らかに遠近法で描かれていることを除いて、すべて正常に機能します。OrthoM は frustumM に異なる行列を返します (行列の値は異なります) が、私が知る限り、同じことを行っています。このアプリケーションには正射投影法が必要です (つまり、透視図がありません)。私はそれを働かせることができません。
次のように、テスト用に単一のキューブを表示するためにコードを削除しました。
OpenGLS20Complete onSurfaceCreated のコードは次のとおりです。
@Override
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
// Set the background frame color
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
transformedshapes = new ArrayList<IDrawable>();
cubeSide = 0.3f;
IDrawable cube1 = new Cube(-0.4f, cubeSide, -0.4f, cubeSide, 0f, cubeSide, Color.GRAY, Color.RED, Color.YELLOW, Color.BLUE, Color.MAGENTA, Color.CYAN);
transformedshapes.add(cube1);
// next line added by me
GLES20.glEnable( GLES20.GL_DEPTH_TEST );
}
Cube コンストラクター Cube(left、cubewidth、right、cubelength、basez、cubeheight、... 6 色) は、6 つの面 (IDrawable オブジェクトとして) を作成するだけで、リストされた 6 つの色のそれぞれの 1 つです。それが想定されていることを行います。
OnSurfaceChanged コードは次のとおりです。
@Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
// Adjust the viewport based on geometry changes,
// such as screen rotation
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / (float) height;
// Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7); // original
Matrix.orthoM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}
画面上の結果は、.frustumM または .orthoM のどちらの方法を使用しても同じです。frustumM メソッドの値を変更すると、期待どおりに表示が変わります。デバッガーを使用すると、frustumM メソッドと orthoM メソッドが異なる行列を準備することがわかります。
OnDraw メソッドは次のようになります。
@Override
public void onDrawFrame(GL10 unused) {
// Draw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// Set the camera position (View matrix)
Matrix.setLookAtM(mVMatrix, 0, 0, 0, 3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
// Draw untransformed shapes
for (IDrawable shape:untransformedshapes)
shape.draw(mMVPMatrix);
float [] rotatedmMVPMatrix = new float[16];
float netRotationAboutX = mAngleRotateAboutX - mAngleStartRotateAboutX;
float netRotationAboutY = mAngleRotateAboutY - mAngleStartRotateAboutY;
float netRotationAboutZ = mAngleRotateAboutZ - mAngleStartRotateAboutZ;
Matrix.setRotateM(mRotationMatrix, 0, netRotationAboutY, 0, -1f, 0f);
float[] xRotationMatrix = new float[16];
Matrix.setRotateM(xRotationMatrix, 0, netRotationAboutX, -1f, 0, 0f);
Matrix.multiplyMM(mRotationMatrix, 0, xRotationMatrix, 0,
mRotationMatrix, 0);
// Combine the rotation matrix with the projection and camera view
Matrix.multiplyMM(rotatedmMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0);
for (IDrawable shape : transformedshapes)
shape.draw(rotatedmMVPMatrix);
}
上記は、デバッグを容易にするために、傾斜行列ではなく回転を実行します。mAngleRotateAboutX と mAngleRotateAboutY を変更すると、1 つまたは複数の立方体が回転するという点で機能します。
メソッド .setLookAtM() は、正投影に完全に適しているとは思えません。パースペクティブが必要ないため、z 距離 (この場合は 3) は不要に思えます。正投影では、方向 (つまり、2 つの独立変数) のみが必要であり、ビューの位置 (3 つの独立変数) は必要ありません。多分私はこれを使うべきではありませんか?
キューブの一部である正方形は、IDrawable インターフェイスの draw メソッドを実装する PWRectShape クラスのインスタンスです。これは、正方形の描画方法 OpenGLS20Complete のモンキー モンキー ドの変更です。
class PWRectShape implements IDrawable{
private final String vertexShaderCode =
// This matrix member variable provides a hook to manipulate
// the coordinates of the objects that use this vertex shader
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
// the matrix must be included as a modifier of gl_Position
" gl_Position = vPosition * uMVPMatrix;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
private final FloatBuffer vertexBuffer;
private final ShortBuffer drawListBuffer;
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
private final short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
// Set color with default red, green, blue and alpha (opacity) values
float color[] = { 0.2f, 0.709803922f, 0.898039216f, 1.0f };
public PWRectShape(float [] rectCoords, int colour) { // the constructor
// initialize vertex byte buffer for shape coordinates
color[0] = (float)Color.red(colour)/256f;
color[1] = (float)Color.green(colour)/256f;
color[2] = (float)Color.blue(colour)/256f;
color[3] = 1f;
ByteBuffer bb = ByteBuffer.allocateDirect(
// (# of coordinate values * 4 bytes per float)
rectCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(rectCoords);
vertexBuffer.position(0);
// initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(
// (# of coordinate values * 2 bytes per short)
drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
// prepare shaders and OpenGL program
int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mProgram); // create OpenGL program executables
}
public void draw(float[] mvpMatrix) { // the draw method
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// get handle to fragment shader's vColor member
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
MyGLRenderer.checkGlError("glGetUniformLocation");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
MyGLRenderer.checkGlError("glUniformMatrix4fv");
// Draw the square
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
単純なシェーダーなどを設定するための追加コードがあり、すべて OpenGLES20Complete から変更されていません。必要に応じて投稿できます。しかし、これはすべてうまくいくようです。実際、すべて問題なく動作します。複数の場所に複数の立方体を描画したり、カスタム射影行列を使用してそれらを正しく歪めたりすることもできます。唯一うまくいかないのは、立方体の上面が下面よりも大きいことです。.frustumM を使用するか、.orthoM を使用するかに関係なく、明らかに遠近法を使用しています。
このコードを実行すると、正投影ではなく透視図が表示される理由について、何か提案やアイデアはありますか?