由于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",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,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,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,-ratio,ratio,* -1,1,3,7); */ // Take into account device orientation if( width > height ) { Matrix.frustumM( mProjMatrix,-1,10 ); } else { Matrix.frustumM( mProjMatrix,-1 / ratio,1 / ratio,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,-useForOrtho / 2,useForOrtho / 2,0.1f,100f ); }
Line.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 }; 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,lcolor,0 ); GLES20.glLineWidth( 3 ); // Draw the triangle GLES20.glDrawArrays( GLES20.GL_LINES,vertexCount ); // Disable vertex array GLES20.glDisableVertexAttribArray( mPositionHandle ); } }
解决方法
OpenGL is not a scene graph. OpenGL does not maintain a scene,knows about objects or keeps tracks of geometry. OpenGL is a drawing API. You give it a canvas (in form of a Window or a PBuffer) and order it to draw points,lines or triangles and OpenGL does exactly that. Once a primitive (=point,line,triangle) has been drawn,OpenGL has no recollection about it whatsoever. If something changes,you have to redraw the whole thing.
重绘场景的正确步骤是:
>禁用模板测试,以便以下步骤在整个窗口上运行.
>使用glClear(位)清除帧缓冲区,其中位是位掩码,指定要清除画布的哪些部分.渲染新帧时,您要清除所有内容,因此位= GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
>设置视口,构建适当的投影矩阵
>为场景中的每个对象加载正确的模型视图矩阵,设置制服,选择顶点数组并进行绘图调用.
>通过冲洗管道完成渲染.如果使用单个缓冲窗口glFinish(),如果使用双缓冲窗口调用SwapBuffers.在更高级别框架的情况下,这可以由框架执行.
重要事项在双缓冲窗口上完成绘图后,您不能继续发送绘图操作,因为通过执行缓冲区交换,您绘制的后台缓冲区的内容是未定义的.因此,您必须重新开始绘图,从清除帧缓冲区开始(步骤1和2).
您的代码遗漏的正是这两个步骤.另外我的印象是你正在执行OpenGL绘图调用,直接对输入事件做出反应,可能在输入事件处理程序中.不要这样做!而是使用输入事件添加到要绘制的基元列表(在您的情况下为行),然后发送重绘事件,这使框架调用绘图函数.在绘图功能中迭代该列表以绘制所需的线条.
重绘整个场景在OpenGL中是规范的!
[1](geesh,我已经厌倦了每隔3个左右写一次这个问题……)