6

我正在opengl ES2.0中开发一个android应用程序。在这个应用程序中,我曾经在GL surfaceView中通过触摸事件绘制多条线和圆。

由于 opengl 依赖于 GPU,目前它在 Google Nexus 7(ULP GeForce)中运行良好。

在三星 Galaxy Note 2(MALI 400MP)中,我试图绘制多条线,但它会清除前一行并将当前行绘制为新行。

在 Sony Xperia Neo V(Adreno 205) 中,我试图画一条新线,它使表面崩溃,如下图所示。在此处输入图像描述

是否可以让它在所有设备上运行,还是我需要为单个 GPU 编写代码?


源代码

MainActivity.java

//in OnCreate method of my activity, i set the glsurfaceview and renderer

final ActivityManager activityManager =
    ( ActivityManager ) getSystemService( Context.ACTIVITY_SERVICE );
final ConfigurationInfo configurationInfo =
    activityManager.getDeviceConfigurationInfo(  );
final boolean supportsEs2 = ( configurationInfo.reqGlEsVersion >= 0x20000
                  || Build.FINGERPRINT.startsWith( "generic" ) );

if( supportsEs2 ) {
    Log.i( "JO", "configurationInfo.reqGlEsVersion:"
           + configurationInfo.reqGlEsVersion + "supportsEs2:"
           + supportsEs2 );
// Request an OpenGL ES 2.0 compatible context.
    myGlsurfaceView.setEGLContextClientVersion( 2 );

    final DisplayMetrics displayMetrics = new DisplayMetrics(  );
    getWindowManager(  ).getDefaultDisplay(  ).getMetrics( displayMetrics );

// Set the renderer to our demo renderer, defined below.
    myRenderer = new MyRenderer( this, myGlsurfaceView );
    myGlsurfaceView.setRenderer( myRenderer, displayMetrics.density );
    myGlsurfaceView.setRenderMode( GLSurfaceView.RENDERMODE_CONTINUOUSLY );

    MyGLSurfaceView.java
//in this im getting the coordinates of my touch on the glSurfaceView to draw the line and //passing those points to the renderer class
        public MyGLsurfaceview( Context context ) {
        super( context );
        Log.i( "JO", "MyGLsurfaceview1" );

    }

    public MyGLsurfaceview(
    Context context,
    AttributeSet attrs )
    {
        super( context, attrs );
        con = context;
        mActivity = new MainActivity(  );
        mActivity.myGlsurfaceView = this;
        Log.i( "JO", "MyGLsurfaceview2" );
    }

    public void setRenderer(
    MyRenderer renderer,
    float density )
    {
        Log.i( "JO", "setRenderer" );
        myRenderer = renderer;
        myDensity = density;
        mGestureDetector = new GestureDetector( con, mGestureListener );
        super.setRenderer( renderer );
        setRenderMode( GLSurfaceView.RENDERMODE_CONTINUOUSLY );

    }
    @Override public boolean onTouchEvent( MotionEvent ev ) {

        boolean retVal = mGestureDetector.onTouchEvent( ev );

        if( myline ) {

            switch ( ev.getAction(  ) ) {

            case MotionEvent.ACTION_DOWN:

                isLUp = false;

                if( count == 1 ) {
                    dx = ev.getX(  );
                    dy = ev.getY(  );
                    dx = ( dx / ( getWidth(  ) / 2 ) ) - 1;
                    dy = 1 - ( dy / ( getHeight(  ) / 2 ) );

                    firstX = dx;
                    firstY = dy;
                } else if( count == 2 ) {

                    ux = ev.getX(  );
                    uy = ev.getY(  );
                    ux = ( ux / ( getWidth(  ) / 2 ) ) - 1;
                    uy = 1 - ( uy / ( getHeight(  ) / 2 ) );

                    secondX = ux;
                    secondY = uy;

                    myRenderer.dx = firstX;
                    myRenderer.dy = firstY;
                    myRenderer.ux = secondX;
                    myRenderer.uy = secondY;

                    midX = ( firstX + secondX ) / 2;
                    midY = ( firstY + secondY ) / 2;
                    Log.e( "JO",
                           "Line:firstX" + firstX +
                           "firstY" + firstY );
                    lp = new LinePoints( firstX, firstY,
                                 secondX, secondY,
                                 midX, midY );
                    lineArray.add( lp );

                    myRenderer.isNewClick = false;
                    myRenderer.isEnteredAngle = false;
                    myRenderer.myline = true;
                    myRenderer.mycircle = false;
                    myRenderer.mydashedline = false;
                    myRenderer.eraseCircle = false;
                    myRenderer.eraseLine = false;
                    myRenderer.eraseSelCir = false;
                    myRenderer.angle = angle;
                    myRenderer.length = length;
                    requestRender(  );
                    count = 0;

                }
                count++;

                break;
            case MotionEvent.ACTION_MOVE:

                isLUp = true;

                break;

            case MotionEvent.ACTION_UP:

                if( isLUp ) {

                    ux = ev.getX(  );
                    uy = ev.getY(  );
                    ux = ( ux / ( getWidth(  ) / 2 ) ) - 1;
                    uy = 1 - ( uy / ( getHeight(  ) / 2 ) );
                    Log.i( "JO", "line2:" + ux + "," + uy );

                    secondX = ux;
                    secondY = uy;
                    myRenderer.dx = firstX;
                    myRenderer.dy = firstY;
                    myRenderer.ux = secondX;
                    myRenderer.uy = secondY;

                    midX = ( firstX + secondX ) / 2;
                    midY = ( firstY + secondY ) / 2;
                    Log.e( "JO",
                           "Line:firstX" + firstX +
                           "firstY" + firstY );
                    lp = new LinePoints( firstX, firstY,
                                 secondX, secondY,
                                 midX, midY );
                    lineArray.add( lp );

                    myRenderer.isNewClick = false;
                    myRenderer.isEnteredAngle = false;
                    myRenderer.myline = true;
                    myRenderer.mycircle = false;
                    myRenderer.mydashedline = false;
                    myRenderer.mysnaptoedge = false;
                    myRenderer.mysnaptoMiddle = false;
                    myRenderer.eraseCircle = false;
                    myRenderer.eraseLine = false;
                    myRenderer.eraseSelCir = false;
                    count = 1;
                    requestRender(  );
                }

                break;

            }
        }
    }
}

