OpenGL ES - текстура карты всех граней 8-вершинного куба?

Работа с некоторыми учебными пособиями по OpenGL-ES с использованием эмулятора Android. Я приступил к отображению текстур и у меня возникли проблемы с отображением куба. Можно ли отобразить текстуру на все грани куба, имеющего 8 вершин и 12 треугольников для 6 граней, как описано ниже?

// Use half as we are going for a 0,0,0 centre.
    width  /= 2;
    height /= 2;
    depth  /= 2;

    float vertices[] = { -width, -height, depth, // 0
                          width, -height, depth, // 1
                          width,  height, depth, // 2
                         -width,  height, depth, // 3
                         -width, -height, -depth, // 4
                          width, -height, -depth, // 5
                          width,  height, -depth, // 6
                         -width,  height, -depth, // 7
    };

    short indices[] = { 
            // Front
            0,1,2,
            0,2,3,
            // Back
            5,4,7,
            5,7,6,
            // Left
            4,0,3,
            4,3,7,
            // Right
            1,5,6,
            1,6,2,
            // Top
            3,2,6,
            3,6,7,
            // Bottom
            4,5,1,
            4,1,0,
    };

   float texCoords[] = {
        1.0f, 1.0f,
        0.0f, 1.0f,
        0.0f, 0.0f,
        1.0f, 0.0f,

        1.0f, 1.0f,
        0.0f, 1.0f,
        0.0f, 0.0f,
        1.0f, 0.0f,

        1.0f, 1.0f,
        0.0f, 1.0f,
        0.0f, 0.0f,
        1.0f, 0.0f,

        1.0f, 1.0f,
        0.0f, 1.0f,
        0.0f, 0.0f,
        1.0f, 0.0f,

        1.0f, 1.0f,
        0.0f, 1.0f,
        0.0f, 0.0f,
        1.0f, 0.0f,

        1.0f, 1.0f,
        0.0f, 1.0f,
        0.0f, 0.0f,
        1.0f, 0.0f,
    };

Я получил правильную работу передней и задней граней, однако ни одна из других граней не показывает текстуру.

http://www.freeimagehosting.net/uploads/60d292613d.jpg

Чертежный код

public void draw(GL10 gl) {
    // Counter-clockwise winding.
    gl.glFrontFace(GL10.GL_CCW);

    // Enable face culling.
    gl.glEnable(GL10.GL_CULL_FACE);

    // What faces to remove with the face culling.
    gl.glCullFace(GL10.GL_BACK);

    // Enabled the vertices buffer for writing and to be used during
    // rendering.
    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

    // Specifies the location and data format of an array of vertex
    // coordinates to use when rendering.
    gl.glVertexPointer(3, GL10.GL_FLOAT, 0, verticesBuffer);

    if (normalsBuffer != null) {
        // Enabled the normal buffer for writing and to be used during rendering.
        gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);

        // Specifies the location and data format of an array of normals to use when rendering.
        gl.glNormalPointer(GL10.GL_FLOAT, 0, normalsBuffer);
    }

    // Set flat color
    gl.glColor4f(rgba[0], rgba[1], rgba[2], rgba[3]);

    // Smooth color
    if ( colorBuffer != null ) {
        // Enable the color array buffer to be used during rendering.
        gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
        // Point out the where the color buffer is.
        gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuffer);
    }

    // Use textures?
    if ( textureBuffer != null) {
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        gl.glEnable(GL10.GL_TEXTURE_2D);
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
    }

    // Translation and rotation before drawing
    gl.glTranslatef(x, y, z);
    gl.glRotatef(rx, 1, 0, 0);
    gl.glRotatef(ry, 0, 1, 0);
    gl.glRotatef(rz, 0, 0, 1);

    gl.glDrawElements(GL10.GL_TRIANGLES, numOfIndices, GL10.GL_UNSIGNED_SHORT, indicesBuffer);

    // Disable the vertices buffer.
    gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
    gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
    gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

    // Disable face culling.
    gl.glDisable(GL10.GL_CULL_FACE);
}

2 ответа

Решение

Вы должны использовать 24 вершины. В OpenGL вершина - это не просто позиция, это совокупность всех атрибутов вершины. Доступ к каждому массиву вершин осуществляется с одинаковым индексом.

Печально известный пример куба - это то, что почти все считают неэффективным, когда начинают использовать OpenGL, но в реальных, более сложных моделях степень дублирования довольно низкая.

Я "только" изменил файл GLES20TriangleRenderer.java в примере SDK BasicGLSurfaceView, скомпилировал его и протестировал на двух устройствах Android, телефоне Android и Nexus 7, и это хорошо работает на двух устройствах:)

/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.

   Modified by YLP (06 January 2014) for to handle a rotated texture mapped cube

 */

package com.example.android.basicglsurfaceview;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import android.os.SystemClock;
import android.util.Log;

class GLES20TriangleRenderer implements GLSurfaceView.Renderer {

