我想在游戏中的 3D 模型上叠加一个图标。我正在使用 gluProject 获取模型中心点的屏幕坐标,然后使用该数据在自定义视图上绘制图标:
在我的渲染器类中:
private void projectScreenCoords(GLSurfaceView view, GraphicsEntity ge, GL10 gl){
MatrixGrabber matrixGrabber = new MatrixGrabber();
matrixGrabber.getCurrentModelView(gl);
float[] modelMat = matrixGrabber.mModelView;
matrixGrabber.getCurrentProjection(gl);
float[] projMat = matrixGrabber.mProjection;
gl.glMatrixMode(GL10.GL_MODELVIEW);
// view port
int[] viewport = new int[]{view.getTop(),view.getLeft(),view.getWidth(),view.getHeight()};
float[] vector = new float[4];
GLU.gluProject(ge.getTransform().tx, ge.getTransform().ty, ge.getTransform().tz, modelMat, 0, projMat, 0, viewport, 0, vector, 0);
ge.setScreenCoords(vector[0], viewport[3] - vector[1]);
}
和我的自定义视图:
protected void onDraw (Canvas canvas){
Vector<GraphicsEntity> scene = renderer.getForegroundScene();
for(int i = 0;i<scene.size();i++){
GraphicsEntity ge = scene.get(i);
float[] xy = ge.getScreenCoords();
if(xy[0]>-1 && xy[1]>-1){
canvas.drawBitmap(bitmap, xy[0]-bitmapWidth/2, xy[1]-bitmapHeight/2, null);
}
invalidate();
}
}
我在哪里设置我的投影矩阵:
protected void setProjectionMatrix(GL10 gl){
float ratio = (float) viewportWidth / viewportHeight;
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glFrustumf(-ratio*zoom, ratio*zoom, -1*zoom, 1*zoom, nearPlane, farPlane);
}
但是,离屏幕中心越远,图标绘制的距离模型中心越远:
显然,如果只是视口大小不正确(由于顶部/底部的条),那么图标将仅在 y 轴上关闭,但在 x 轴上也将关闭。我错过了什么/做错了什么?此外,我还没有使用 gluProject 返回的第三个值,但它的值始终为 0.7,所以我不太确定它会如何使用?
使用 SDK 版本 7,我在多个设备(运行 android 2.1 的 ZTE Blade 和运行 2.3.4 的 Kindle Fire)上进行了测试,结果相同。viewportWidth/Height 和 view.getWidth/Height() 相同,并且 view.getTop/Left() 返回 0。MatrixGrabber 代码适用于 gluUnProject,所以我有理由相信这不是问题
编辑:这是我的 GL 相关绘图代码的其余部分:
在渲染器中:
// camera variables
protected float FOV = 60.0f;
protected float nearPlane = 3;
protected float farPlane = 7;
protected float eyeX = 0;
protected float eyeY = 0;
protected float eyeZ = 0;
protected float centreX = 0;
protected float centreY = 0;
protected float centreZ = ((farPlane - nearPlane) / 2) + nearPlane;
protected float upX = 0;
protected float upY = 1;
protected float upZ = 0;
protected float viewportWidth;
protected float viewportHeight;
// user camera control variables
protected float zoom = 1;
protected float rotatedX = 0;
protected float rotatedY = 0;
protected boolean zoomed = true;
protected TrackingTransform tracking;
protected void setWorldTransform(GL10 gl){
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
// transforms the centre position to 0,0,0
gl.glTranslatef(-centreX, -centreY, -centreZ);
}
protected void setModelRotation(GL10 gl, GraphicsEntity ge){
if(ge.isTrackerball()){
gl.glRotatef(rotatedX, 1.0f, 0, 0);
gl.glRotatef(rotatedY, 0, -1.0f, 0);
if(tracking!=null){
synchronized(tracking){
tracking.transform(gl);
}
}
} else if(ge.isBackground()){
gl.glTranslatef(centreX, centreY, centreZ);
gl.glRotatef(rotatedX, 1.0f, 0, 0);
gl.glRotatef(rotatedY, 0, -1.0f, 0);
if(ge.isSkybox()==true){
ge.getTransform().sx = nearPlane + 1.0f;
ge.getTransform().sy = nearPlane + 1.0f;
ge.getTransform().sz = nearPlane + 1.0f;
ge.getTransform().tx = 0;
ge.getTransform().ty = 0;
ge.getTransform().tz = 0;
}
}
}
protected void setModelViewMatrix(GL10 gl){
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
// not used:
//GLU.gluLookAt(gl, eyeX, eyeY, eyeZ, centreX, centreY, centreZ, upX, upY, upZ);
}
@Override
public void onDrawFrame(GL10 gl) {
// Set up various things before drawing
gl.glFrontFace(GL10.GL_CW);
gl.glEnable(GL10.GL_CULL_FACE);
gl.glCullFace(GL10.GL_FRONT);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glEnable(GL10.GL_DEPTH_TEST);
// change projection matrix
float oldzoom = zoom;
zoom = 1.0f;
setProjectionMatrix(gl);
zoom = oldzoom;
// set global world transform (also changes to modelview)
setWorldTransform(gl);
// loop through and draw background models
for(int i = 0;i<backgroundGraphicsEntities.size();i++){
GraphicsEntity ge = backgroundGraphicsEntities.get(i);
SimpleTransform t = ge.getTransform();
int modelIndex = ge.getModelIndex();
if(modelIndex>=0){
gl.glPushMatrix();
setModelRotation(gl, ge);
t.transform(gl);
if(ge.isSprite() && gl11flag){
Sprite s = sprites.get(modelIndex);
s.draw((GL11) gl, ge);
} else {
Model m = models.get(modelIndex);
if(m!=null){
m.draw(gl);
}
}
gl.glPopMatrix();
}
if(i==0 && ge.isSkybox()){
// if skybox, reset depth bit
gl.glClear(GL10.GL_DEPTH_BUFFER_BIT);
}
}
gl.glClear(GL10.GL_DEPTH_BUFFER_BIT);
// change projection matrix (if zoomed)
setProjectionMatrix(gl);
// change back to modelview
gl.glMatrixMode(GL10.GL_MODELVIEW);
// loop through and draw models
for(int i = 0;i<graphicsEntities.size();i++){
GraphicsEntity ge = graphicsEntities.get(i);
SimpleTransform t = ge.getTransform();
int modelIndex = ge.getModelIndex();
if(modelIndex>=0){
gl.glPushMatrix();
setModelRotation(gl, ge);
t.transform(gl);
if(ge.isSprite() && gl11flag){
Sprite s = sprites.get(modelIndex);
s.draw((GL11) gl, ge);
} else {
Model m = models.get(modelIndex);
if(m!=null){
m.draw(gl);
}
if(projectScreenCoords){
projectScreenCoords(glSurfaceView, ge, gl);
}
}
gl.glPopMatrix();
}
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
viewportWidth = width;
viewportHeight = height;
gl.glViewport(0, 0, width, height);
setProjectionMatrix(gl);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
if(gl instanceof GL11){
gl11flag = true;
}
gl.glClearColor(0, 0, 0, 1);
gl.glShadeModel(GL10.GL_SMOOTH);
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glDepthMask(true);
//gl.glClearDepthf(1.0f);
gl.glDepthFunc(GL10.GL_LEQUAL);
//gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
setProjectionMatrix(gl);
}
然后在 SimpleTransform 中(即调用 ge.getTransform().transform(gl) 时调用的内容):
public void transform(GL10 gl) {
gl.glTranslatef(tx, ty, tz);
gl.glRotatef(rz, 0, 0, 1);
gl.glRotatef(ry, 0, 1, 0);
gl.glRotatef(rx, 1, 0, 0);
gl.glScalef(sx, sy, sz);
}
对于 TrackingTransform:
@Override
public void transform(GL10 gl) {
gl.glTranslatef(-tx, -ty, -tz);
}
最后在 model.draw() 中:
public void draw(GL10 gl){
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// Pass the vertex buffer in
gl.glVertexPointer(3, GL10.GL_FLOAT, 0,
vertices);
int textureID = material.getTexture().getTextureID();
if(textureID>=0){
// Enable Textures
gl.glEnable(GL10.GL_TEXTURE_2D);
// Get specific texture.
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureID);
// Use UV coordinates.
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
// Pass in texture coordinates
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureCoordinates);
}
// Pass in vertex normals
gl.glNormalPointer(GL10.GL_FLOAT, 0, normals);
gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
gl.glDrawElements(GL10.GL_TRIANGLES, numindices,GL10.GL_UNSIGNED_SHORT, indices);
gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
if(textureID>=0){
// Disable buffers
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
}