MyRenderer.java

//renderer class to render the line to the glsurfaceview
Lines line;
public MyRenderer(
    MainActivity mainActivity,
    MyGLsurfaceview myGlsurfaceView )
{
    Log.i( "JO", "MyRenderer" );
    this.main = mainActivity;
    myGlsurface = myGlsurfaceView;

}

public void onDrawFrame(
    GL10 gl )
{
    line.draw( dx, dy, ux, uy );
}

@Override public void onSurfaceCreated(
    GL10 gl,
    EGLConfig config )
{
    Log.i( "JO", "onSurfaceCreated" );
// Set the background frame color
    GLES20.glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
// Create the GLText
    glText = new GLText( main.getAssets(  ) );

// Load the font from file (set size + padding), creates the texture
// NOTE: after a successful call to this the font is ready for
// rendering!
    glText.load( "Roboto-Regular.ttf", 14, 2, 2 );  // Create Font (Height: 14
// Pixels / X+Y Padding
// 2 Pixels)
// enable texture + alpha blending
    GLES20.glEnable( GLES20.GL_BLEND );
    GLES20.glBlendFunc( GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA );
}

@Override public void onSurfaceChanged(
    GL10 gl,
    int width,
    int height )
{
// Adjust the viewport based on geometry changes,
// such as screen rotation
    GLES20.glViewport( 0, 0, width, height );

    ratio = ( float ) width / height;

    width_surface = width;
    height_surface = height;

/*
* // this projection matrix is applied to object coordinates // in the
* onDrawFrame() method Matrix.frustumM(mProjMatrix, 0, -ratio, ratio,
* -1, 1, 3, 7);
*/
// Take into account device orientation
    if( width > height ) {
        Matrix.frustumM( mProjMatrix, 0, -ratio, ratio, -1, 1, 1, 10 );
    } else {
        Matrix.frustumM( mProjMatrix, 0, -1, 1, -1 / ratio, 1 / ratio,
                 1, 10 );
    }

// Save width and height
    this.width = width; // Save Current Width
    this.height = height;   // Save Current Height

    int useForOrtho = Math.min( width, height );

// TODO: Is this wrong?
    Matrix.orthoM( mVMatrix, 0, -useForOrtho / 2, useForOrtho / 2,
               -useForOrtho / 2, useForOrtho / 2, 0.1f, 100f );
}

线.java

//Line class to draw line

public class Lines
{

    final String vertexShaderCode = "attribute vec4 vPosition;"
        + "void main() {" + " gl_Position = vPosition;" + "}";

    final String fragmentShaderCode = "precision mediump float;"
        + "uniform vec4 vColor;" + "void main() {"
        + " gl_FragColor = vColor;" + "}";

    final FloatBuffer vertexBuffer;
    final int mProgram;
    int mPositionHandle;
    int mColorHandle;

// number of coordinates per vertex in this array
    final int COORDS_PER_VERTEX = 3;
    float lineCoords[] = new float[6];
    final int vertexCount = lineCoords.length / COORDS_PER_VERTEX;
    final int vertexStride = COORDS_PER_VERTEX * 4; // bytes per vertex
// Set color with red, green, blue and alpha (opacity) values
    float lcolor[] = { 1.0f, 1.0f, 1.0f, 1.0f };

