0

我正在做一些 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 个面(作为 IDrawable 对象),列出的 6 种颜色中的每一种。做它应该做的。

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 时,立方体或立方体会旋转。

方法 .setLookAtM() 似乎并不完全适合正交投影。由于不需要透视,z 距离(在本例中为 3)似乎没有必要。正交投影应该只需要一个方向(即 2 个自变量)而不是视图位置(3 个自变量)。也许我不应该使用这个?

作为我的立方体一部分的正方形是 PWRectShape 类的实例,它实现了我的 IDrawable 接口的 draw 方法。这是正方形绘制方法OpenGLS20Complete的monkey-see-monkey-do修改

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。

关于为什么在运行此代码时获得透视图而不是正交投影的任何建议或想法?

4

1 回答 1

1

不确定问题两年后的答案有多有用 - 但它可能对其他人有所帮助。

在你的OnDrawFrame()方法中,你有这个:

Matrix.multiplyMM(rotatedmMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0);

而我认为您打算首先应用旋转变换来变换场景中的对象,然后再通过视图和投影变换来变换结果。

这将需要像这样反转 LHS 和 RHS 的顺序:

Matrix.multiplyMM(rotatedmMVPMatrix, 0, mMVPMatrix, 0, mRotationMatrix, 0);

我怀疑你知道,当你将矩阵相乘时,它会产生一个新矩阵,相当于首先将 RHS 应用于输入,然后将 LHS 应用于结果。

于 2015-11-19T22:23:46.523 回答