OpenGL ES 3.0 | 以HelloTriangle为实

2020-07-31  本文已影响0人  凌川江雪

案例运行(绘制一个三角形)的基本步骤

【可以先看看文末的代码,结合文章内容去看,
理解了整个流程之后再来看这个步骤,会容易很多】

  • 用EGL创建屏幕上的渲染表面(Android直接用一个GLSurfaceView
  • 加载顶点、片段着色器
  • 创建一个程序对象,
    连接顶点、片段着色器,
    并链接程序对象;
  • 设置视口;
  • 清除颜色缓冲区;
  • 渲染简单图元
  • 使颜色缓冲区的内容在EGL窗口表面(GLSurfaceView)中可见

着色器

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 ( 0.0, 0.8, 1.0, 1.0 );  \n"
         + "}                                            \n";      

编译和加载着色器

   ///
   // Create a shader object,
   // load the shader source, and
   // compile the shader,
   // finally,
   // return the shader`s id!
   // 【适用于 顶点着色器、片段着色器】
   //
   private int LoadShader ( int type, String shaderSrc )
   {
      int shader;
      int[] compiled = 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, compiled, 0 );

      //编译失败,则 报错 并 删除着色器实例
      if ( compiled[0] == 0 )
      {
         Log.e ( TAG, GLES30.glGetShaderInfoLog ( shader ) );
         GLES30.glDeleteShader ( shader );
         return 0;
      }

      //编译成功,则返回 着色器id
      return shader;
   }

创建一个程序对象并链接着色器

   ///
   // Initialize the shader and program object
   // 初始化 着色器 和 渲染管线程序
   //
   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 ( 0.0, 0.8, 1.0, 1.0 );  \n"
         + "}                                            \n";

      int vertexShader;//加载好的 顶点着色器实例
      int fragmentShader;//加载好的 片段着色器实例
      int programObject;//自定义的渲染管线程序id
      int[] linked = new int[1];//存放链接成功 渲染管线程序id 的数组

      // Load the vertex/fragment shaders
      // 【调用 LoadShader() 】加载着色器实例
      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
      // 把链接成功的 渲染管线程序id 存入数组linked
      GLES30.glGetProgramiv ( programObject, GLES30.GL_LINK_STATUS, linked, 0 );

      //若链接失败则报错 并 删除程序
      if ( linked[0] == 0 )
      {
         Log.e ( TAG, "Error linking program:" );
         Log.e ( TAG, GLES30.glGetProgramInfoLog ( programObject ) );
         GLES30.glDeleteProgram ( programObject );
         return;
      }

      // Store the program object
      // 保存 链接成功的 渲染管线程序id 到 全局变量
      mProgramObject = programObject;

      //指定清除屏幕用的颜色
      GLES30.glClearColor ( 1.0f, 1.0f, 1.0f, 0.0f );
   }

至此,便完成了
编译着色器、检查编译错误、
创建程序对象、连接着色器、链接程序并检查链接错误等流程;

public void onDrawFrame ( GL10 glUnused )
   {
      // Set the viewport
      // viewport【窗口】
      GLES30.glViewport ( 0, 0, mWidth, mHeight );

      // Clear the color buffer
      GLES30.glClear ( GLES30.GL_COLOR_BUFFER_BIT );

      // Use the program object
      // 指定使用某套shader程序
      GLES30.glUseProgram ( mProgramObject );

      // Load the vertex data
      // vertex【顶点】
      // 将顶点位置数据【mVertices】传送进 渲染管线
      GLES30.glVertexAttribPointer ( 0, 3, GLES30.GL_FLOAT, false, 0, mVertices );
      //启用顶点位置数据
      GLES30.glEnableVertexAttribArray ( 0 );

//      GLES30.glDrawArrays ( GLES30.GL_TRIANGLES, 0, 3 );
//      绘制
      GLES30.glDrawArrays ( GLES30.GL_TRIANGLE_STRIP, 0, 3 );
   }

设置视口和清除颜色缓冲区

设置视口

清除颜色缓冲区

  • 在OpenGL ES中,
    绘图中涉及多种缓冲区类型:颜色、深度、模板;

加载几何形状和绘制图元

加载几何形状

private final float[] mVerticesData =
           { 0.0f, 0.5f, 0.0f,
             -0.5f, -0.5f, 0.0f,
             0.5f, -0.5f, 0.0f };

...

public HelloTriangleRenderer ( Context context )
   {
      mVertices = ByteBuffer.allocateDirect ( mVerticesData.length * 4 )
                  .order ( ByteOrder.nativeOrder() ).asFloatBuffer();
      mVertices.put ( mVerticesData ).position ( 0 );
   }

...

GLES30.glVertexAttribPointer ( 0, 3, GLES30.GL_FLOAT, false, 0, mVertices );
GLES30.glEnableVertexAttribArray ( 0 );
GLES30.glDrawArrays ( GLES30.GL_TRIANGLE_STRIP, 0, 3 );

绘制图元

显示后台缓冲区

如何在屏幕上
真正显示帧缓冲区的内容
——双缓冲区

项目代码

// Book:      OpenGL(R) ES 3.0 Programming Guide, 2nd Edition
// Authors:   Dan Ginsburg, Budirijanto Purnomo, Dave Shreiner, Aaftab Munshi
// ISBN-10:   0-321-93388-5
// ISBN-13:   978-0-321-93388-1
// Publisher: Addison-Wesley Professional
// URLs:      http://www.opengles-book.com
//            http://my.safaribooksonline.com/book/animation-and-3d/9780133440133
//

// Hello_Triangle
//
//    This is a simple example that draws a single triangle with
//    a minimal vertex/fragment shader.  The purpose of this
//    example is to demonstrate the basic concepts of
//    OpenGL ES 3.0 rendering.

package com.lwp.openglorigintest;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import android.util.Log;

public class HelloTriangleRenderer implements GLSurfaceView.Renderer
{
   // Member variables
   private int mProgramObject;//自定义渲染管线程序id
   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,
   // finally,
   // return the shader`s id!
   // 【适用于 顶点着色器、片段着色器】
   //
   private int LoadShader ( int type, String shaderSrc )
   {
      int shader;
      int[] compiled = 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, compiled, 0 );

      //编译失败,则 报错 并 删除着色器实例
      if ( compiled[0] == 0 )
      {
         Log.e ( TAG, GLES30.glGetShaderInfoLog ( shader ) );
         GLES30.glDeleteShader ( shader );
         return 0;
      }

      //编译成功,则返回 着色器id
      return shader;
   }

   ///
   // Initialize the shader and program object
   // 初始化 着色器 和 渲染管线程序
   //
   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 ( 0.0, 0.8, 1.0, 1.0 );  \n"
         + "}                                            \n";

      int vertexShader;//加载好的 顶点着色器实例
      int fragmentShader;//加载好的 片段着色器实例
      int programObject;//自定义的渲染管线程序id
      int[] linked = new int[1];//存放链接成功 渲染管线程序id 的数组

      // Load the vertex/fragment shaders
      // 【调用 LoadShader() 】加载着色器实例
      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
      // 把链接成功的 渲染管线程序id 存入数组linked
      GLES30.glGetProgramiv ( programObject, GLES30.GL_LINK_STATUS, linked, 0 );

      //若链接失败则报错 并 删除程序
      if ( linked[0] == 0 )
      {
         Log.e ( TAG, "Error linking program:" );
         Log.e ( TAG, GLES30.glGetProgramInfoLog ( programObject ) );
         GLES30.glDeleteProgram ( programObject );
         return;
      }

      // Store the program object
      // 保存 链接成功的 渲染管线程序id 到 全局变量
      mProgramObject = programObject;

      //指定清除屏幕用的颜色
      GLES30.glClearColor ( 1.0f, 1.0f, 1.0f, 0.0f );
   }

   // /
   // Draw a triangle using the shader pair created in onSurfaceCreated()
   // 使用onSurfaceCreated()中创建好的 着色器对(一对顶点、片段着色器) 画一个三角形
   //
   public void onDrawFrame ( GL10 glUnused )
   {
      // Set the viewport
      // viewport【窗口】
      GLES30.glViewport ( 0, 0, mWidth/2, mHeight/2 );

      // Clear the color buffer
      GLES30.glClear ( GLES30.GL_COLOR_BUFFER_BIT );

      // Use the program object
      // 指定使用某套shader程序
      GLES30.glUseProgram ( mProgramObject );

      // Load the vertex data
      // vertex【顶点】
      // 将顶点位置数据【mVertices】传送进 渲染管线
      GLES30.glVertexAttribPointer ( 0, 3, GLES30.GL_FLOAT, false, 0, mVertices );
      //启用顶点位置数据
      GLES30.glEnableVertexAttribArray ( 0 );

//      GLES30.glDrawArrays ( GLES30.GL_TRIANGLES, 0, 3 );
//      绘制
      GLES30.glDrawArrays ( GLES30.GL_TRIANGLE_STRIP, 0, 3 );
   }

   // /
   // Handle surface changes
   //
   public void onSurfaceChanged ( GL10 glUnused, int width, int height )
   {
      mWidth = width;
      mHeight = height;
   }
}
package com.lwp.openglorigintest;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ConfigurationInfo;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.util.Log;

public class HelloTriangle extends AppCompatActivity {

    private final int CONTEXT_CLIENT_VERSION = 3;

    @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();
    }

    private GLSurfaceView mGLSurfaceView;
}
然后就可以运行了:










参考自:

上一篇 下一篇

猜你喜欢

热点阅读