    public Lines(
         )
    {

// initialize vertex byte buffer for shape coordinates
        ByteBuffer bb = ByteBuffer.allocateDirect(
// (number of coordinate values * 4 bytes per float)
                                  lineCoords.
                                  length * 4 );
// use the device hardware's native byte order
        bb.order( ByteOrder.nativeOrder(  ) );

// create a floating point buffer from the ByteBuffer
        vertexBuffer = bb.asFloatBuffer(  );

// prepare shaders and OpenGL program
        int vertexShader =
            MyRenderer.loadShader( GLES20.GL_VERTEX_SHADER,
                           vertexShaderCode );
        int fragmentShader =
            MyRenderer.loadShader( GLES20.GL_FRAGMENT_SHADER,
                           fragmentShaderCode );

        mProgram = GLES20.glCreateProgram(  );  // create empty OpenGL Program
        GLES20.glAttachShader( mProgram, vertexShader );    // add the vertex shader
// to program
        GLES20.glAttachShader( mProgram, fragmentShader );  // add the fragment
// shader to program
        GLES20.glLinkProgram( mProgram );   // create OpenGL program executables
    }

    public void draw(
    float dX,
    float dY,
    float uX,
    float uY )
    {

        lineCoords[0] = dX;
        lineCoords[1] = dY;
        lineCoords[2] = 0.0f;
        lineCoords[3] = uX;
        lineCoords[4] = uY;
        lineCoords[5] = 0.0f;
        Log.i( "JO",
               "lineCoords:" + lineCoords[0] + "," + lineCoords[1] +
               "," + lineCoords[3] + "," + lineCoords[4] );

        vertexBuffer.put( lineCoords );
        vertexBuffer.position( 0 );
// Add program to OpenGL environment
        GLES20.glUseProgram( mProgram );

// get handle to vertex shader's vPosition member
        mPositionHandle =
            GLES20.glGetAttribLocation( mProgram, "vPosition" );

// Enable a handle to the triangle vertices
        GLES20.glEnableVertexAttribArray( mPositionHandle );

// Prepare the triangle coordinate data
        GLES20.glVertexAttribPointer( mPositionHandle,
                          COORDS_PER_VERTEX,
                          GLES20.GL_FLOAT, false,
                          vertexStride, vertexBuffer );

// get handle to fragment shader's vColor member
        mColorHandle =
            GLES20.glGetUniformLocation( mProgram, "vColor" );

// Set color for drawing the triangle
        GLES20.glUniform4fv( mColorHandle, 1, lcolor, 0 );
        GLES20.glLineWidth( 3 );
// Draw the triangle
        GLES20.glDrawArrays( GLES20.GL_LINES, 0, vertexCount );

// Disable vertex array
        GLES20.glDisableVertexAttribArray( mPositionHandle );
    }

}
4

5 回答 5

4

好的,又来了:^1

OpenGL 不是场景图。OpenGL 不维护场景,不了解对象或跟踪几何。OpenGL 是一个绘图API。你给它一个画布(以窗口或 PBuffer 的形式)并命令它绘制点、线或三角形,OpenGL 正是这样做的。一旦绘制了一个图元(=点、线、三角形),OpenGL 就不会再回忆它了。如果有什么变化,你必须重新绘制整个东西。

重绘场景的正确步骤是:

  1. 禁用模板测试,以便以下步骤在整个窗口上运行。

  2. 使用 清除帧缓冲区glClear(bits),其中bits是一个位掩码,指定要清除画布的哪些部分。渲染新帧时,您要清除所有内容bits = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT

  3. 设置视口,构建合适的投影矩阵

  4. 为场景中的每个对象加载正确的模型视图矩阵,设置制服,选择顶点数组并进行绘图调用。

  5. 通过刷新管道完成渲染。如果使用单缓冲窗口glFinish(),如果使用双缓冲窗口调用SwapBuffers。在更高级别的框架的情况下,这可以由框架执行。

重要一旦在双缓冲窗口上完成绘图,您就不能继续发送绘图操作,因为通过执行缓冲区交换,您正在绘制的后台缓冲区的内容是未定义的。因此,您必须重新开始绘图,从清除帧缓冲区开始(步骤 1 和 2)。

您的代码缺少的正是这两个步骤。此外,我的印象是您正在执行 OpenGL 绘图调用以直接响应输入事件,可能在输入事件处理程序本身中。不要这样做!. 而是使用输入事件添加到要绘制的基元列表(在您的情况下为线),然后发送重绘事件,这使得框架调用绘图函数。在绘图函数中迭代该列表以绘制所需的线条。

