物体的边缘在旋转时移动

时间:2014-01-23 12:09:40

标签: android opengl-es 3d opengl-es-2.0

我有一个以3d对象为特色的项目。用户可以用手指手势旋转和缩放它。我遇到的问题是在旋转立方体时(其他实体也会导致相同的错误,但我会在立方体上设置一个示例),它的边缘会崩溃(如屏幕截图所示)。旋转越快,问题就越严重。

我试图通过禁用面部剔除来掩盖它,以便用户看到立方体的内部,它基本上具有相同的颜色。然而,项目经理对该解决方案并不满意(我承认它不能很好地工作)。

立方体有6个面,每个面包含大约242个多边形。

以下是与渲染3d场景相关的代码片段(我正在使用VBO):

public void onDrawFrame(GL10 glUnused) 
{
    GLES20.glDisable(GLES20.GL_BLEND);
    isBlendingEnabled = false;

    Matrix.setLookAtM(mViewMatrix, 0, mEyeX, mEyeY, mEyeZ, mLookX, mLookY, mLookZ, mUpX, mUpY, mUpZ);


    if(!mSolid.equals(((ActivityMain) mContext).getSolid())){
        refreshSolid();
        refreshTextureData();
    }

    if(mSolid.hasChanged){
        refreshSolid();
    }

    if(mSolid.hasTexturesChanged){
        refreshTextureData();
        mSolid.hasTexturesChanged = false;
    }

    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);                    

    // Set per-vertex lighting program.
    GLES20.glUseProgram(mProgramHandle);

    // Set program handles for solid drawing.
    mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVPMatrix");
    mMVMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVMatrix"); 
    mLightPosHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_LightPos");
    mColorHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Color");
    mTextureUniformHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_Texture");
    mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position");        
    mNormalHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Normal"); 
    mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_TexCoordinate");                        

    // Calculate position of the light. Handle it's rotation
    Matrix.setIdentityM(mLightModelMatrix, 0);
    Matrix.translateM(mLightModelMatrix, 0, 0.0f, 0.0f, -2.0f);      
    Matrix.rotateM(mLightModelMatrix, 0, mAngleInDegrees, 0.0f, 1.0f, 0.0f);
    Matrix.translateM(mLightModelMatrix, 0, 0.0f, 0.0f, 3.5f);

    Matrix.multiplyMV(mLightPosInWorldSpace, 0, mLightModelMatrix, 0, mLightPosInModelSpace, 0);
    Matrix.multiplyMV(mLightPosInEyeSpace, 0, mViewMatrix, 0, mLightPosInWorldSpace, 0);                        

    //Doing that step for each of the faces
    for(int i = 0; i < mSolid.get3dMesh().size(); i++){
        // Draw a solid.
        // Translate the solid into the screen.

        Matrix.setIdentityM(mModelMatrix, 0);
        Matrix.translateM(mModelMatrix, 0, 0.0f, 0.0f, -3.5f);

        // Set a matrix that contains the current rotation.
        Matrix.setIdentityM(mCurrentRotation, 0);        
        Matrix.rotateM(mCurrentRotation, 0, mDeltaX, 0.0f, 1.0f, 0.0f);
        Matrix.rotateM(mCurrentRotation, 0, mDeltaY, 1.0f, 0.0f, 0.0f);

        //handle inertia
        if(Math.abs(mDeltaX) > 2f){
            mDeltaX = Math.signum(mDeltaX);
            mDeltaX = 0.5f*mDeltaX;
        }else if(Math.abs(mDeltaX) > 0.05f){
            mDeltaX = 0.99f*mDeltaX;
        }else
            mDeltaX = 0.0f;

        if(Math.abs(mDeltaY) > 2f){
            mDeltaY = Math.signum(mDeltaY);
            mDeltaY = 0.5f*mDeltaY;
        }else if(Math.abs(mDeltaY) > 0.05f){
            mDeltaY = 0.99f*mDeltaY;
        }else
            mDeltaY = 0.0f;

        // Multiply the current rotation by the accumulated rotation, and then set the accumulated rotation to the result.
        Matrix.multiplyMM(mTemporaryMatrix, 0, mCurrentRotation, 0, mAccumulatedRotation, 0);
        System.arraycopy(mTemporaryMatrix, 0, mAccumulatedRotation, 0, 16);

        // Rotate the cube taking the overall rotation into account.
        Matrix.multiplyMM(mTemporaryMatrix, 0, mModelMatrix, 0, mAccumulatedRotation, 0);
        System.arraycopy(mTemporaryMatrix, 0, mModelMatrix, 0, 16);

        // Set the active texture unit to texture unit 0.
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        // Bind the texture to this unit.
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle[i]);

        // Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
        GLES20.glUniform1i(mTextureUniformHandle, 0);

        drawCube(i);
    }
    if(ModelCorePrefs.getDrawMesh())
        drawVertices();

    GLES20.glEnable(GLES20.GL_BLEND);
    GLES20.glBlendFunc (GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);    
    isBlendingEnabled = true;

    Matrix.setIdentityM(mModelMatrix, 0);
    Matrix.translateM(mModelMatrix, 0, 0.0f, -3.5f, -3.5f);
    Matrix.scaleM(mModelMatrix, 0, 2f, 1.0f, 2f);

    // Set the active texture unit to texture unit 0.
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    // Bind the texture to this unit.
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle[mSolid.get3dMesh().size()]);

    // Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
    GLES20.glUniform1i(mTextureUniformHandle, 0);

    drawShadow();
}