    public GLES20TriangleRenderer(Context context) {
        mContext = context;

        // mTriangleVertices = ByteBuffer.allocateDirect(mTriangleVerticesData.length * FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
        // mTriangleVertices.put(mTriangleVerticesData).position(0);

        mTriangleVertices = ByteBuffer.allocateDirect(cubeVerticesStrip.length * FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
        mTriangleVertices.put(cubeVerticesStrip).position(0);

        mTriangleTexcoords = ByteBuffer.allocateDirect(cubeTexCoordsStrip.length * FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
        mTriangleTexcoords.put(cubeTexCoordsStrip).position(0);

    }

    public void onDrawFrame(GL10 glUnused) {
        // Ignore the passed-in GL10 interface, and use the GLES20
        // class's static methods instead.
        GLES20.glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
        GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);

        GLES20.glEnable( GLES20.GL_DEPTH_TEST );
        GLES20.glDepthFunc( GLES20.GL_LEQUAL );
        GLES20.glDepthMask( true );

        GLES20.glUseProgram(mProgram);
        checkGlError("glUseProgram");

        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID);

        // mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
        // GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,
        //        TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
        // checkGlError("glVertexAttribPointer maPosition");

        // mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
        // GLES20.glEnableVertexAttribArray(maPositionHandle);
        // checkGlError("glEnableVertexAttribArray maPositionHandle");

        // GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false,
        //         TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
        // checkGlError("glVertexAttribPointer maTextureHandle");
        // GLES20.glEnableVertexAttribArray(maTextureHandle);
        // checkGlError("glEnableVertexAttribArray maTextureHandle");

        // From http://www.endodigital.com/opengl-es-2-0-on-the-iphone/part-fourteen-creating-the-cube
        // (but slighty modified)
        mTriangleVertices.position(0);
        // GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
           GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, 0, mTriangleVertices);
        GLES20.glEnableVertexAttribArray(maPositionHandle);

