你好,三角形:
■ 先给出完整源代码 ■
AndroidMainfest.xml中添加:
<!-- Tell the system this application requires OpenGL ES 3.0. -->
<uses-feature android:glEsVersion="0x00030000" android:required="true" />
public class HelloTriangleRenderer implements GLSurfaceView.Renderer {
// Member variables
private int mProgramObject;
private int mWidth;
private int mHeight;
private FloatBuffer mVertices;
private static String TAG = "HelloTriangleRenderer";
private final float[] mVerticesData = {
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};
// Constructor
public HelloTriangleRenderer(Context context) {
mVertices = ByteBuffer.allocateDirect(mVerticesData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
mVertices.put(mVerticesData).position(0);
}
// Create a shader object, load the shader source, and compile the shader.
private int LoadShader(int type, String shaderSrc) {
int shader;
int[] compiledStatus = new int[1];
// Create the shader object
shader = GLES30.glCreateShader(type);
if (shader == 0) {
return 0;
}
// Load the shader source
GLES30.glShaderSource(shader, shaderSrc);
// Compile the shader
GLES30.glCompileShader(shader);
// Check the compile status
GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compiledStatus, 0);
if (compiledStatus[0] == 0) {
Log.e(TAG, GLES30.glGetShaderInfoLog(shader));
GLES30.glDeleteShader(shader);
return 0;
}
return shader;
}
// Initialize the shader and program object
@Override
public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
String vShaderStr = "#version 300 es \n"
+ "in vec4 vPosition; \n"
+ "void main() \n"
+ "{ \n"
+ " gl_Position = vPosition; \n"
+ "} \n";
String fShaderStr = "#version 300 es \n"
+ "precision mediump float; \n"
+ "out vec4 fragColor; \n"
+ "void main() \n"
+ "{ \n"
+ " fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 ); \n"
+ "} \n";
int vertexShader;
int fragmentShader;
int programObject;
int[] linkedStatus = new int[1];
// Load the vertex/fragment shaders
vertexShader = LoadShader(GLES30.GL_VERTEX_SHADER, vShaderStr);
fragmentShader = LoadShader(GLES30.GL_FRAGMENT_SHADER, fShaderStr);
// Create the program object
programObject = GLES30.glCreateProgram();
if (programObject == 0) {
return;
}
GLES30.glAttachShader(programObject, vertexShader);
GLES30.glAttachShader(programObject, fragmentShader);
// Bind vPosition to attribute 0
GLES30.glBindAttribLocation(programObject, 0, "vPosition");
// Link the program
GLES30.glLinkProgram(programObject);
// Check the link status
GLES30.glGetProgramiv(programObject, GLES30.GL_LINK_STATUS, linkedStatus, 0);
if (linkedStatus[0] == 0) {
Log.e(TAG, "Error linking program:");
Log.e(TAG, GLES30.glGetProgramInfoLog(programObject));
GLES30.glDeleteProgram(programObject);
return;
}
// Store the program object
mProgramObject = programObject;
GLES30.glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
}
// Handle surface changes
@Override
public void onSurfaceChanged(GL10 glUnused, int width, int height) {
mWidth = width;
mHeight = height;
}
// Draw a triangle using the shader pair created in onSurfaceCreated()
@Override
public void onDrawFrame(GL10 glUnused) {
// Set the viewport
GLES30.glViewport(0, 0, mWidth, mHeight);
// Clear the color buffer
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
// Use the program object
GLES30.glUseProgram(mProgramObject);
// Load the vertex data
GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, mVertices);
GLES30.glEnableVertexAttribArray(0);
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 3);
}
}
public class HelloTriangle extends Activity {
private final int CONTEXT_CLIENT_VERSION = 3;
private GLSurfaceView mGLSurfaceView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLSurfaceView = new GLSurfaceView(this);
if (detectOpenGLES30()) {
// Tell the surface view we want to create an OpenGL ES 3.0-compatible
// context, and set an OpenGL ES 3.0-compatible renderer.
mGLSurfaceView.setEGLContextClientVersion(CONTEXT_CLIENT_VERSION);
mGLSurfaceView.setRenderer(new HelloTriangleRenderer(this));
} else {
Log.e("HelloTriangle", "OpenGL ES 3.0 not supported on device. Exiting...");
finish();
}
setContentView(mGLSurfaceView);
}
private boolean detectOpenGLES30() {
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
ConfigurationInfo info = am.getDeviceConfigurationInfo();
return (info.reqGlEsVersion >= 0x30000);
}
@Override
protected void onResume() {
// Ideally a game should implement onResume() and onPause()
// to take appropriate action when the activity looses focus
super.onResume();
mGLSurfaceView.onResume();
}
@Override
protected void onPause() {
// Ideally a game should implement onResume() and onPause()
// to take appropriate action when the activity looses focus
super.onPause();
mGLSurfaceView.onPause();
}
}
public class GLES30 extends GLES20
java.lang.Object
↳ android.opengl.GLES20
↳ android.opengl.GLES30
Known direct subclasses
GLES31
Known indirect subclasses
GLES32
public class GLSurfaceView
extends SurfaceView implements SurfaceHolder.Callback2
java.lang.Object
↳ android.view.View
↳ android.view.SurfaceView
↳ android.opengl.GLSurfaceView
An implementation of SurfaceView that uses the dedicated surface for displaying OpenGL rendering.
GLSurfaceView.setRenderer(Renderer)
Public methods:
abstract void onDrawFrame(GL10 gl)
● Called to draw the current frame.
abstract void onSurfaceChanged(GL10 gl, int width, int height)
● Called when the surface changed size.
abstract void onSurfaceCreated(GL10 gl, EGLConfig config)
● Called when the surface is created or recreated.
记住:OpenGL ES 3.0完全基于着色器,这意味着,如果没有加载和绑定合适的着色器,就无法绘制任何几何形状。也就是说,渲染需要比使用固定功能处理的桌面OpenGL更多的设置代码。着色器,至少要有顶点着色器和片段着色器。
最终渲染效果:
可见,顶点着色器决定了三个顶点构造的三角形GL_TRIANGLES,片段着色器决定了图元的颜色vec4(1.0,0.0,0.0,1.0)。
一旦应用程序为顶点和片段着色器创建了着色器对象,就需要创建一个程序对象。从概念上说,程序对象可以视为最终链接的程序。不同的着色器编译为一个着色器对象之后,它们必须连接到一个程序对象并一起链接,才能绘制图形。
显示后台缓冲区:
到了将三角形绘制到帧缓冲区时,必须介绍最后一个细节:如何在屏幕上真正显示帧缓冲区的内容。
先讨论双缓冲区(Double Buffering)的概念。
屏幕上可见的帧缓冲区由一个像素数据的二维数组表示。我们可以将在屏幕上显示图像视为在绘制时简单地更新可见帧缓冲区中的像素数据。但是,直接在可显示缓冲区上更新像素有一个严重的问题——也就是说,在典型的显示系统中,物理屏幕以固定的速率从帧缓冲区内存中更新。如果我们直接绘制到帧缓冲区,那么用户在部分更新帧缓冲区时会看到伪像。
为了解决这个问题,我们使用所谓的双缓冲区。在这种方案中,有两个缓冲区:前台缓冲区和后台缓冲区。所有渲染都发生在后台缓冲区,它位于不可见屏幕的内存区域。当所有渲染完成时,这个缓冲区被“交换”到前台缓冲区(或者可见缓冲区)。然后前台缓冲区变成下一帧的后台缓冲区。使用这种技术,我们在一帧上的所有渲染完成之前不显示可见表面。