嵌入式加速的3D制图法标准
OpenGL ES是一种免版税的、跨平台的API,用于在嵌入式和移动系统(包括控制台、电话、设备和车辆)上呈现高级2D和3D图形。它由定义良好的适合低功耗设备的桌面OpenGL子集组成,并在软件和图形加速硬件之间提供了灵活而强大的接口。
OpenGL ES版本一览
OpenGL ES 3.2 -附加的OpenGL功能
在本系列的最新版本中,OpenGL ES 3.2为OpenGL ES 3.1添加了基于Android扩展包的附加功能,这使得移动API的功能与桌面API OpenGL更加接近。
OpenGL ES 3.1 -将计算引入移动图形
尽管OpenGL ES 3.1只是API的一个小修改,但它对API来说是一个巨大的里程碑,因为它增加了在API中进行通用计算的能力,将计算引入了移动图形。
OpenGL ES 3.0 -增强的图形
OpenGL ES 3.0是OpenGL ES的另一个进化步骤,特别是包括多个渲染目标,额外的纹理功能,统一的缓冲,实例化和转换反馈。
OpenGL ES 2.0 -可编程的阴影
OpenGL ES 2.0是第一个在最新一代图形硬件中公开可编程着色器的便携移动图形API。它仍然是当今流行的API,并且仍然是最广泛可用的3D图形API,并且仍然是瞄准市场上最广泛设备的可靠选择。
OpenGL ES - 1 固定函数图形
OpenGL ES 1.0和1.1是第一个可移植的移动图形api,定义相对于OpenGL 1.5规范,提供固定的功能图形加速
API和拓展头文件
由于扩展因平台和驱动程序的不同而不同,OpenGL开发人员不能期望所有扩展的接口都在标准的gl.h和glx中定义。和wgl.h头文件提供的OS /图形驱动程序。这里提供了额外的头文件,包括:
下面描述的几乎所有头文件都依赖于一个平台头文件,该文件是多个Khronos api所共有的<KHR/khrplatform.h>。
- <GL/glext.h> - OpenGL 1.2及以上的兼容性配置文件和扩展接口
- <GL/glcorearb.h> - OpenGL核心配置文件和ARB扩展接口,如OpenGL 4.3规范的附录G.2所述。不包括仅在兼容性配置文件中找到的接口
- <GL/glxext.h> - GLX 1.3及以上API和GLX扩展接口
- <GL/wglext.h> - WGL扩展接口
这些头定义了包括枚举数在内的接口;原型;对于支持动态运行时扩展查询的平台,如Linux和Microsoft Windows,函数指针类型为defs。
GL命令语法
GL命令是函数或过程。执行不同的命令组相同的操作,但是不同的参数是如何提供给它们的。为了方便地适应这种变化,我们采用了一种描述命令的符号和他们的论点。GL命令由后面的名称组成,具体取决于具体的名称命令,最多4个字符。第一个字符表示值的数量必须呈现给命令的指定类型。第二个字符或字符对指示参数的特定类型:8位整数、16位整数、32位整数、单精度浮点数或双精度浮点数。最后一个字符(如果存在)是v,表示命令接受a指向值数组(向量)的指针,而不是一系列单独的参数。
两个具体的顶点命令的例子:
void Vertex3f( float x, float y, float z );
void Vertex2sv( short v[2] );
这些示例显示了这些命令的ANSI C声明。一般来说,命令声明的格式是:
rtype Name{1234}{ b s i f d ub us ui}{v} ( [args ,] T arg1 , . . . , T argN [, args] );
type是函数的返回类型。大括号({})括起一系列字符(或字符对),从中选择一个。表示 没有 character.的
括号中的参数([args,]和[,args])可能存在,也可能不存在。
使用NVIDIA框架创建GL项目
对于基于tegra的设备,NVIDIA提供了一个框架,大大简化了这个过程(该框架可能也适用于其他Android设备,但我们还没有测试过)。现在我们将实现一个与前一个非常相似的项目,但是我们将使用这个助手来展示它的基本用法。应用程序会根据触摸的x和y位置改变背景颜色,但除此之外,如果应用程序处于焦点或暂停状态,它还会覆盖不同的文本。为了执行后者,我们将处理UI生成的事件。如果应用程序是焦点,我们将显示“活动模式!”,我们将跟踪触摸的位置。当返回按钮被按下一次,或“最近的应用”按钮被按下,或按下右下角,应用将被认为是暂停,或失焦,文本将被更改为:
Auto-pause Press back to quit Tap window to resume
当应用程序暂停时,除非生成一个新事件,否则将再呈现4帧,之后就不会发生其他任何事情。首先,我们像以前一样创建一个项目(SimpleNativeGL_NV)。按照上面描述的第一步操作,直到修改AndroidManifest.xml文件的部分。清单文件的唯一区别是我们给库的名字:
[...]
<!-- Tell NativeActivity the name of our .so -->
<meta-data android:name="android.app.lib_name"
android:value="SimpleNativeGL_NV" />
[...]
现在让我们修改Android项目jni文件夹中的mk文件:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := SimpleNativeGL_NV
LOCAL_SRC_FILES := SimpleNativeGL_NV.cpp Engine.cpp
LOCAL_LDLIBS := -lc -lm -llog -landroid -ldl -lGLESv2 -lEGL
LOCAL_STATIC_LIBRARIES := nv_and_util nv_egl_util nv_bitfont nv_math nv_glesutil nv_hhdds nv_log nv_shader nv_file nv_thread
LOCAL_CFLAGS += -std=gnu++0x
include $(BUILD_SHARED_LIBRARY)
# Add the folder with the NVIDIA helper, assuming we are at the nvsample_workspace
$(call import-add-path, $(NVPACK_PATH)/TDK_Samples/tegra_android_native_samples_v10p14/libs/jni)
# Import the modules from the NVIDIA helper
$(call import-module, nv_and_util)
$(call import-module, nv_egl_util)
$(call import-module, nv_bitfont)
$(call import-module, nv_math)
$(call import-module, nv_glesutil)
$(call import-module, nv_hhdds)
$(call import-module, nv_log)
$(call import-module, nv_shader)
$(call import-module, nv_file)
$(call import-module, nv_thread)
除了我们在示例中所做的之外,我们还从头创建了一个OpenGL项目,这里我们只是链接到NVIDIA库并导入相应的模块。注意,我们再次假设您使用的是$NVPACK_PATH/nvsample_workspace工作区。如果没有,您将不得不更改NVIDIA助手文件夹的指针。因为我们将创建一个名为Engine的类,所以我们将相应的源文件添加到LOCAL_SRC_FILES列表中。此外,我们需要字体的文本,我们将覆盖在屏幕上。我们可以从工作区中其他项目的assets文件夹中获取它们。例如,在Project Explorer中展开文件夹NativeBasic > assets。复制两个*。abc和*。将dds文件复制到我们项目的assets文件夹中(总共需要复制4个文件)。在Eclipse Project Explorer中按住ALT键拖放可能是最简单的方法。最后,为了确保我们的应用程序得到充分优化,我们需要application .mk。您可以像从头创建OpenGL项目那样创建它,或者直接从其他项目复制它。因为我们想要使用新的c++ 11 STL的智能指针,我们需要添加一行来表明我们将使用GNU STL实现:
APP_ABI := armeabi-v7a APP_STL := gnustl_static
现在,我们将从前面的示例创建一个扩展引擎结构的类,并将大部分代码移动到它。在Project Explorer窗格中,右键单击jni文件夹,然后单击New >类。在类名字段中,键入Engine,然后单击Finish。Eclipse现在已经为我们创建了类的头文件和源文件。让我们从头文件开始:
#ifndef __ENGINE_H
#define __ENGINE_H
// Includes for the NVIDIA helper libraries
#include <nv_and_util/nv_native_app_glue.h>
#include <nv_egl_util/nv_egl_util.h>
// Logging macros
#define APP_NAME "SimpleNativeGL_NV"
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG,APP_NAME, __VA_ARGS__))
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, APP_NAME, __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, APP_NAME, __VA_ARGS__))
#define FRAMES_TO_RENDER 4
/**
* This is the engine which implements the required callback functions,
* as well as a few utility functions.
* This implementation relies on the NVIDIA utilities.
*/
class Engine
{
public:
/**
* The constructor saves a pointer to the engine and to the callback
* functions in the Android app. It also initializes the nv_shader library.
*/
Engine( NvEGLUtil& egl, struct android_app *app );
/**
* The destructor mainly frees the allocated BitFonts.
*/
~Engine();
/**
* The application can be active or paused. This function returns
* false when the application is paused.
*/
bool isActiveMode()
{
return mActiveMode;
}
/**
* Render the frame, after checking what the status of the Android app is.
* Interactible should be true if the app is running, it is active, focused,
* and has a valid surface.
*/
void updateFrame( bool interactible );
protected:
/**
* Wrapper to handle commands generated by the UI.
*/
static void handleCmdThunk( struct android_app *app, int32_t cmd );
/**
* Handle commands.
*/
void handleCommand( int cmd );
/**
* Wrapper to handle input events generated by the UI.
*/
static int32_t handleInputThunk( struct android_app *app, AInputEvent *event );
/**
* Handle inputs.
*/
int handleInput( AInputEvent *event );
/* Auxiliary functions */
/**
* Set mActiveMode; we use mActiveMode to keep track of whether the app is actually
* running or paused. Examples of when the app is paused is when the user has pressed
* the back button, the "Recent apps" button, or the time at the bottom right corner
* of the screen.
*/
void setActiveMode( bool running );
/**
* If the app's window had been resized returns true and forces the frame to be
* rendered.
*/
bool checkWindowResized();
/**
* Resets the number of frames that need to be rendered.
*/
void requestForceRender()
{
// Even when no events are generated, we required that the system still renders
// FRAMES_TO_RENDER frames.
mForceRender = FRAMES_TO_RENDER;
}
/*
* Check if we are done rendering frames.
*/
bool isForcedRenderPending()
{
// mForceRender is decremented every time a frame is rendered and
// reinitialized to FRAMES_TO_RENDER when a new event is generated
// by the UI
return mForceRender > 0;
}
/**
* This is the actual method which renders the frame.
* It returns false if either EGL was not ready to render or
* the UI could not be initialized.
*/
bool renderFrame( bool allocateIfNeeded );
/**
* Initialize the UI
* Returns true if the UI was initialized, false if the fonts could not be
* initialized.
*/
bool initUI();
/**
* If there is a pending window resize, perform it and return true.
*/
bool resizeIfNeeded();
/* Variables */
struct android_app *mApp; // Pointer to the Android app
NvEGLUtil &mEgl; // NvEGLUtil is the NVIDIA wrapper to EGL
bool mResizePending; // Is there a window-resize pending?
bool mActiveMode; // Is the app active or has it been paused?
bool mUiInitialized; // Has the UI been initialized?
void *mUiText[2]; // We need this variable to get a pointer to the bftext objects
int mTouchX, mTouchY; // Position of the touch event
int mForceRender; // This variable counts how many more frames we want
// to render in case no new requests are generated.
};
#endif // __ENGINE_H
注释以及方法和变量的命名应该是不言自明的。所以我们可以看看不同方法的实现,从包含、构造函数和析构函数开始:
#include "Engine.h"
// Includes for GL and EGL
#include <EGL/egl.h>
#include <EGL/eglplatform.h>
#include <GLES2/gl2.h>
// Includes for the NVIDIA helper libraries
#include <nv_and_util/nv_native_app_glue.h>
#include <nv_egl_util/nv_egl_util.h>
#include <nv_bitfont/nv_bitfont.h>
#include <nv_shader/nv_shader.h>
Engine::Engine( NvEGLUtil& egl, struct android_app *app )
: mEgl( egl ), mApp( app ), mResizePending( false ), mActiveMode( true ),
mForceRender( 4 ), mUiInitialized( false )
{
// Save a pointer to the engine in the Android app
app->userData = this;
// Save pointers to the implementation of the callback functions in the
// Android app
app->onAppCmd = &Engine::handleCmdThunk;
app->onInputEvent = &Engine::handleInputThunk;
// Initialize the nv_shader library
nv_shader_init( app->activity->assetManager );
// Initialize the pointers to the bftext objects
mUiText[0] = NULL;
mUiText[1] = NULL;
}
Engine::~Engine()
{
// Free the allocated BitFonts
NVBFTextFree( mUiText[1] );
NVBFTextFree( mUiText[0] );
NVBFCleanup();
}
在上面的代码中,我们使用了一些NVIDIA库,比如EGL实用程序。没有关于这些库的全面文档,但是代码注释得很好。让我们研究一下EGL是由什么组成的。将光标放在NvEGLUtil引擎构造函数的参数列表中,然后按F3(或者按CTRL-click [CMD-click on Mac])。这将把您带到nv_egl_util.h中该对象的定义。如果索引器工作正常,您现在可以执行相同的操作,在某些成员函数(如swap())的声明和定义之间进行切换。可以使用CTRL-TAB在头文件和实现文件之间切换。使索引器为nv_egl_util工作。h,你需要转到项目属性> C/ c++一般>路径和符号>源位置,并将文件夹链接到nv_egl_util.cpp文件所在的位置(它在$NVPACK_PATH/TDK_Samples/tegra_android_native_samples_v10p14/libs/jni/nv_egl_util中)。无论如何,浏览这两个文件可以看到NvEGLUtil类是如何工作的。到目前为止,除了初始化了nv_shader库之外,它与前面的代码非常相似。现在我们需要编写处理来自UI的命令的函数。在构造函数中,我们将app->onAppCmd回调链接到方法引擎::handleCmdThunk();这个方法只是从应用程序获取一个指向引擎对象的指针,并调用一个实际处理命令的方法。
这一部分来源于https://docs.nvidia.com/gameworks/content/technologies/mobile/native_android_opengles.htm