android - gluProject 函数如何工作?我不明白

标签 android opengl-es

我需要显示一个占屏幕宽度 100% 的正方形多边形,然后,我想我必须缩放它(使用 Z 轴)直到多边形边界触及屏幕边界。

我正在尝试使用 gluProject 将 3D 坐标投影到 2D 屏幕坐标中来实现此目的。如果屏幕坐标为 0 或与宽度或高度匹配,则它正在触摸屏幕边框。

问题是出了点问题,gluProject 返回的 outputCoords 数组为我提供了这些值:0,0,0.5,但我的正方形位于屏幕中心,并且 Z= -5.0f!!!!

我不明白这些值(value)观...

这是我用来获取我的方形多边形在屏幕上的二维投影的代码:

这段代码在 GLSurfaceView 类的 onSurfaceCreated 方法中,是否必须放在另一个方法中?在哪里?

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

最佳答案

gluProject与固定功能转换管道完全相同:

  1. 通过附加 1 作为第四个坐标,将 3D 顶点扩展为齐次坐标:v[3]=1

  2. 然后将这个齐次顶点乘以模型 View 矩阵和投影矩阵: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 值发生了什么的关键是要认识到,到相机的距离( View 空间中的 z 值)首先被投影矩阵转换为 [-1,1] 范围,具体取决于在远近范围上(您输入 glOrthoglFrustumgluPerspective 的近远值)。然后,将此归一化值转换为 [0,1] 范围,以生成写入深度缓冲区的最终深度值,gluProject 计算为窗口坐标的 z 值。

所以你实际得到的 (0, 0, 0.5) 是屏幕的左下角,深度为 0.5。使用正交矩阵(没有任何透视失真)和恒等模型 View 矩阵,这将等于 (left, bottom, (far-near)/2) 的坐标,其中 bottomleftnearfar 是您放入 glOrtho 函数调用(或具有类似功能的东西)。因此顶点位于近远范围的中间,并且位于视域的左下角(从相机上看)。但这不适用于透视投影,因为在这种情况下,从 View 空间 z 坐标到深度值的转换不是线性的(当然仍然是单调的)。

由于您放入了顶点 (-1, -1, 0),这可能意味着您的模型 View 矩阵是恒等矩阵,而您的投影矩阵对应于使用 glOrtho(- 1, 1, -1, 1, -1, 1),它也接近于单位矩阵(尽管具有镜像的 z 值,但由于输入 z 为 0,您可能不会注意到它)。因此,如果这些不是您所期待的值(当然是在理解了 gluProject 的工作原理之后),也可能只是您的矩阵没有被正确检索而您只是得到了单位矩阵而不是您实际的模型 View 和投影矩阵。

所以我认为您的gluProject 函数没有任何问题。您还可以查看 this question 的答案以更深入地了解 OpenGL 的默认转换管道。尽管随着顶点着色器的出现,某些阶段的计算方式有所不同,但您通常仍会遵循惯用的模型 -> View -> 投影方法。

关于android - gluProject 函数如何工作?我不明白,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7980430/

相关文章:

java - 将 Android 传感器 TYPE_ORIENTATION 转换为旋转矩阵

android - 使用倾斜传感器的 Arduino 程序中的正确功能顺序

android - 每当我在 git 中提交时,如何清除 $HOME/.gradle/caches/?

android - 将视频从 Android 上传到服务器?

java - 使用 Canvas.drawText 从位图上的 TextView 获取错误的 Y 坐标

opengl-es - 如何在 Swift 中通过 OpenGL 显示 RGB 纹理?

android - 使用 libgdx 和 OpenGL ES 制作橡皮擦效果

java - 为什么我的 OpenGL 坐标系轴不是 [-1; 1]?

java - 防止 Spite 动画随着 openGL ES 中的背景滚动

java - 使用 AsyncTask 时屏幕闪烁