2016-09-06 4 views
0

Мне нужно создать равноугольный просмотрщик изображений, например, один из Ricoh Theta app.Android OpenGL ES текстурированная полусфера

Я делаю это на Android с Open GL ES (1.0, но при необходимости я могу изменить его на 2.0).

В настоящем время, мне удалось создать половину сферы (на основе this answer), с этим кодом:

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 
    // --------------------------------------------------------------------------------------------- 
} 

Конечно, текстура не применяется правильно, так как я использую координаты мои вершины. Кто-нибудь видит, как это сделать правильно? Мне также нужно будет «переместить» текстуру, когда пользователь скопирует.

EDIT: как предложено кодификатором, выполняющим lat/180 и lon/360, а затем нормализуя [0..1]. Теперь я пытаюсь добавить панорамирование. Он работает, когда панорамирование по долготе (по горизонтали):

longitude panning

Но не тогда, когда панорамирование на широте (по вертикали):

enter image description here

Я просто добавляющие ценности между 0..1 когда это пользовательские панели. Я попытался использовать формулу, указанную here, без успеха. Есть идеи?

Если это помогает, это то, что я хочу (полученный с приложением Ricoh Theta):

enter image description here

ответ

0

Для того, чтобы сфера полной сферы 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)); 

Единственное изменение использует 2 * Math.PI/mNbStrips для второго угла вместо Math.PI/mNbStrips

И повернуть изображение, вы можете вращать сферу, используя

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

Update: Чтобы получить правильную текстуру Координаты для сферы, для стандартной текстуры искажения, которую вы можете использовать (lat/180, lon/360) и нормализовать ее, чтобы получить [0..1]. Как упоминалось здесь https://stackoverflow.com/a/10395141/409315

+0

Спасибо, но половина сферы - это то, что мне нужно, чтобы иметь возможность «unzoom» (= отображать полусферу дальше). С сферой, если я получаю «выход» из сферы, результат не то, что я хочу. Вопрос в том, как применить текстуру к моей половине сферы? –

+0

Да, так оно и работает. Но теперь у меня проблемы с широким панорамированием (см. Мои правки). –

+0

На самом деле, я думаю, вы не можете панорамировать по широте для изображения с равноправным проекционным изображением. Это создаст искажение. – codetiger