java - 在 OpenGL ES 2.0 中绘制球体

标签 java android opengl-es glsl opengl-es-2.0

我正在尝试在 Android 上的 openGL ES 2.0 中绘制一个球体。我已经查看了相关问题并尝试了他们的一些代码,但我仍然无法让它工作。

基于Android developer examplesthis code found on gamedev.net我想出了下面的代码。然而它并没有正确绘制;当使用 glDrawArrays() 渲染有效但结果不正确时,当使用 glDrawElements() 时,我收到 GL_INVALID_OPERATION 错误。我在下面列出了缓冲区的内容。

Sphere.java

public class Sphere
{
    private int stacks;
    private int slices;
    private float radius;

    //Buffers
    private FloatBuffer vertexBuffer;
    private FloatBuffer colorBuffer;
    private ShortBuffer indexBuffer;

    //Buffer sizes in aantal bytes
    private int vertexBufferSize;
    private int colorBufferSize;
    private int indexBufferSize;

    private int vertexCount;

    private int program;

    static final int FLOATS_PER_VERTEX = 3; // Het aantal floats in een vertex (x, y, z)
    static final int FLOATS_PER_COLOR = 4;  // Het aantal floats in een kleur (r, g, b, a)
    static final int SHORTS_PER_INDEX = 2;  
    static final int BYTES_PER_FLOAT = 4;   
    static final int BYTES_PER_SHORT = 2;   

    static final int BYTES_PER_VERTEX = FLOATS_PER_VERTEX * BYTES_PER_FLOAT;
    static final int BYTES_PER_COLOR = FLOATS_PER_COLOR * BYTES_PER_FLOAT;
    static final int BYTES_PER_INDEX_ENTRY = SHORTS_PER_INDEX * BYTES_PER_SHORT;

    // Set color with red, green, blue and alpha (opacity) values
    private float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };

    public Sphere(float radius, int stacks, int slices)
    {
        this.stacks = stacks;
        this.slices = slices;
        this.radius = radius;

        vertexCount         = (stacks+1) * (slices+1);
        vertexBufferSize    = vertexCount * BYTES_PER_VERTEX;
        colorBufferSize     = vertexCount * BYTES_PER_COLOR;
        indexBufferSize     = vertexCount * BYTES_PER_INDEX_ENTRY;

        program = GLHelpers.createProgram();
        if (program == 0) {
            return;
        }
        GLHelpers.checkGlError("program");

        // Setup vertex-array buffer. Vertices in float. A float has 4 bytes.
        vertexBuffer = ByteBuffer.allocateDirect(vertexBufferSize).order(ByteOrder.nativeOrder()).asFloatBuffer();
        colorBuffer = ByteBuffer.allocateDirect(colorBufferSize).order(ByteOrder.nativeOrder()).asFloatBuffer();
        indexBuffer = ByteBuffer.allocateDirect(indexBufferSize).order(ByteOrder.nativeOrder()).asShortBuffer();    

        generateSphereCoords(radius, stacks, slices);

        vertexBuffer.position(0);
        colorBuffer.position(0);
        indexBuffer.position(0);
    }


    public void draw(float[] modelViewProjectionMatrix)
    {
        GLES20.glUseProgram(program);

        GLHelpers.checkGlError("useprogram");

        int positionHandle = GLES20.glGetAttribLocation(program, "a_Position");
        GLES20.glEnableVertexAttribArray(positionHandle);
        GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, BYTES_PER_VERTEX, vertexBuffer);
        GLHelpers.checkGlError("pos");

        //int colorHandle = GLES20.glGetAttribLocation(program, "a_Color");
        //GLES20.glEnableVertexAttribArray(colorHandle);
        //GLES20.glVertexAttribPointer(colorHandle, 4, GLES20.GL_FLOAT, false, BYTES_PER_COLOR, colorBuffer);
        //GLHelpers.checkGlError("color");

        int matrixHandle = GLES20.glGetUniformLocation(program, "u_Matrix");
        GLES20.glUniformMatrix4fv(matrixHandle, 1, false, modelViewProjectionMatrix, 0);

        /*
         * When using glDrawArrays rendering works but the results are not correct, when using glDrawElements I get an GL_INVALID_OPERATION error.
         */
        GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, indexBuffer.capacity(), GLES20.GL_SHORT, indexBuffer);
        //GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexCount);

        GLHelpers.checkGlError("draw");

        // Disable vertex array
        GLES20.glDisableVertexAttribArray(positionHandle);
        //GLES20.glDisableVertexAttribArray(colorHandle);
    }

    private void generateSphereCoords(float radius, int stacks, int slices)
    {
        for (int stackNumber = 0; stackNumber <= stacks; ++stackNumber)
        {
            for (int sliceNumber = 0; sliceNumber < slices; ++sliceNumber)
            {
                float theta = (float) (stackNumber * Math.PI / stacks);
                float phi = (float) (sliceNumber * 2 * Math.PI / slices);
                float sinTheta = FloatMath.sin(theta);
                float sinPhi = FloatMath.sin(phi);
                float cosTheta = FloatMath.cos(theta);
                float cosPhi = FloatMath.cos(phi);
                vertexBuffer.put(new float[]{radius * cosPhi * sinTheta, radius * sinPhi * sinTheta, radius * cosTheta});
            }
        }

        for (int stackNumber = 0; stackNumber < stacks; ++stackNumber)
        {
            for (int sliceNumber = 0; sliceNumber <= slices; ++sliceNumber)
            {
                indexBuffer.put((short) ((stackNumber * slices) + (sliceNumber % slices)));
                indexBuffer.put((short) (((stackNumber + 1) * slices) + (sliceNumber % slices)));
            }
        }
    }
}

