我在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编写代码?
//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;
}
}
}
}
//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 );
}
//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 );
}
}
答案 0 :(得分:4)
好的,又来了:^ 1
OpenGL不是场景图。 OpenGL不维护场景,了解对象或保持几何轨迹。 OpenGL是一个绘图 API。你给它一个画布(以窗口或PBuffer的形式)并命令它绘制点,线或三角形,OpenGL就是这样做的。一旦绘制了一个基元(=点,线,三角形),OpenGL就没有任何关于它的回忆。如果有什么变化,你必须重绘整个事情。
重绘场景的正确步骤是:
禁用模板测试,以便在整个窗口中执行以下步骤。
使用glClear(bits)
清除帧缓冲区,其中位是一个位掩码,指定要清除画布的哪些部分。渲染新框架时,您要清除所有内容,bits = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT
;
设置视口,构建适当的投影矩阵
为场景中的每个对象加载正确的模型视图矩阵,设置制服,选择顶点数组并进行绘图调用。
通过刷新管道完成渲染。如果使用单个缓冲窗口glFinish()
,如果使用双缓冲窗口调用SwapBuffers
。在更高级别框架的情况下,这可以由框架执行。
重要在双缓冲窗口上完成绘图后,您不能继续发送绘图操作,因为通过执行缓冲交换,您正在绘制的后台缓冲区的内容是未定义。因此,您必须重新开始绘图,从清除帧缓冲区开始(步骤1和2)。
您的代码遗漏的正是这两个步骤。另外,我的印象是你正在执行OpenGL绘图调用,直接对输入事件做出反应,可能是在输入事件处理程序中。 不要这样做!。而是使用输入事件添加到要绘制的基元列表(在您的情况下为行),然后发送重绘事件,这使框架调用绘图函数。在绘图功能中迭代该列表以绘制所需的行。
重绘整个场景在OpenGL中是规范的!
[1](geesh,我已经厌倦了每隔3个左右写一次这个问题......)
答案 1 :(得分:3)
在这里采取行动,但你真的在清理屏幕吗?您所看到的行为类型表明您不是,并且在不同的场景中您会看到不同的错误 - 未初始化的内存,重用旧的缓冲区,隐式清除等等。
GL要求您具体说明您想要的内容,因此您需要明确清除。
答案 2 :(得分: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
答案 3 :(得分:0)
为什么不提供一个工作示例,以便人们真的可以提供帮助?
从您的代码: 我看不出你在哪里创建你的线?类似的东西:
@Override public void onSurfaceCreated(GL10 gl, EGLConfig config){
...
mLine = new Lines();
...
}
正如其他人已经提到的那样,onDrawFrame
总是清除缓冲区:
public void onDrawFrame(GL10 gl )
{
// Erase CL_COLOR_BUFFER
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
设置相机:
// 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);
绘制:
line.draw( dx, dy, ux, uy );
答案 4 :(得分:0)
从我对类似问题Why my opengl output differs for various devices?的回答中删除:
编码时我们应该考虑GPU吗?没办法,OpenGL API是应用程序和硬件之间的一层。
这对桌面图形来说非常正确,因为所有GPU都是即时渲染器,但是,移动图形并非如此。
Mali GPU使用基于图块的立即模式渲染。对于这种类型的渲染,帧缓冲区被划分为大小为16乘16像素的区块。 Polygon List Builder(PLB)将应用程序的输入数据组织到多边形列表中。每个图块都有一个多边形列表。当图元覆盖图块的一部分时,称为多边形列表命令的条目被添加到图块的多边形列表中。像素处理器获取一个图块的多边形列表,并在开始处理下一个图块之前计算该图块中所有像素的值。因为这种基于区块的方法使用快速的片上瓦片缓冲区,所以GPU仅将区块缓冲区内容写入每个区块末尾的主存储器中的帧缓冲区。基于非平铺的立即模式渲染器通常需要更多的帧缓冲访问。因此,基于磁贴的方法消耗更少的内存带宽,并且有效地支持深度测试,混合和抗锯齿等操作。
另一个区别是渲染缓冲区的处理。立即渲染器将“保存”缓冲区的内容,有效地允许您仅在先前存在的情况下绘制渲染场景中的差异。但是,这在马里可用,默认情况下不启用,因为如果使用不当会导致不良影响。
有关如何在GLES2 SDK中正确使用“EGL Preserve”的Mali GLES2 SDK示例
基于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。
澄清马里在Khronos GLES规格方面的立场 - 马里完全合规。