在 OpenGL 中重绘整个场景是规范的!


[1] (天啊,我已经厌倦了每隔 3 个问题就写这个……)

于 2013-06-21T14:32:35.837 回答
3

在这里赌一把,但你真的清除过屏幕吗?您看到的行为类型表明您不是,并且在不同的场景中您会看到不同的错误 - 未初始化的内存、重用旧缓冲区、隐式清除等。

GL 要求您具体说明您想要什么,因此您需要明确明确。

于 2013-06-21T08:57:52.440 回答
1

OpenGL只是一个标准。API 的实际实现取决于显卡制造商。所以是的,OpenGL 开发有时可能依赖于 GPU。但是,所有实现都应该提供相同的结果(幕后发生的事情可能真的不同)。如果您的代码使用不同的 GPU 给出不同的结果,则 OpenGL 实现中可能存在版本差异。

您可以使用这些函数来获取支持的 OpenGL 版本:

glGetIntegerv​(GL_MAJOR_VERSION​, *); //version 3.0+
glGetIntegerv​(GL_MINOR_VERSION​, *); //version 3.0+
glGetString​(GL_VERSION​); //all versions
于 2013-06-21T08:10:03.727 回答
0

从我对类似问题的回答交叉发布为什么我的 opengl 输出因各种设备而异?

我们应该在编码时考虑 GPU 吗?没办法,OpenGL API 是您的应用程序和硬件之间的一层。

这对于桌面图形在很大程度上是正确的,因为所有 GPU 都是即时渲染器,但是,在移动图形中并非如此。

Mali GPU 使用基于图块的即时模式渲染。对于这种类型的渲染,帧缓冲区被分成大小为 16 x 16 像素的图块。多边形列表生成器 (PLB) 将来自应用程序的输入数据组织到多边形列表中。每个图块都有一个多边形列表。当一个图元覆盖一个图块的一部分时,一个称为多边形列表命令的条目被添加到该图块的多边形列表中。像素处理器获取一个图块的多边形列表,并在开始处理下一个图块之前计算该图块中所有像素的值。由于这种基于切片的方法使用快速的片上切片缓冲区,因此 GPU 仅在每个切片末尾将切片缓冲区内容写入主存储器中的帧缓冲区。基于非平铺的即时模式渲染器通常需要更多的帧缓冲区访问。

另一个区别是渲染缓冲区的处理。即时渲染器将“保存”缓冲区的内容,有效地允许您仅在先前存在的内容之上绘制渲染场景中的差异。但是,此 IS 在 Mali 中可用,但默认情况下未启用,因为如果使用不当可能会导致不良影响。

有一个 Mali GLES2 SDK 示例,说明如何在 GLES2 SDK 中正确使用“EGL Preserve”

基于 Geforce ULP 的 nexus 7 按预期工作的原因是,作为基于即时的渲染器,它默认保留缓冲区,而 Mali 没有。

来自 Khronos EGL 规范:

EGL_SWAP_BEHAVIOR

指定使用 eglSwapBuffers 发布表面对颜色缓冲区的影响。EGL_BUFFER_PRESERVED 值表示颜色缓冲区内容不受影响,而 EGL_BUFFER_DESTROYED 值表示颜色缓冲区内容可能会被操作破坏或更改。

EGL_SWAP_BEHAVIOR 的初始值由实现选择。

Mali 平台上 EGL_SWAP_BEHAVIOUR 的默认值为 EGL_BUFFER_DESTROYED。这是由于在渲染新帧之前必须从内存中获取前一个缓冲区并在最后存储它以及带宽消耗(这对移动设备上的电池寿命也非常不利)相关的性能损失。我无法肯定地评论 Tegra SoC 的默认行为,但是对我来说很明显它们的默认行为是 EGL_BUFFER_PRESERVED。

为了阐明 Mali 对 Khronos GLES 规范的立场 - Mali 完全符合要求。

于 2013-07-16T14:11:27.270 回答
0
  1. 您为什么不提供一个可行的示例,以便人们实际上可以提供帮助?

  2. 从您的代码中:我看不到您在哪里创建线路?就像是:

    @Override public void onSurfaceCreated(GL10 gl, EGLConfig config){
        ...
        mLine = new Lines();
        ...
    }
    
  3. 正如其他人已经提到的,onDrawFrame始终清除缓冲区:

    public void onDrawFrame(GL10 gl )
    {
        // Erase CL_COLOR_BUFFER
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    
  4. 设置相机:

    // Set the camera position (View matrix)
    Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
    //
    // Calculate the projection and view transformation
    Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mViewMatrix, 0);
    
  5. 画:

    line.draw( dx, dy, ux, uy );
    
于 2013-06-30T11:08:17.607 回答