简体   繁体   English

Android OpenGL ES纹理半球

[英]Android OpenGL ES textured half sphere

I have to develop an equirectangular image viewer, like the one of the Ricoh Theta app . 我必须开发一个equirectangular图像查看器,就像理光Theta应用程序之一

I'm doing it on Android, with Open GL ES (1.0, but I can change to 2.0 if needed). 我在Android上使用Open GL ES(1.0,但如果需要我可以更改为2.0)。

For now, I have managed to create the half sphere (based on this answer ), with this code: 目前,我已设法创建半球(基于此答案 ),使用以下代码:

public class HalfSphere {

    // ---------------------------------------------------------------------------------------------
    // region Attributes

    private final int[] mTextures = new int[1];

    float[][] mVertices;
    int mNbStrips;
    int mNbVerticesPerStrips;

    private final List<FloatBuffer> mVerticesBuffer = new ArrayList<>();
    private final List<ByteBuffer> mIndicesBuffer = new ArrayList<>();
    private final List<FloatBuffer> mTextureBuffer = new ArrayList<>();

    // endregion
    // ---------------------------------------------------------------------------------------------



    // ---------------------------------------------------------------------------------------------
    // region Constructor

    public HalfSphere(int nbStrips, int nbVerticesPerStrips, float radius) {

        // Generate the vertices:
        mNbStrips = nbStrips;
        mNbVerticesPerStrips = nbVerticesPerStrips;
        mVertices = new float[mNbStrips * mNbVerticesPerStrips][3];

        for (int i = 0; i < mNbStrips; i++) {

            for (int j = 0; j < mNbVerticesPerStrips; j++) {

                mVertices[i * mNbVerticesPerStrips + j][0] = (float) (radius * Math.cos(j * 2 * Math.PI / mNbVerticesPerStrips) * Math.cos(i * Math.PI / mNbStrips));
                mVertices[i * mNbVerticesPerStrips + j][1] = (float) (radius * Math.sin(i * Math.PI / mNbStrips));
                mVertices[i * mNbVerticesPerStrips + j][2] = (float) (radius * Math.sin(j * 2 * Math.PI / mNbVerticesPerStrips) * Math.cos(i * Math.PI / mNbStrips));
            }
        }

        // Populate the buffers:
        for(int i = 0; i < mNbStrips - 1; i++) {

            for(int j = 0; j < mNbVerticesPerStrips; j++) {

                byte[] indices = {
                        0, 1, 2,  // first triangle (bottom left - top left - top right)
                        0, 2, 3   // second triangle (bottom left - top right - bottom right)
                };

                float[] p1 = mVertices[i * mNbVerticesPerStrips + j];
                float[] p2 = mVertices[i * mNbVerticesPerStrips + (j + 1) % mNbVerticesPerStrips];
                float[] p3 = mVertices[(i + 1) * mNbVerticesPerStrips + (j + 1) % mNbVerticesPerStrips];
                float[] p4 = mVertices[(i + 1) * mNbVerticesPerStrips + j];

                float[] quad = {
                        p1[0], p1[1], p1[2],
                        p2[0], p2[1], p2[2],
                        p3[0], p3[1], p3[2],
                        p4[0], p4[1], p4[2]
                };

                mVerticesBuffer.add(floatArrayToFloatBuffer(quad));
                mTextureBuffer.add(floatArrayToFloatBuffer(quad));
                mIndicesBuffer.add(byteArrayToByteBuffer(indices));
            }
        }
    }

    // endregion
    // ---------------------------------------------------------------------------------------------



    // ---------------------------------------------------------------------------------------------
    // region Draw