上面使用的drawCube()方法:

private void drawCube(int i)
{   
    // Pass in the position information
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mCubePositionsBufferIdx[i]);
    GLES20.glEnableVertexAttribArray(mPositionHandle);                       
    GLES20.glVertexAttribPointer(mPositionHandle, mPositionDataSize, GLES20.GL_FLOAT, false, 0, 0);

    // Pass in the color information
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mCubeColorsBufferIdx[i]);
    GLES20.glEnableVertexAttribArray(mColorHandle);
    GLES20.glVertexAttribPointer(mColorHandle, mColorDataSize, GLES20.GL_FLOAT, false, 0, 0);

    // Pass in the normal information
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mCubeNormalsBufferIdx[i]);
    GLES20.glEnableVertexAttribArray(mNormalHandle);                
    GLES20.glVertexAttribPointer(mNormalHandle, mNormalDataSize, GLES20.GL_FLOAT, false, 0, 0);

    // Pass in the texture coordinate information
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mCubeTexCoordsBufferIdx[i]);
    GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
    GLES20.glVertexAttribPointer(mTextureCoordinateHandle, mTextureCoordinateDataSize, GLES20.GL_FLOAT, false, 0, 0);

    // This multiplies the view matrix by the model matrix, and stores the result in the MVP matrix
    // (which currently contains model * view).
    Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);   

    // Pass in the modelview matrix.
    GLES20.glUniformMatrix4fv(mMVMatrixHandle, 1, false, mMVPMatrix, 0);                

    // This multiplies the modelview matrix by the projection matrix, and stores the result in the MVP matrix
    // (which now contains model * view * projection).        
    Matrix.multiplyMM(mTemporaryMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
    System.arraycopy(mTemporaryMatrix, 0, mMVPMatrix, 0, 16);

    // Pass in the combined matrix.
    GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);

    // Pass in the light position in eye space.        
    GLES20.glUniform3f(mLightPosHandle, mLightPosInEyeSpace[0], mLightPosInEyeSpace[1], mLightPosInEyeSpace[2]);

    // Draw the cube.
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, mSolid.get3dMesh().get(i).length/3);                               
}

问题的截图:

Diagram

1 个答案:

答案 0 :(得分:1)

我强烈建议您从glGet*Location (...)方法中移除onDrawFrame (...)来电。最初链接程序后,这些内容不会改变,并且每个框架按字符串名称搜索这些位置会妨碍您的性能。同样,您只需要为每个程序设置一次采样器均匀,因为它始终使用纹理图像单元0。

至于你的真实问题:

我没有看到循环中每个面都有自己独特的模型矩阵的证据,所以你应该移动逻辑,将矩阵逐步转换出循环。否则,您将单独旋转多维数据集中的每个面,这是您的图表所显示的。说实话,我不知道你为什么要一次画一张脸。

// Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
GLES20.glUniform1i(mTextureUniformHandle, 0);
// ^^^^^ ONLY DO THAT ONCE!


// Translate the solid into the screen.
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, 0.0f, 0.0f, -3.5f);

// Set a matrix that contains the current rotation.
Matrix.setIdentityM(mCurrentRotation, 0);        
Matrix.rotateM(mCurrentRotation, 0, mDeltaX, 0.0f, 1.0f, 0.0f);
Matrix.rotateM(mCurrentRotation, 0, mDeltaY, 1.0f, 0.0f, 0.0f);

//handle inertia
if(Math.abs(mDeltaX) > 2f){
    mDeltaX = Math.signum(mDeltaX);
    mDeltaX = 0.5f*mDeltaX;
}else if(Math.abs(mDeltaX) > 0.05f){
    mDeltaX = 0.99f*mDeltaX;
}else
    mDeltaX = 0.0f;

if(Math.abs(mDeltaY) > 2f){
    mDeltaY = Math.signum(mDeltaY);
    mDeltaY = 0.5f*mDeltaY;
}else if(Math.abs(mDeltaY) > 0.05f){
    mDeltaY = 0.99f*mDeltaY;
}else
    mDeltaY = 0.0f;

// Multiply the current rotation by the accumulated rotation, and then set the accumulated rotation to the result.
Matrix.multiplyMM(mTemporaryMatrix, 0, mCurrentRotation, 0, mAccumulatedRotation, 0);
System.arraycopy(mTemporaryMatrix, 0, mAccumulatedRotation, 0, 16);

// Rotate the cube taking the overall rotation into account.
Matrix.multiplyMM(mTemporaryMatrix, 0, mModelMatrix, 0, mAccumulatedRotation, 0);
System.arraycopy(mTemporaryMatrix, 0, mModelMatrix, 0, 16);

//Doing that step for each of the faces
for(int i = 0; i < mSolid.get3dMesh().size(); i++){
    // Draw a solid.

    // Set the active texture unit to texture unit 0.
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    // Bind the texture to this unit.
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle[i]);

    drawCube(i);
}