GLHelpers.java

public class GLHelpers
{
    private static final String TAG = "GLHelpers";

    private static final String VERTEX_SHADER_CODE =
            "uniform mat4 u_Matrix;" +
            "attribute vec4 a_Position;" +
            "attribute vec4 a_Color;" +
            "varying vec4 v_Color;" +
            "void main() {" +
            "  v_Color = a_Color;" +
            "  gl_Position = a_Position * u_Matrix;" +
            "}";

    private static final String FRAGMENT_SHADER_CODE =
        "precision mediump float;" +
        "varying vec4 v_Color;" +
        "void main() {" +
        "  gl_FragColor = v_Color;" +
        "}";

    private static int loadShader(int shaderType, String source)
    {
        int shader = GLES20.glCreateShader(shaderType);
        if (shader != 0) {
            GLES20.glShaderSource(shader, source);
            GLES20.glCompileShader(shader);
            int[] compiled = new int[1];
            GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
            if (compiled[0] == 0)
            {
                Log.e(TAG, "Could not compile shader " + shaderType + ":");
                Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
                GLES20.glDeleteShader(shader);
                shader = 0;
            }
        }
        return shader;
    }

    public static int createProgram()
    {
        int vertexShader = GLHelpers.loadShader(GLES20.GL_VERTEX_SHADER, GLHelpers.VERTEX_SHADER_CODE);
        if (vertexShader == 0)
            return 0;

        int pixelShader = GLHelpers.loadShader(GLES20.GL_FRAGMENT_SHADER, GLHelpers.FRAGMENT_SHADER_CODE);
        if (pixelShader == 0)
            return 0;

        int program = GLES20.glCreateProgram();
        if (program != 0) {
            GLES20.glAttachShader(program, vertexShader);
            GLHelpers.checkGlError("glAttachShader");
            GLES20.glAttachShader(program, pixelShader);
            GLHelpers.checkGlError("glAttachShader");
            GLES20.glLinkProgram(program);
            int[] linkStatus = new int[1];
            GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
            if (linkStatus[0] != GLES20.GL_TRUE)
            {
                Log.e(TAG, "Could not link program: ");
                Log.e(TAG, GLES20.glGetProgramInfoLog(program));
                GLES20.glDeleteProgram(program);
                program = 0;
            }
        }
        return program;
    }

    public static void checkGlError(String glOperation)
    {
        int error;
        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR)
        {
            Log.e(TAG, glOperation + ": glError " + error);
            throw new RuntimeException(glOperation + ": glError " + error);
        }
    }
}

vertexBuffer的内容

X               Y               Z
0.0,            0.0,            1.0,
0.0,            0.0,            1.0,
-0.0,           0.0,            1.0,
-0.0,           -0.0,           1.0,
0.0,            -0.0,           1.0,
0.58778524,     0.0,            0.809017,
0.18163562,     0.559017,       0.809017,
-0.4755283,     0.34549147,     0.809017,
-0.4755282,     -0.34549156,    0.809017,
0.18163571,     -0.55901694,    0.809017,
0.95105654,     0.0,            0.30901697,
0.29389262,     0.90450853,     0.30901697,
-0.769421,      0.55901694,     0.30901697,
-0.76942086,    -0.5590171,     0.30901697,
0.29389274,     -0.9045085,     0.30901697,
0.9510565,      0.0,            -0.30901703,
0.2938926,      0.9045085,      -0.30901703,
-0.7694209,     0.5590169,      -0.30901703,
-0.7694208,     -0.55901706,    -0.30901703,
0.29389274,     -0.9045084,     -0.30901703,
0.5877852,      0.0,            -0.80901706,
0.1816356,      0.55901694,     -0.80901706,
-0.47552824,    0.3454914,      -0.80901706,
-0.47552818,    -0.34549153,    -0.80901706,
0.1816357,      -0.5590169,     -0.80901706,
-8.742278E-8,   -0.0,           -1.0,
-2.7015123E-8,  -8.3144E-8,     -1.0,
7.0726514E-8,   -5.138581E-8,   -1.0,
7.072651E-8,    5.138583E-8,    -1.0,
-2.7015135E-8,  8.3143995E-8,   -1.0,
0.0,            0.0,            0.0,
0.0,            0.0,            0.0,
0.0,            0.0,            0.0,
0.0,            0.0,            0.0,
0.0,            0.0,            0.0,
0.0,            0.0,            0.0

