简体   繁体   English

在OpenGL ES 2.0中绘制球体

[英]Drawing a sphere in OpenGL ES 2.0

I'm trying to draw a sphere in openGL ES 2.0 on Android. 我正在尝试在Android的openGL ES 2.0中画一个球。 I already looked at the related questions and tried some of their code but I still can't get it to work. 我已经查看了相关问题并尝试了一些代码,但仍然无法正常工作。

Based on the Android developer examples and this code found on gamedev.net I came up with the code below. 基于Android开发人员示例以及在gamedev.net上找到的这段代码,想到了以下代码。 However it is not drawing correctly; 但是,它绘制不正确。 When using glDrawArrays() rendering works but the results are not correct, when using glDrawElements() I get an GL_INVALID_OPERATION error. 使用glDrawArrays()渲染时可以工作,但结果不正确,使用glDrawElements()会出现GL_INVALID_OPERATION错误。 I listed the contents of my buffers below. 我在下面列出了缓冲区的内容。

Sphere.java 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 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);
        }
    }
}

The contents of vertexBuffer 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

The contents of indexBuffer 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]

The result when using glDrawArrays() : 使用glDrawArrays()时的结果:

结果

As promised the working code for creating the sphere: 如所承诺的,创建球体的工作代码:

    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;

    }

You're setting vertexCount as lat * lon * bytes per float , which looks very weird to me. 您将vertexCount设置为lat * lon * bytes per float ,这对我来说很奇怪。

I think you have misnamed this variable, as the number of vertices has nothing to do with bytes per float. 我认为您没有正确命名此变量,因为顶点数与每个浮点数的字节无关。

You're using the same variable in glDrawArrays, which seems to me will not have the accurate number of vertices. 您在glDrawArrays中使用了相同的变量,在我看来,顶点的数量将不准确。

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

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