        mTriangleTexcoords.position(0);
        // GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false,TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
           GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false, 0, mTriangleTexcoords);
        GLES20.glEnableVertexAttribArray(maTextureHandle);

        long time = SystemClock.uptimeMillis() % 4000L;
        float angle = 0.090f * ((int) time);
        float scale = 0.7f;

        Matrix.setRotateM(mMMatrix, 0, angle, 0, 0, 1.0f);

        // YLP : add others movements cycles
        Matrix.rotateM(mMMatrix, 0, angle, 1.0f, 0.0f, 0.0f );
        // Matrix.rotateM(mMMatrix, 0, angle, 0.0f, 1.0f, 0.0f );
        // float scale = (float)( Math.abs( Math.sin( ((float)time) * (6.28f/4000.0f) ) ));
        Matrix.scaleM(mMMatrix, 0, scale, scale, scale);


        Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0);
        Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);

        GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);

        // Somes tests with only somes triangles
        // GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3); // worked initialy but only one triangle
        // GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 6); // worked initialy but only two triangles
        // GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); // GL_QUADS does not exist in GL 2.0  :(
        // GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 8); // GL_QUADS does not exist in GL 2.0  :(

        // Draw the cube
        // TODO : make only one glDraWArrays() call instead one per face
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 4, 4);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 8, 4);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 12, 4);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 16, 4);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 20, 4);

        checkGlError("glDrawArrays");
    }

    public void onSurfaceChanged(GL10 glUnused, int width, int height) {
        // Ignore the passed-in GL10 interface, and use the GLES20
        // class's static methods instead.
        GLES20.glViewport(0, 0, width, height);
        float ratio = (float) width / height;
        Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
    }

    public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
        // Ignore the passed-in GL10 interface, and use the GLES20
        // class's static methods instead.
        mProgram = createProgram(mVertexShader, mFragmentShader);
        if (mProgram == 0) {
            return;
        }
        maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
        checkGlError("glGetAttribLocation aPosition");
        if (maPositionHandle == -1) {
            throw new RuntimeException("Could not get attrib location for aPosition");
        }
        maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
        checkGlError("glGetAttribLocation aTextureCoord");
        if (maTextureHandle == -1) {
            throw new RuntimeException("Could not get attrib location for aTextureCoord");
        }

        muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
        checkGlError("glGetUniformLocation uMVPMatrix");
        if (muMVPMatrixHandle == -1) {
            throw new RuntimeException("Could not get attrib location for uMVPMatrix");
        }

        /*
         * Create our texture. This has to be done each time the
         * surface is created.
         */

        int[] textures = new int[1];
        GLES20.glGenTextures(1, textures, 0);

        mTextureID = textures[0];
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID);

        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
                GLES20.GL_NEAREST);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                GLES20.GL_TEXTURE_MAG_FILTER,
                GLES20.GL_LINEAR);

        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
                GLES20.GL_REPEAT);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
                GLES20.GL_REPEAT);

        InputStream is = mContext.getResources()
            .openRawResource(R.raw.robot);
        Bitmap bitmap;
        try {
            bitmap = BitmapFactory.decodeStream(is);
        } finally {
            try {
                is.close();
            } catch(IOException e) {
                // Ignore.
            }
        }

        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
        bitmap.recycle();

        Matrix.setLookAtM(mVMatrix, 0, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
    }

    private 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;
    }

    private int createProgram(String vertexSource, String fragmentSource) {
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
        if (vertexShader == 0) {
            return 0;
        }

        int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
        if (pixelShader == 0) {
            return 0;
        }

        int program = GLES20.glCreateProgram();
        if (program != 0) {
            GLES20.glAttachShader(program, vertexShader);
            checkGlError("glAttachShader");
            GLES20.glAttachShader(program, pixelShader);
            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;
    }

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

    private static final int FLOAT_SIZE_BYTES = 4;
    private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
    private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
    private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
    private static final int TRIANGLE_TEXCOORDS_DATA_STRIDE_BYTES = 2 * FLOAT_SIZE_BYTES;

    private final float[] mTriangleVerticesData =
    {
            // X, Y, Z, U, V

           // initial triangle from the source
           // -1.0f,   -0.5f,          0,  -0.5f,   0.0f,
           //  1.0f,   -0.5f,          0,   1.5f,  -0.0f,
           // 0.0f,    1.11803399f,   0,   0.5f,   1.61803399f,

            // YLP : transform this to two triangles for to have a quad
            // -1, -1, 0,    0, 0,
            // 1, -1, 0,    1, 0,
            // -1,  1, 0,    0, 1,
            // 1,  1, 0,    1, 1,
            // -1,  1, 0,    0, 1,
            // 1, -1, 0,    1, 0

            // YLP :  use two quads with GL_TRIANGLE_STRIP
            // Don't work because this make one accordeon effect :(
            -1, -1, -1,    0, 0,
             1, -1, -1,    1, 0,
            -1,  1, -1,    0, 1,
             1,  1, -1,    1, 1,
            -1, -1,  1,    0, 0,
             1, -1,  1,    1, 0,
            -1,  1,  1,    0, 1,
             1,  1,  1,    1, 1,
    };

    // From http://www.endodigital.com/opengl-es-2-0-on-the-iphone/part-fourteen-creating-the-cube/
    // (only moodify "static const GLfloat" to "private final float" on it)
    private final float cubeVerticesStrip[] = {
            // Front face
            -1,-1,1, 1,-1,1, -1,1,1, 1,1,1,
            // Right face
            1,-1,1, 1,-1,-1, 1,1,1, 1,1,-1,
            // Back face
            1,-1,-1, -1,-1,-1, 1,1,-1, -1,1,-1,
            // Left face
            -1,-1,-1, -1,-1,1, -1,1,-1, -1,1,1,
            // Bottom face
            -1,-1,-1, 1,-1,-1, -1,-1,1, 1,-1,1,
            // Top face
            -1,1,1, 1,1,1, -1,1,-1, 1,1,-1

    };

    private final float cubeTexCoordsStrip[] = {
            // Front face
            0,0, 1,0, 0,1, 1,1,
            // Right face
            0,0, 1,0, 0,1, 1,1,
            // Back face
            0,0, 1,0, 0,1, 1,1,
            // Left face
            0,0, 1,0, 0,1, 1,1,
            // Bottom face
            0,0, 1,0, 0,1, 1,1,
            // Top face
            0,0, 1,0, 0,1, 1,1
    };

    private FloatBuffer mTriangleVertices;
    private FloatBuffer mTriangleTexcoords;

    private final String mVertexShader =
        "uniform mat4 uMVPMatrix;\n" +
        "attribute vec4 aPosition;\n" +
        "attribute vec2 aTextureCoord;\n" +
        "varying vec2 vTextureCoord;\n" +
        "void main() {\n" +
        "  gl_Position = uMVPMatrix * aPosition;\n" +
        "  vTextureCoord = aTextureCoord;\n" +
        "}\n";

    private final String mFragmentShader =
        "precision mediump float;\n" +
        "varying vec2 vTextureCoord;\n" +
        "uniform sampler2D sTexture;\n" +
        "void main() {\n" +
        "  gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
        "}\n";

    private float[] mMVPMatrix = new float[16];
    private float[] mProjMatrix = new float[16];
    private float[] mMMatrix = new float[16];
    private float[] mVMatrix = new float[16];

    private float[] mMMatrix2 = new float[16];

    private int mProgram;
    private int mTextureID;
    private int muMVPMatrixHandle;
    private int maPositionHandle;
    private int maTextureHandle;

    private Context mContext;
    private static String TAG = "GLES20TriangleRenderer";
}

=> Мне удалось реализовать это всего за несколько часов, и эта работа:)

==> так что, если Android не самая лучшая платформа, это, однако, кажется действительно хорошей и жизнеспособной платформой, для которой я начинаю немного больше играть с некоторыми мультимедийными разработками на устройствах Android:)

Другие вопросы по тегам