    public void draw(final GL10 gl) {

        // bind the previously generated texture.
        gl.glBindTexture(GL10.GL_TEXTURE_2D, this.mTextures[0]);

        // Point to our buffers.
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

        // Set the face rotation, clockwise in this case.
        gl.glFrontFace(GL10.GL_CW);

        for(int i = 0; i < mVerticesBuffer.size(); i++) {

            gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVerticesBuffer.get(i));
            gl.glTexCoordPointer(3, GL10.GL_FLOAT, 0, mTextureBuffer.get(i));

            gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, 6, GL10.GL_UNSIGNED_BYTE, mIndicesBuffer.get(i)); // GL_TRIANGLE_STRIP / GL_LINE_LOOP
        }

        // Disable the client state before leaving.
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    }

    // endregion
    // ---------------------------------------------------------------------------------------------



    // ---------------------------------------------------------------------------------------------
    // region Utils

    public void loadGLTexture(GL10 gl, Bitmap texture) {
        // Generate one texture pointer, and bind it to the texture array.
        gl.glGenTextures(1, this.mTextures, 0);
        gl.glBindTexture(GL10.GL_TEXTURE_2D, this.mTextures[0]);

        // Create nearest filtered texture.
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

        // Use Android GLUtils to specify a two-dimensional texture image from our bitmap.
        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, texture, 0);
        texture.recycle();
    }

    public FloatBuffer floatArrayToFloatBuffer(float[] array) {

        ByteBuffer vbb = ByteBuffer.allocateDirect(array.length * 4);
        vbb.order(ByteOrder.nativeOrder());    // use the device hardware's native byte order
        FloatBuffer fb = vbb.asFloatBuffer();  // create a floating point buffer from the ByteBuffer
        fb.put(array);    // add the coordinates to the FloatBuffer
        fb.position(0);      // set the buffer to read the first coordinate

        return fb;
    }

    public ByteBuffer byteArrayToByteBuffer(byte[] array) {

        ByteBuffer vbb = ByteBuffer.allocateDirect(array.length * 4);
        vbb.order(ByteOrder.nativeOrder());    // use the device hardware's native byte order
        vbb.put(array);    // add the coordinates to the FloatBuffer
        vbb.position(0);      // set the buffer to read the first coordinate

        return vbb;
    }

    // endregion
    // ---------------------------------------------------------------------------------------------
}

Of course, the texture is not applied correctly, as I'm using the coordinates of my vertices. 当然,纹理没有正确应用,因为我正在使用我的顶点的坐标。 Does someone see how to do it correctly? 有人看到如何正确地做到这一点? I'll also need to be able to "move" the texture when the user pan. 我还需要能够在用户平移时“移动”纹理。

EDIT: as suggested by codetiger, doing lat/180 and lon/360, and then normalizing to [0..1] worked. 编辑:按照codetiger的建议,执行lat / 180和lon / 360,然后归一化为[0..1]工作。 Now, I'm trying to add the panning. 现在,我正在尝试添加平移。 It works when panning on longitude (horizontally): 它在经度(水平)平移时有效:

经度平移

But not when panning on latitude (vertically): 但不是在纬度(垂直)平移时:

在此输入图像描述

I'm simply adding values between 0..1 when the user pans. 我只是在用户平移时在0..1之间添加值。 I tried to use the formula given here with no success. 我试着用这里给出的公式没有成功。 Any idea? 任何想法?

If it helps, that's what I want (obtained with the Ricoh Theta app): 如果它有帮助,那就是我想要的(用理光Theta应用程序获得):

在此输入图像描述

In order to make the sphere a full 360 degree sphere, you can replace the lines below. 为了使球体成为一个完整的360度球体,您可以替换下面的线条。

mVertices[i * mNbVerticesPerStrips + j][0] = (float) (radius * Math.cos(j * 2 * Math.PI / mNbVerticesPerStrips) * Math.cos(2 * i * Math.PI / mNbStrips));
mVertices[i * mNbVerticesPerStrips + j][1] = (float) (radius * Math.sin(2 * i * Math.PI / mNbStrips));
mVertices[i * mNbVerticesPerStrips + j][2] = (float) (radius * Math.sin(j * 2 * Math.PI / mNbVerticesPerStrips) * Math.cos(2 * i * Math.PI / mNbStrips));

The only change is using 2 * Math.PI / mNbStrips for second angle instead of Math.PI / mNbStrips 唯一的变化是用2 * Math.PI / mNbStrips为第二角度,而不是Math.PI / mNbStrips

And to rotate the image, you can rotate the sphere by using 要旋转图像,您可以使用旋转球体

gl.glRotatef(angle, 1.0f, 0.0f, 0.0f);

Update: To get correct Texture Coordinates for the sphere, for standard distortion sphere texture you can use (lat/180, lon/360) and normalise it to get [0..1]. 更新:要获得球体的正确纹理坐标,对于标准失真球体纹理,您可以使用(lat / 180,lon / 360)并将其标准化以获得[0..1]。 As mentioned here https://stackoverflow.com/a/10395141/409315 这里提到https://stackoverflow.com/a/10395141/409315

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

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