简体   繁体   English

Android OpenGL ES 2.0:Cube模型不仅失真(透视是错误的?),而且面部加载不正确(顶点不正确?)

[英]Android OpenGL ES 2.0: Cube model is not only distorted (perspective is wrong?), but also the faces are loaded incorrectly (vertices not correct?)

I have come across some problems I'm not able to explain very well without you guys trying it out. 我遇到了一些问题,如果没有你们尝试的话,我无法解释。

I'm unable to get a cube to load correctly. 我无法正确加载多维数据集。 I was able to make it rotate nicely on all axes, though. 不过,我能够让它在所有轴上旋转得很好。 (plural of "axis" is "axes"?) (“轴”的复数是“轴”?)

I haven't ventured on with lighting and textures, so I'm sorry if you can't seem to make out the model yet. 我还没有尝试过照明和纹理,所以如果你似乎还没能看出模型,我很抱歉。

This is what it looks like right now (snapshot of a freely-spinning model): 这就是它现在的样子(自由旋转模型的快照):

多维数据集的快照。尚未显示深度。

This is the expected outcome: 这是预期的结果:

搅拌机型号。期望太高了,先生。

This is the code for my GLSurfaceView.Renderer : 这是我的GLSurfaceView.Renderer的代码:

package dd.ww;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView.Renderer;
import android.opengl.Matrix;

public class Render implements Renderer {

    private Context context;
    private Cube cube;

    private float[] modelViewProjectionMatrix = new float[16];
    private float[] projectionMatrix = new float[16];
    private float[] viewMatrix = new float[16];
    private float[] rotationMatrix = new float[16];
    private float angle = 0f;

    public Render(Context context) {
        this.context = context;
    }

    @Override
    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        GLES20.glClearColor(1f, 1f, 1f, 1f);
        cube = new Cube(context);
    }

    @Override
    public void onSurfaceChanged(GL10 unused, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
        float ratio = (float) width / (float) height;
        Matrix.frustumM(projectionMatrix, 0, -3f, 3f, -3f, 3f, 1f, 10f);
    }

    @Override
    public void onDrawFrame(GL10 unused) {
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
        //Camera position
        Matrix.setLookAtM(viewMatrix, 0, 0f, 0f, -4f, 0f, 0f, 0f, 0f, 1f, 0f);
        // projection x view = modelView
        Matrix.multiplyMM(modelViewProjectionMatrix, 0, projectionMatrix, 0, viewMatrix, 0);
        //Creating rotation matrix
        Matrix.setRotateM(rotationMatrix, 0, angle, 0f, 0f, -1f);
        //rotation x camera = modelView
        Matrix.multiplyMM(modelViewProjectionMatrix, 0, rotationMatrix, 0, modelViewProjectionMatrix, 0);
        Matrix.setRotateM(rotationMatrix, 0, angle, 0f, -1f, 0f);
        Matrix.multiplyMM(modelViewProjectionMatrix, 0, rotationMatrix, 0, modelViewProjectionMatrix, 0);
        Matrix.setRotateM(rotationMatrix, 0, angle, -1f, 0f, 0f);
        Matrix.multiplyMM(modelViewProjectionMatrix, 0, rotationMatrix, 0, modelViewProjectionMatrix, 0);

        cube.draw(modelViewProjectionMatrix);

        angle += 0.7f;
        if (angle > 360f)
            angle = 0f;
    }

}

This is the code for the Cube class, along with its OBJ loader. 这是Cube类的代码及其OBJ加载器。 The OBJ Loader is used for loading the OBJ model that was exported from Blender (which is the expected outcome of the Cube, shown in Blender.): OBJ Loader用于加载从Blender导出的OBJ模型(这是Cube的预期结果,如Blender所示):

package dd.ww;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import android.content.Context;
import android.content.res.AssetManager;
import android.opengl.GLES20;
import android.util.Log;

public class Cube {
    private Context context;
    private FloatBuffer vertexBuffer;
    private ShortBuffer indexBuffer;

    private int shaderProgram;

    //TODO: Go to Google Code, find OpenGL ES 2.0 Programming Guide source code, Android,
    //check in the ESShapes.java, and study the FloatBuffers...

    public Cube(Context c) {
        context = c;
        loadCube("cube/cube.obj");
    }

