我需要显示一个100%屏幕宽度的正方形多边形,然后,我假设我必须缩放它(用z轴),直到多边形边框突出屏幕边框。
我正在尝试使用gluproject将三维坐标投影到二维屏幕坐标中。如果屏幕坐标为0或与宽度或高度匹配,则它接触屏幕边框。
问题是出了问题,gluproject返回的outputCoords
数组给了我这些值:0,0,0.5,但是我的平方是以sreen为中心的,z=-5.0f!!!!!
我不明白这些价值观…
这是我用来在屏幕上获得正方形多边形的二维投影的代码:
这段代码在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作为第四个坐标:v[3]=1
,将三维顶点展开为齐次坐标。
然后,这个同质顶点乘以modelview矩阵和投影矩阵:v'=P*M*v
。
然后是迫害部门。通过除以第四个坐标,我们可以解释透视失真(如果您有一个正交投影,例如使用glOrtho
,然后v'[3]==1
,并且没有透视失真):v"=v'/v'[3]
。
现在,你的观察体积(场景的可见区域)中的所有东西都转换成了标准化的设备坐标,即[-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.5。对于一个正交矩阵(没有任何透视失真)和一个identity modelview矩阵,这将等于一个坐标(0, 0, 0.5)
,其中(left, bottom, (far-near)/2)
,bottom
,left
和near
是您放入far
函数调用(或具有类似功能的东西)的相应参数。所以顶点在近远范围的中间,在观察体积的左下角(从相机看)。但对于透视投影来说,这是不成立的,因为在这种情况下,从视图空间z坐标到深度值的转换不是线性的(当然,仍然是单调的)。
由于您放入了顶点glOrtho
,这可能意味着您的modelview矩阵是identity,而您的投影矩阵对应于用(-1, -1, 0)
创建的矩阵,该矩阵也几乎是identity矩阵(尽管具有镜像的z值,但由于输入z为0,您可能不会注意到它)。因此,如果这些不是您应该等待的值(当然,在理解glOrtho(-1, 1, -1, 1, -1, 1)
的工作原理之后),也可能是您的矩阵没有被正确检索,您只是得到了标识矩阵,而不是实际的模型视图和投影矩阵。
所以我认为您的gluProject
函数没有问题。您还可以查看this question的答案,进一步了解opengl的默认转换管道。虽然随着顶点着色器的出现,某些阶段的计算方式有所不同,但通常仍遵循惯用的“模型”->“视图”->“投影”方法。