OpenGL笔记(五) 着色器渲染(以Android为例)

1、Android平台上下文环境的建立及初始化

1. 首先实例化Android上下文环境,即EGL的初始化。函数

bool EGLCore::init(EGLContext sharedContext) { EGLint numConfigs; EGLint width; EGLint height; const EGLint attribs[] = { EGL_BUFFER_SIZE, 32, EGL_ALPHA_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; //eglGetDisplay来返回OpenGL ES渲染的目标,每一个厂商都会返回默认的显示设备
    if ((display = eglGetDisplay(EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY) { LOGE("eglGetDisplay() returned error %d", eglGetError()); return false; } // 初始化显示设备
    if (!eglInitialize(display, 0, 0)) { LOGE("eglInitialize() returned error %d", eglGetError()); return false; } // 获得配置选项信息
    if (!eglChooseConfig(display, attribs, &config, 1, &numConfigs)) { LOGE("eglChooseConfig() returned error %d", eglGetError()); release(); return false; } // 建立OpenGL的上下文环境EGLContext
    EGLint eglContextAttributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; if (!(context = eglCreateContext(display, config, NULL == sharedContext ? EGL_NO_CONTEXT : sharedContext, eglContextAttributes))) { LOGE("eglCreateContext() returned error %d", eglGetError()); release(); return false; } pfneglPresentationTimeANDROID = (PFNEGLPRESENTATIONTIMEANDROIDPROC)eglGetProcAddress("eglPresentationTimeANDROID"); if (!pfneglPresentationTimeANDROID) { LOGE("eglPresentationTimeANDROID is not available!"); } return true; }

2. 将EGL和设备的屏幕链接起来。使用EGLSurface,经过EGL库提供的eglCreateWindowSurface能够建立一个可实际显示的Surface,经过EGL库提供的eglCreatePbufferSurface能够建立一个OffScreen的Surface。_window就是经过Java层的Surface对象建立出的ANativeWindow类型的对象,即本地设备屏幕的表示。也就是说真实显示的Surface仍是经过Java层建立好的, 而后OpenGL只是绘制到了这个target上边?ui

EGLSurface EGLCore::createWindowSurface(ANativeWindow* _window) { EGLSurface surface = NULL; EGLint format; if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format)) { LOGE("eglGetConfigAttrib() returned error %d", eglGetError()); release(); return surface; } ANativeWindow_setBuffersGeometry(_window, 0, 0, format); if (!(surface = eglCreateWindowSurface(display, config, _window, 0))) { LOGE("eglCreateWindowSurface() returned error %d", eglGetError()); } return surface; }

经过Java层的Surface对象建立ANativeWindow类型的对象方法以下:spa

JNIEXPORT void JNICALL Java_com_phuket_tour_opengl_renderer_PngPreviewController_setSurface (JNIEnv * env, jobject obj, jobject surface) { if (surface != 0 && NULL != controller) { window = ANativeWindow_fromSurface(env, surface); LOGI("Got window %p", window); controller->setWindow(window); } else if (window != 0) { LOGI("Releasing window"); ANativeWindow_release(window); window = 0; } }

3. 开发者须要开辟一个新的线程,来执行OpenGL ES的渲染操做,并且还必须为该线程绑定显示设备(surface)与上下文环境(Context)。EGL是双缓冲模式,内部有两个FrameBuffer,当EGL将一个FrameBuffer显示到屏幕上的时候,另一个FrameBuffer就在后台等待OpenGL ES进行渲染输出了。直到调用函数eglSwapBuffers这条指令的时候,才会把前台的FrameBuffer和后台的FrameBuffer进行交换。线程

bool EGLCore::makeCurrent(EGLSurface eglSurface) { return eglMakeCurrent(display, eglSurface, eglSurface, context); }

2、Texture/Shader/Program

 1. 建立Texture(纹理)code

int PicPreviewTexture::initTexture() { glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); return 1; }

2. 建立/初始化/编译Shaderorm

GLuint PicPreviewRender::compileShader(GLenum type, const char *sources) { GLint status; GLuint shader = glCreateShader(type); if (shader == 0 || shader == GL_INVALID_ENUM) { LOGI("Failed to create shader %d", type); return 0; } glShaderSource(shader, 1, &sources, NULL); glCompileShader(shader); glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status == GL_FALSE) { glDeleteShader(shader); LOGI("Failed to compile shader : %s\n", sources); return 0; } return shader; }

3. 建立并使用Program(显卡可执行程序)对象

int PicPreviewRender::useProgram() { program = glCreateProgram(); glAttachShader(program, vertShader); glAttachShader(program, fragShader); glBindAttribLocation(program, ATTRIBUTE_VERTEX, "position"); glBindAttribLocation(program, ATTRIBUTE_TEXCOORD, "texcoord"); glLinkProgram(program); GLint status; glGetProgramiv(program, GL_LINK_STATUS, &status); if (status == GL_FALSE) { LOGI("Failed to link program %d", program); return -1; } glUseProgram(program); uniformSampler = glGetUniformLocation(program, "yuvTexSampler"); return 1; }

 3、渲染操做

void PicPreviewRender::render(){ glViewport(_backingLeft, _backingTop, _backingWidth, _backingHeight); glClearColor(0.0f, 0.0f, 1.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glUseProgram(program); static const GLfloat _vertices[] = { -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f }; glVertexAttribPointer(ATTRIBUTE_VERTEX, 2, GL_FLOAT, 0, 0, _vertices); glEnableVertexAttribArray(ATTRIBUTE_VERTEX); static const GLfloat texCoords[] = { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f }; glVertexAttribPointer(ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, 0, 0, texCoords); glEnableVertexAttribArray(ATTRIBUTE_TEXCOORD); picPreviewTexture->bindTexture(uniformSampler); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); }

    首先,_vertices有4个顶点,每一个顶点都会调用一次顶点着色器程序。按照规则,一共有四个点,每一个点为vec2,可是顶点着色器的in输入变量为vec3,在GLSL语法中,vec2传递给vec3,其中扩充的维度的值默认为0,这也能够接受由于所在的是二维平面,否则也不会2个float表明一个点,因此,第三维自动为0。gl_Position 的第四维是和裁剪以及变换有关的,没有相关的变换时候是1.0blog

    顶点坐标的归一化区间是[-1, 1],纹理坐标的归一化区间是[0, 1]。因此代码是将纹理总体完整铺在显示区域,若是想把纹理的一部分铺上去,那就在[0, 1]区间内部分写进去。开发

执行完渲染操做后,调用eglSwapBuffers便可显示。至此,一次渲染操做完成。get

相关文章
相关标签/搜索