    private void loadCube(String filename) {

        ArrayList<Float> tempVertices = new ArrayList<Float>();
        //ArrayList<Float> tempNormals = new ArrayList<Float>();
        ArrayList<Short> vertexIndices = new ArrayList<Short>();
        //ArrayList<Short> normalIndices = new ArrayList<Short>();

        try {
            AssetManager manager = context.getAssets();
            BufferedReader reader = new BufferedReader(new InputStreamReader(manager.open(filename)));
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.startsWith("v")) {
                    tempVertices.add(Float.valueOf(line.split(" ")[1])); //vx
                    tempVertices.add(Float.valueOf(line.split(" ")[2])); //vy
                    tempVertices.add(Float.valueOf(line.split(" ")[3])); //vz
                }
                //              else if (line.startsWith("vn")) {
                //                  tempNormals.add(Float.valueOf(line.split(" ")[1])); //nx
                //                  tempNormals.add(Float.valueOf(line.split(" ")[2])); //ny
                //                  tempNormals.add(Float.valueOf(line.split(" ")[3])); //nz
                //              }
                else if (line.startsWith("f")) {

                    /*
                    vertexIndices.add(Short.valueOf(tokens[1].split("/")[0])); //first point of a face
                    vertexIndices.add(Short.valueOf(tokens[2].split("/")[0])); //second point
                    vertexIndices.add(Short.valueOf(tokens[3].split("/")[0])); //third point
                    normalIndices.add(Short.valueOf(tokens[1].split("/")[2])); //first normal
                    normalIndices.add(Short.valueOf(tokens[2].split("/")[2])); //second normal
                    normalIndices.add(Short.valueOf(tokens[3].split("/")[2])); //third
                     */
                    //                  for (int i = 1; i <= 3; i++) {
                    //                      //String[] s = tokens[i].split("/");
                    //                      vertexIndices.add(Short.valueOf());
                    //                      //normalIndices.add(Short.valueOf(s[2]));
                    //                  }

                    vertexIndices.add(Short.valueOf(line.split(" ")[1]));
                    vertexIndices.add(Short.valueOf(line.split(" ")[2]));
                    vertexIndices.add(Short.valueOf(line.split(" ")[3]));
                }
            }

            float[] vertices = new float[tempVertices.size()];
            for (int i = 0; i < tempVertices.size(); i++) {
                Float f = tempVertices.get(i);
                vertices[i] = (f != null ? f : Float.NaN);
            }

            short[] indices = new short[vertexIndices.size()];
            for (int i = 0; i < vertexIndices.size(); i++) {
                Short s = vertexIndices.get(i);
                indices[i] = (s != null ? s : 1);
            }

            vertexBuffer = ByteBuffer.allocateDirect(vertices.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
            vertexBuffer.put(vertices).position(0);

            indexBuffer = ByteBuffer.allocateDirect(indices.length * 2).order(ByteOrder.nativeOrder()).asShortBuffer();
            indexBuffer.put(indices).position(0);


            int vertexShader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
            GLES20.glShaderSource(vertexShader, vertexCode);
            GLES20.glCompileShader(vertexShader);
            int fragmentShader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
            GLES20.glShaderSource(fragmentShader, fragmentCode);
            GLES20.glCompileShader(fragmentShader);

            shaderProgram = GLES20.glCreateProgram();
            GLES20.glAttachShader(shaderProgram, vertexShader);
            GLES20.glAttachShader(shaderProgram, fragmentShader);
            GLES20.glLinkProgram(shaderProgram);

            int[] linked = new int[1];
            GLES20.glGetProgramiv(shaderProgram, GLES20.GL_LINK_STATUS, linked, 0);
            if (linked[0] == 0){
                Log.d("DEBUG", "Shader code error.");
                Log.d("DEBUG", GLES20.glGetProgramInfoLog(shaderProgram));
                GLES20.glDeleteProgram(shaderProgram);
                return;
            }

            GLES20.glDeleteShader(vertexShader);
            GLES20.glDeleteShader(fragmentShader);




        }
        catch (Exception e) {
            Log.d("DEBUG", "Error.", e);
        }
    }

    private String vertexCode = "" +
            "attribute vec4 a_position;  \n" +
            "uniform mat4 mvpMatrix;     \n" +
            "void main() {               \n" +
            "   gl_Position = a_position * mvpMatrix;\n" +
            "}                           \n";

    private String fragmentCode = "" +
            "precision mediump float;                   \n" +
            "void main() {                              \n" +
            "   gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n" +
            "}                                          \n";

    private int attribute_Position;
    private int uniform_mvpMatrix;

    public void draw(float[] mvpMatrix){
        GLES20.glUseProgram(shaderProgram);
        attribute_Position = GLES20.glGetAttribLocation(shaderProgram, "a_position");
        GLES20.glVertexAttribPointer(attribute_Position, 3, GLES20.GL_FLOAT, false, 3 * 4, vertexBuffer);
        GLES20.glEnableVertexAttribArray(attribute_Position);
        uniform_mvpMatrix = GLES20.glGetUniformLocation(shaderProgram, "mvpMatrix");
        GLES20.glUniformMatrix4fv(uniform_mvpMatrix, 1, false, mvpMatrix, 0);
        GLES20.glDrawElements(GLES20.GL_TRIANGLES, indexBuffer.capacity(), GLES20.GL_UNSIGNED_SHORT, indexBuffer);
        GLES20.glDisableVertexAttribArray(attribute_Position);
    }
}