indexBuffer的内容

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

使用glDrawArrays()时的结果:

result

最佳答案

正如所 promise 的,创建球体的工作代码:

    public static Model3D createSphere(float radius, int stacks, int slices)
    {
        int vertexCount = (stacks + 1) * (slices + 1);
        FloatBuffer vertexBuffer        = ByteBuffer.allocateDirect(vertexCount * GLHelpers.BYTES_PER_VERTEX).order(ByteOrder.nativeOrder()).asFloatBuffer();
        FloatBuffer normalBuffer        = ByteBuffer.allocateDirect(vertexCount * GLHelpers.BYTES_PER_NORMAL).order(ByteOrder.nativeOrder()).asFloatBuffer();
        FloatBuffer textureCoordBuffer  = ByteBuffer.allocateDirect(vertexCount * GLHelpers.BYTES_PER_TEXTURE_COORD).order(ByteOrder.nativeOrder()).asFloatBuffer();
        ShortBuffer indexBuffer         = ByteBuffer.allocateDirect(vertexCount * GLHelpers.BYTES_PER_TRIANGLE_INDEX).order(ByteOrder.nativeOrder()).asShortBuffer();

        for (int stackNumber = 0; stackNumber <= stacks; ++stackNumber)
        {
            for (int sliceNumber = 0; sliceNumber <= slices; ++sliceNumber)
            {
                float theta = (float) (stackNumber * Math.PI / stacks);
                float phi = (float) (sliceNumber * 2 * Math.PI / slices);
                float sinTheta = FloatMath.sin(theta);
                float sinPhi = FloatMath.sin(phi);
                float cosTheta = FloatMath.cos(theta);
                float cosPhi = FloatMath.cos(phi);

                float nx = cosPhi * sinTheta;
                float ny = cosTheta;
                float nz = sinPhi * sinTheta;


                float x = radius * nx;
                float y = radius * ny;
                float z = radius * nz;

                float u = 1.f - ((float)sliceNumber / (float)slices);
                float v = (float)stackNumber / (float)stacks;



                normalBuffer.put(nx);
                normalBuffer.put(ny);
                normalBuffer.put(nz);

                vertexBuffer.put(x);
                vertexBuffer.put(y);
                vertexBuffer.put(z);

                textureCoordBuffer.put(u);
                textureCoordBuffer.put(v);
            }
        }

        for (int stackNumber = 0; stackNumber < stacks; ++stackNumber)
        {
            for (int sliceNumber = 0; sliceNumber < slices; ++sliceNumber)
            {
                int second = (sliceNumber * (stacks + 1)) + stackNumber;
                int first = second + stacks + 1;

                //int first = (stackNumber * slices) + (sliceNumber % slices);
                //int second = ((stackNumber + 1) * slices) + (sliceNumber % slices);

                indexBuffer.put((short) first);
                indexBuffer.put((short) second);
                indexBuffer.put((short) (first + 1));

                indexBuffer.put((short) second);
                indexBuffer.put((short) (second + 1));
                indexBuffer.put((short) (first + 1));
            }
        }

        vertexBuffer.rewind();
        normalBuffer.rewind();
        indexBuffer.rewind();
        textureCoordBuffer.rewind();

        Model3D sphere = new Model3D().setVertexBuffer(vertexBuffer)
                                      .setNormalBuffer(normalBuffer)
                                      .setIndexBuffer(indexBuffer)
                                      .setTexture(R.drawable.earth)
                                      .setTextureCoordBuffer(textureCoordBuffer)
                                      .setDiffuseLighting(-3f, 2.3f, 2f);
        return sphere;

    }

关于java - 在 OpenGL ES 2.0 中绘制球体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11376064/

相关文章:

java - 如何使用java的selenium验证下拉菜单是否打开?

java - 支持的区域设置 - ga_IE

android - 使用 JSON 使用未弃用的方法将 MySQL 数据加载到 ListView 中

android - 如何从 Android 发送 SNEP GET 请求

opengl-es - 跨步在OpenGL | ES中是什么意思

java - 如何使用线程的 id 挂起线程?

java - 使用本地 fork 的 Maven 方法是什么?

android - 旋转后, Sprite 和矩形位置在 Libgdx 中未对齐

android - 使用 ffmpeg 播放带有音轨的 mp3 数据

android - 在 Android 上使用 OpenGL 时自动清除缓冲区