Как работает функция gluProject?Я не могу этого понять - PullRequest
3 голосов
/ 02 ноября 2011

Мне нужно показать квадратный многоугольник со 100% ширины экрана, затем я предлагаю увеличить его (по оси Z) до тех пор, пока границы многоугольника не пересекают границы экрана.

Я пытаюсь добиться этого с помощью gluProject, чтобы проецировать координату в 3D в координату 2D-экрана. Если координата экрана равна 0 или соответствует ширине или высоте, значит, она касается границы экрана.

Проблема в том, что что-то идет не так, массив outputCoords, возвращаемый с помощью gluProject, дает мне эти значения: 0,0,0,5, но мой квадрат центрирован по экрану, а с Z = -5,0f !! !!

Я не понимаю этих значений ...

Это код, который я использую для получения 2D-проекции моего квадратного полигона на экране:

Этот код находится в методе onSurfaceCreated класса GLSurfaceView, его нужно поместить в другой метод? ¿Где?

/////////////// NEW CODE FOR SCALING THE AR IMAGE TO THE DESIRED WIDTH /////////////////

        mg.getCurrentModelView(gl);  
        mg.getCurrentProjection(gl);   

        float [] modelMatrix = new float[16];
        float [] projMatrix = new float[16];        
        modelMatrix=mg.mModelView;
        projMatrix=mg.mProjection;
        int [] mView = new int[4];
        // Fill this with your window width and height
        mView[0] = 0;
        mView[1] = 0;
        mView[2] = 800; //width
        mView[3] = 480; //height
        // Make sure you have 3 components in this array even if the screen only needs 2
        float [] outputCoords = new float[3];
        // objX, objY, objZ are the coordinates of one of the borders
        GLU.gluProject(-1.0f, -1.0f, 0.0f, modelMatrix, 0, projMatrix, 0, mView, 0, outputCoords, 0);

Это мой квадратный класс:

public class Square {
//Buffer de vertices
private FloatBuffer vertexBuffer;
//Buffer de coordenadas de texturas
private FloatBuffer textureBuffer;
//Puntero de texturas
private int[] textures = new int[3];
//El item a representar
private Bitmap image;
//Definición de vertices

private float vertices[] = 
{ 
    -1.0f, -1.0f, 0.0f,     //Bottom Left
    1.0f, -1.0f, 0.0f,      //Bottom Right
    -1.0f, 1.0f, 0.0f,      //Top Left
    1.0f, 1.0f, 0.0f        //Top Right
};

private float texture[] =
{
    //Mapping coordinates for the vertices
    0.0f, 1.0f,
    1.0f, 1.0f,
    0.0f, 0.0f,
    1.0f, 0.0f
};
//Inicializamos los buffers
public Square(Bitmap image) {
    ByteBuffer byteBuf = ByteBuffer.allocateDirect(vertices.length * 4);
    byteBuf.order(ByteOrder.nativeOrder());
    vertexBuffer = byteBuf.asFloatBuffer();
    vertexBuffer.put(vertices);
    vertexBuffer.position(0);

    byteBuf = ByteBuffer.allocateDirect(texture.length * 4);
    byteBuf.order(ByteOrder.nativeOrder());
    textureBuffer = byteBuf.asFloatBuffer();
    textureBuffer.put(texture);
    textureBuffer.position(0);

    this.image=image;
} 
//Funcion de dibujado
public void draw(GL10 gl) {
    gl.glFrontFace(GL10.GL_CCW);
    //gl.glEnable(GL10.GL_BLEND);
    //Bind our only previously generated texture in this case
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
    //Point to our vertex buffer
    gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
    gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
    //Enable vertex buffer
    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    //Draw the vertices as triangle strip
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);
    //Disable the client state before leaving
    gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    //gl.glDisable(GL10.GL_BLEND);      
}
//Carga de texturas
public void loadGLTexture(GL10 gl, Context context) {
    //Generamos un puntero de texturas
    gl.glGenTextures(1, textures, 0);       
    //y se lo asignamos a nuestro array
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
    //Creamos filtros de texturas
    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);
    //Diferentes parametros de textura posibles GL10.GL_CLAMP_TO_EDGE
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);     
    /*
    String imagePath = "radiocd5.png";
    AssetManager mngr = context.getAssets();
    InputStream is=null;
    try {
        is = mngr.open(imagePath);
    } catch (IOException e1) {  e1.printStackTrace();   }
    */
    //Get the texture from the Android resource directory
    InputStream is=null;
    /*
    if (item.equals("rim"))
        is = context.getResources().openRawResource(R.drawable.rueda);
    else if (item.equals("selector"))
        is = context.getResources().openRawResource(R.drawable.selector);
    */      
    /*
    is = context.getResources().openRawResource(resourceId);
    Bitmap bitmap = null;
    try {
        bitmap = BitmapFactory.decodeStream(is);
    } finally {
        try {
            is.close();
            is = null;
        } catch (IOException e) {
        }
    }
    */
    Bitmap bitmap =image;       
    //con el siguiente código redimensionamos las imágenes que sean mas grandes de 256x256.
    int newW=bitmap.getWidth();
    int newH=bitmap.getHeight();
    float fact;
    if (newH>256 || newW>256)
    {
        if (newH>256)
        {
            fact=(float)255/(float)newH; //porcentaje por el que multiplicar para ser tamaño 256
            newH=(int)(newH*fact); //altura reducida al porcentaje necesario
            newW=(int)(newW*fact); //anchura reducida al porcentaje necesario   
        }
        if (newW>256)
        {
            fact=(float)255/(float)newW; //porcentaje por el que multiplicar para ser tamaño 256
            newH=(int)(newH*fact); //altura reducida al porcentaje necesario
            newW=(int)(newW*fact); //anchura reducida al porcentaje necesario
        }
        bitmap=Bitmap.createScaledBitmap(bitmap, newW, newH, true);
    }       
    //con el siguiente código transformamos imágenes no potencia de 2 en imágenes potencia de 2 (pot)
    //meto el bitmap NOPOT en un bitmap POT para que no aparezcan texturas blancas.
    int nextPot=256;
    int h = bitmap.getHeight();
    int w = bitmap.getWidth();
    int offx=(nextPot-w)/2; //distancia respecto a la izquierda, para que la imagen quede centrada en la nueva imagen POT
    int offy=(nextPot-h)/2; //distancia respecto a arriba, para que la imagen quede centrada en la nueva imagen POT
    Bitmap bitmap2 = Bitmap.createBitmap(nextPot, nextPot, Bitmap.Config.ARGB_8888); //crea un bitmap transparente gracias al ARGB_8888
    Canvas comboImage = new Canvas(bitmap2);
    comboImage.drawBitmap(bitmap, offx, offy, null);
    comboImage.save();

    //Usamos Android GLUtils para espcificar una textura de 2 dimensiones para nuestro bitmap
    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap2, 0);

    //Checkeamos si el GL context es versión 1.1 y generamos los Mipmaps por Flag. Si no, llamamos a nuestra propia implementación
    if(gl instanceof GL11) {
        gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_GENERATE_MIPMAP, GL11.GL_TRUE);
        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap2, 0);
    } else {
        buildMipmap(gl, bitmap2);
    }   
    //Limpiamos los bitmaps
    bitmap.recycle();
    bitmap2.recycle();
}
//Nuestra implementación de MipMap. Escalamos el bitmap original hacia abajo por factor de 2 y lo asignamos como nuevo nivel de mipmap
private void buildMipmap(GL10 gl, Bitmap bitmap) {
    int level = 0;
    int height = bitmap.getHeight();
    int width = bitmap.getWidth();
    while(height >= 1 || width >= 1) {
        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bitmap, 0);
        if(height == 1 || width == 1) {
            break;
        }
        level++;
        height /= 2;
        width /= 2;
        Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true);
        bitmap.recycle();
        bitmap = bitmap2;
    }
}
}

1 Ответ

11 голосов
/ 02 ноября 2011

gluProject делает то же самое, что и конвейер преобразования с фиксированной функцией:

  1. 3D-вершина расширяется до однородных координат, добавляя 1 какчетвертая координата: v[3]=1.

  2. Затем эта однородная вершина умножается на матрицу вида модели и матрицу проекции: v'=P*M*v.

  3. Затем наступает перцептивное разделение.Делив на четвертую координату, мы учитываем искажение перспективы (если у вас есть ортографическая проекция, например, с использованием glOrtho, тогда v'[3]==1, и нет искажения перспективы): v"=v'/v'[3].

  4. Теперь все в вашем объеме просмотра (видимой области вашей сцены) было преобразовано в нормализованные координаты устройства, куб [-1,1].Поэтому необходимо преобразовать это в экранные координаты [0, w] x [0, h]: x=w * (v"[0]+1) / 2 и y = h * (v"[1]+1) / 2.И, наконец, координата z преобразуется из [-1,1] в [0,1], чтобы получить нормализованное значение глубины, которое записывается в буфер глубины: z = (v"[2]+1) / 2.

Таким образом, ключом к пониманию того, что происходит со значением z, является осознание того, что расстояние до камеры (значение z в пространстве обзора) сначала преобразуется в диапазон [-1,1] с помощью матрицы проекции, в зависимости отближний дальний диапазон (ближний и дальний значения, которые вы вводите в glOrtho, glFrustum или gluPerspective).Затем это нормализованное значение преобразуется в диапазон [0,1], в результате чего получается окончательное значение глубины, которое записывается в буфер глубины и которое gluProject вычисляется как z-значение координат окна.

Такто, что вы на самом деле получили (0, 0, 0.5) - это нижний левый угол экрана с глубиной 0,5.С ортогональной матрицей (без каких-либо искажений перспективы) и матрицей представления тождественной модели она будет равна координате (left, bottom, (far-near)/2), где bottom, left, near и far - соответствующие аргументы, которые вы вводите ввызов функции glOrtho (или что-то подобное).Таким образом, вершина находится в середине ближней дальности и в левом нижнем углу громкости просмотра (как видно из камеры).Но это не будет иметь место для перспективной проекции, так как в этом случае преобразование из z-координаты пространства обзора в значение глубины не является линейным (хотя все еще монотонным, конечно).

Поскольку вы положилив вершине (-1, -1, 0) это может означать, что ваша матрица представления модели является тождественной, а ваша матрица проекции соответствует матрице, созданной с помощью glOrtho(-1, 1, -1, 1, -1, 1), которая также является почти единичной матрицей (хотя и с зеркальным значением z, но поскольку входные данные z0, вы можете этого не заметить).Так что, если это не те значения, которые вы ожидали (конечно, после понимания работы gluProject), это может быть просто из-за того, что ваши матрицы не были получены правильно, и вы просто получили идентификационные матрицы вместо вашего фактического вида моделии матрицы проекций.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...