And finally, here's the APK attachment (Uploaded to Mediafire, should not get removed. It's non-licensed freeware). 最后,这是APK附件(上传到Mediafire,不应该删除。它是非许可的免费软件)。 The attached APK file is signed, exported straight from my project, and can only be run on Gingerbread or above. 附加的APK文件已签名,直接从我的项目导出,并且只能在Gingerbread或更高版本上运行。 (This is what OpenGL ES 2.0 is for...): Mediafire download link to the APK file. (这就是OpenGL ES 2.0的用途......): Mediafire下载链接到APK文件。

If anyone is willing to help me realize what I'm doing wrong, I'll be glad for the rest of my life. 如果有人愿意帮助我意识到我做错了什么,我将为我的余生感到高兴。 This question here is the closest that I find when searching on SO that has a 40% chance my problem is related to. 这里的这个问题是我在SO上搜索时最接近的问题,我的问题与我的问题有40%的可能性。 Unfortunately, he still has his model distorted. 不幸的是,他的模型仍然被扭曲了。 All the rest of the questions I found seems to be about textures not rendering correctly, translating the model around, etc. But I will try my best to find questions with similar problems as mine. 我发现的所有其他问题似乎都是关于纹理无法正确渲染,翻译模型等等。但我会尽我所能找到与我类似问题的问题。

Holy Cow... 天啊...

I finally got it to work. 我终于开始工作了。

快照1

快照2

The problem is how OpenGL ES 2.0 matrices work. 问题是OpenGL ES 2.0矩阵是如何工作的。

Quote from SO user, Tim: Tim来自SO用户:

I believe it should be mvpMatrix * mRotationMatrix, but you're not supposed to use the same matrix as the input and output to that function, you need to use a temporary matrix. 我相信它应该是mvpMatrix * mRotationMatrix,但你不应该使用与该函数的输入和输出相同的矩阵,你需要使用临时矩阵。 Android.opengl.Matrix " The same float array may be passed for result, lhs, and/or rhs. However, the result element values are undefined if the result elements overlap either the lhs or rhs elements." Android.opengl.Matrix“可以为result,lhs和/或rhs传递相同的float数组。但是, 如果结果元素与lhs或rhs元素重叠则结果元素值是未定义的。” If that doesn't help then post your entire code. 如果这没有帮助,那么发布您的整个代码。

The bolded text means that if you do this: 粗体文字表示如果你这样做:

//Creating rotation matrix
Matrix.setRotateM(rotationMatrix, 0, angle, 0f, 0f, -1f);

//rotation x camera = modelView    
Matrix.multiplyMM(modelViewProjectionMatrix, 0, modelViewProjectionMatrix, 0, rotationMatrix, 0);

Matrix.setRotateM(rotationMatrix, 0, angle, 0f, -1f, 0f);
Matrix.multiplyMM(modelViewProjectionMatrix, 0, modelViewProjectionMatrix, 0, rotationMatrix, 0);

Matrix.setRotateM(rotationMatrix, 0, angle, -1f, 0f, 0f);
Matrix.multiplyMM(modelViewProjectionMatrix, 0, modelViewProjectionMatrix, 0, rotationMatrix, 0);

cube.draw(modelViewProjectionMatrix);

Which seems like normal, the cube will look skewed. 这看起来很正常,立方体看起来会偏斜。 Reason is, lhs and rhs shouldn't be the same as your resulting matrix. 原因是,lhs和rhs不应与结果矩阵相同。

But, if you do this: 但是,如果你这样做:

//Creating rotation matrix
Matrix.setRotateM(rotationMatrix, 0, angle, 0f, 0f, -1f);

//rotation x camera = modelView
float[] duplicateMatrix = Arrays.copyOf(modelViewProjectionMatrix, 16);

Matrix.multiplyMM(modelViewProjectionMatrix, 0, duplicateMatrix, 0, rotationMatrix, 0);

Matrix.setRotateM(rotationMatrix, 0, angle, 0f, -1f, 0f);
duplicateMatrix = Arrays.copyOf(modelViewProjectionMatrix, 16);
Matrix.multiplyMM(modelViewProjectionMatrix, 0, duplicateMatrix, 0, rotationMatrix, 0);

Matrix.setRotateM(rotationMatrix, 0, angle, -1f, 0f, 0f);
duplicateMatrix = Arrays.copyOf(modelViewProjectionMatrix, 16);
Matrix.multiplyMM(modelViewProjectionMatrix, 0, duplicateMatrix, 0, rotationMatrix, 0);

cube.draw(modelViewProjectionMatrix);

It will display correctly. 它会正确显示。

Where I found the helpful hint from. 我从哪里找到了有用的提示。

And this is where I realized why 3 people voted to close this question off. 这就是我意识到为什么3个人投票决定关闭这个问题的地方。 They must've known the answer to this question in the first place, and wanted me to find the solution by myself. 他们必须首先知道这个问题的答案,并希望我自己找到解决方案。 Hats off to them... 向他们致敬......

I swear to God, this is a hard problem I have finally conquered. 我向上帝发誓,这是我最终征服的一个难题。 There goes 2 years of wasted life down the drain... 耗费了2年的浪费时间......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM