Skip to content

Instantly share code, notes, and snippets.

@tamarous

tamarous/GLHelper.cpp

Last active Dec 24, 2017
Embed
What would you like to do?
使用OpenGL ES以双目渲染和显示360度视频。博文地址:http://www.tamarous.com/2017/12/23/render-360-video-using-opengles/
#include "GLHelper.h"
#include <android/native_window.h>
const char* TEXTURE_UNIFORMS[] = {"y_tex", "u_tex", "v_tex"};
GLuint yuvTextures[3];
#define glCheckError() glCheckError_(__LINE__)
void glCheckError_(int line)
{
GLenum errorCode;
char error[100];
memset(error,0,sizeof(error));
while ((errorCode = glGetError()) != GL_NO_ERROR)
{
switch (errorCode)
{
case GL_INVALID_ENUM: sprintf(error,"GL_INVALID_ENUM"); break;
case GL_INVALID_VALUE: sprintf(error,"GL_INVALID_VALUE"); break;
case GL_INVALID_OPERATION: sprintf(error,"GL_INVALID_OPERATION"); break;
case GL_OUT_OF_MEMORY: sprintf(error,"GL_OUT_OF_MEMORY"); break;
case GL_INVALID_FRAMEBUFFER_OPERATION: sprintf(error,"GL_INVALID_FRAMEBUFFER_OPERATION"); break;
}
LOGE("Line is %d, glError: %s", line, error);
}
}
const GLfloat SAMPLER_VERTICES[] = {
0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 1.0f,
1.0f, 0.0f,
};
const GLfloat LEFTSCREEN_VERTICES[] = {
-1.0f, 1.0f,
-1.0f, -1.0f,
0.0f, 1.0f,
0.0f, -1.0f,
};
const GLfloat RIGHTSCREEN_VERTICES[] = {
0.0f, 1.0f,
0.0f, -1.0f,
1.0f, 1.0f,
1.0f, -1.0f,
};
static const char SPHERE_VERTEXSHADER[] =
"uniform mat4 matrix;\n"
"varying vec2 interp_tc;\n"
"attribute vec4 in_pos;\n"
"attribute vec2 in_tc;\n"
"void main() {\n"
" gl_Position = matrix * in_pos;\n"
" interp_tc = in_tc;\n"
"}\n";
static const char SPHERE_FRAGMENTSHADER[] =
"precision mediump float;\n"
"varying vec2 interp_tc;\n"
"uniform sampler2D y_tex;\n"
"uniform sampler2D u_tex;\n"
"uniform sampler2D v_tex;\n"
"void main() {\n"
" float y = 1.164 * (texture2D(y_tex, interp_tc).r - 0.0625);\n"
" float u = texture2D(u_tex, interp_tc).r - 0.5;\n"
" float v = texture2D(v_tex, interp_tc).r - 0.5;\n"
" gl_FragColor = vec4(y + 1.596 * v, "
" y - 0.391 * u - 0.813 * v, "
" y + 2.018 * u, "
" 1.0);\n"
"
"}\n";
static const char SCREEN_VERTEXSHADER[] =
"varying vec2 interp_tc;\n"
"attribute vec4 in_pos;\n"
"attribute vec2 in_tc;\n"
"void main() {\n"
" gl_Position = in_pos;\n"
" interp_tc = in_tc;\n"
"}\n";
static const char SCREEN_FRAGMENTSHADER[] =
"precision mediump float;\n"
"varying vec2 interp_tc;\n"
"uniform sampler2D texture;\n"
"void main() {\n"
"gl_FragColor = texture2D(texture, interp_tc);\n"
"}\n";
void addShader(int type, const char* source, int program) {
int shader = glCreateShader(type);
glShaderSource(shader, 1, &source, NULL);
glCompileShader(shader);
GLint compiled = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
glAttachShader(program, shader);
glDeleteShader(shader);
}
GLHelper::GLHelper() {
myCamera = new MyGLCamera(FOVY,0,zNear,zFar);
float defaultPos[6] = {0.0,0.0,0.0,0.0,0.0,0.0};
myCamera->setModelPosition(defaultPos);
}
GLHelper::~GLHelper() {
close();
free(vertexCoordinates);
free(uvCoordinates);
}
void GLHelper::setWindow(ANativeWindow *window) {
_window = window;
return;
}
bool GLHelper::setupMemories() {
this->numberOfPatches = 64;
this->vertexCount = this->numberOfPatches * this->numberOfPatches/2 * 6;
this->vertexCoordinates = (float *)malloc(this->vertexCount * 3 * sizeof(float));
if (! this->vertexCoordinates) {
return false;
}
this->uvCoordinates = (float *)malloc(this->vertexCount * 2 * sizeof(float));
if (! this->uvCoordinates) {
return false;
}
return true;
}
bool GLHelper::init()
{
// Init EGL Context.
EGLBoolean returnValue;
EGLint majorVersion;
EGLint minorVersion;
EGLConfig myConfig = {0};
EGLint numConfig = 0;
EGLint format = 0;
EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
EGLint s_configAttribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE };
const EGLint RGB888Config[] = {
EGL_BUFFER_SIZE, 24,
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8,
EGL_DEPTH_SIZE, 0,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE
};
dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (dpy == EGL_NO_DISPLAY) {
printf("eglGetDisplay returned EGL_NO_DISPLAY.\n");
return false;
}
returnValue = eglInitialize(dpy, &majorVersion, &minorVersion);
if (returnValue != EGL_TRUE) {
printf("eglInitialize failed\n");
return false;
}
returnValue = eglChooseConfig(dpy, RGB888Config, &myConfig, 1, &numConfig);
if (returnValue != EGL_TRUE || numConfig != 1) {
printf("eglInitialize failed\n");
return false;
}
if (! eglGetConfigAttrib(dpy,myConfig,EGL_NATIVE_VISUAL_ID,&format) ) {
return false;
}
ANativeWindow_setBuffersGeometry(_window,0,0,format);
surface = eglCreateWindowSurface(dpy, myConfig, static_cast<EGLNativeWindowType>(_window), NULL);
if (surface == EGL_NO_SURFACE) {
return false;
}
context = eglCreateContext(dpy, myConfig, EGL_NO_CONTEXT, context_attribs);
if (context == EGL_NO_CONTEXT) {
return false;
}
returnValue = eglMakeCurrent(dpy, surface, surface, context);
if (returnValue != EGL_TRUE) {
return false;
}
eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
myCamera->setAspectRatio((float)(w/2)/h);
if (!setupMemories()) {
LOGE("Could not set up neccessary numbers.");
return false;
}
if(!setupGraphics()) {
LOGE( "Could not set up graphics.\n");
return false;
}
if(! setupTextures()) {
LOGE( "Could not set up Textures.\n");
return false;
}
if(! setupVertices()) {
LOGE( "Could not set up vertices");
}
if(! setupMatrix()) {
LOGE( "Could not set up matrix");
}
if (! setupFrameBuffer(w,h,leftEyeFrameBufferDesc)) {
LOGE("Error: could not create framebuffer for left eye");
}
if (! setupFrameBuffer(w, h, rightEyeFrameBufferDesc)) {
LOGE("Error: could not create framebuffer for right eye");
}
eglSwapInterval(dpy,0);
return true;
}
void GLHelper::close() {
eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(dpy, context);
eglDestroySurface(dpy, surface);
eglTerminate(dpy);
dpy = EGL_NO_DISPLAY;
surface = EGL_NO_SURFACE;
context = EGL_NO_CONTEXT;
if (sphereProgram) {
glDeleteProgram(sphereProgram);
}
if (leftScreenProgram) {
glDeleteProgram(leftScreenProgram);
}
if (rightScreenProgram) {
glDeleteProgram(rightScreenProgram);
}
glDeleteRenderbuffers(1, &leftEyeFrameBufferDesc.m_nDepthBufferID);
glDeleteTextures(1, &leftEyeFrameBufferDesc.m_nRenderTextureID);
glDeleteFramebuffers(1, &leftEyeFrameBufferDesc.m_nRenderFramebufferID);
glDeleteRenderbuffers(1, &rightEyeFrameBufferDesc.m_nDepthBufferID);
glDeleteTextures(1, &rightEyeFrameBufferDesc.m_nRenderTextureID);
glDeleteFramebuffers(1, &rightEyeFrameBufferDesc.m_nRenderFramebufferID);
return;
}
bool GLHelper::setupGraphics() {
glCheckError();
sphereProgram = glCreateProgram();
if (!sphereProgram) {
return false;
}
addShader(GL_VERTEX_SHADER, SPHERE_VERTEXSHADER, sphereProgram);
addShader(GL_FRAGMENT_SHADER, SPHERE_FRAGMENTSHADER, sphereProgram);
glLinkProgram(sphereProgram);
GLint linkStatus = GL_FALSE;
glGetProgramiv(sphereProgram, GL_LINK_STATUS, &linkStatus);
if (linkStatus != GL_TRUE) {
LOGE("Link sphereProgram failed.");
return false;
}
leftScreenProgram = glCreateProgram();
if (!leftScreenProgram) {
return false;
}
linkStatus = GL_FALSE;
addShader(GL_VERTEX_SHADER, SCREEN_VERTEXSHADER, leftScreenProgram);
addShader(GL_FRAGMENT_SHADER, SCREEN_FRAGMENTSHADER, leftScreenProgram);
glLinkProgram(leftScreenProgram);
glGetProgramiv(leftScreenProgram, GL_LINK_STATUS, &linkStatus);
if (linkStatus != GL_TRUE) {
LOGE("Link leftScreenProgram failed.");
return false;
}
rightScreenProgram = glCreateProgram();
if (! rightScreenProgram) {
return false;
}
linkStatus = GL_FALSE;
addShader(GL_VERTEX_SHADER, SCREEN_VERTEXSHADER, rightScreenProgram);
addShader(GL_FRAGMENT_SHADER, SCREEN_FRAGMENTSHADER, rightScreenProgram);
glLinkProgram(rightScreenProgram);
glGetProgramiv(rightScreenProgram,GL_LINK_STATUS, &linkStatus);
if (linkStatus != GL_TRUE) {
LOGE("link rightScreenProgram failed.");
return false;
}
return true;
}
bool GLHelper::setupTextures() {
glUseProgram(sphereProgram);
glGenTextures(3, yuvTextures);
for (int i = 0; i < 3; i++) {
glUniform1i(glGetUniformLocation(sphereProgram, TEXTURE_UNIFORMS[i]), i);
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, yuvTextures[i]);
glTexParameterf(GL_TEXTURE_2D,
GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D,
GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D,
GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D,
GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
glUseProgram(0);
return true;
}
bool GLHelper::setupVertices() {
int radius = 10;
int pieces = this->numberOfPatches;
int half_pieces = this->numberOfPatches/2;
double step_z = M_PI/(half_pieces);
double step_xy = step_z;
double angle_z;
double angle_xy;
float z[4] = {0.0f};
float x[4] = {0.0f};
float y[4] = {0.0f};
float u[4] = {0.0f};
float v[4] = {0.0f};
int m = 0, n = 0;
for(int i = 0; i < half_pieces; i++) {
angle_z = i * step_z;
for(int j = 0; j < pieces;j ++ ) {
angle_xy = j * step_xy;
z[0] = (float)(radius * sin(angle_z)*cos(angle_xy));
x[0]= (float)(radius*sin(angle_z)*sin(angle_xy));
y[0]= (float)(radius*cos(angle_z));
u[0]= (float)j / pieces;
v[0]= (float) i/half_pieces;
z[1] = (float)(radius*sin(angle_z+step_z)*cos(angle_xy));
x[1] = (float)(radius*sin(angle_z + step_z)*sin(angle_xy));
y[1] = (float)(radius*cos(angle_z+step_z));
u[1] = (float)j/pieces;
v[1] = (float)(i+1)/half_pieces;
z[2] = (float)(radius*sin(angle_z+step_z)*cos(angle_xy+step_xy));
x[2] = (float)(radius *sin(angle_z+step_z)*sin(angle_xy+step_xy));
y[2] = (float)(radius*cos(angle_z+step_z));
u[2] = (float)(j+1)/pieces;
v[2] = (float)(i+1)/half_pieces;
z[3] = (float)(radius*sin(angle_z)*cos(angle_xy+step_xy));
x[3] = (float)(radius*sin(angle_z)*sin(angle_xy+step_xy));
y[3] = (float)(radius*cos(angle_z));
u[3] = (float)(j+1)/pieces;
v[3] = (float)i/half_pieces;
this->vertexCoordinates[m++] = x[0];
this->vertexCoordinates[m++] = y[0];
this->vertexCoordinates[m++] = z[0];
this->uvCoordinates[n++] = u[0];
this->uvCoordinates[n++] = v[0];
this->vertexCoordinates[m++] = x[1];
this->vertexCoordinates[m++] = y[1];
this->vertexCoordinates[m++] = z[1];
this->uvCoordinates[n++] = u[1];
this->uvCoordinates[n++] = v[1];
this->vertexCoordinates[m++] = x[2];
this->vertexCoordinates[m++] = y[2];
this->vertexCoordinates[m++] = z[2];
this->uvCoordinates[n++] = u[2];
this->uvCoordinates[n++] = v[2];
this->vertexCoordinates[m++] = x[2];
this->vertexCoordinates[m++] = y[2];
this->vertexCoordinates[m++] = z[2];
this->uvCoordinates[n++] = u[2];
this->uvCoordinates[n++] = v[2];
this->vertexCoordinates[m++] = x[3];
this->vertexCoordinates[m++] = y[3];
this->vertexCoordinates[m++] = z[3];
this->uvCoordinates[n++] = u[3];
this->uvCoordinates[n++] = v[3];
this->vertexCoordinates[m++] = x[0];
this->vertexCoordinates[m++] = y[0];
this->vertexCoordinates[m++] = z[0];
this->uvCoordinates[n++] = u[0];
this->uvCoordinates[n++] = v[0];
}
}
gSpherePositionAttribPointer = glGetAttribLocation(sphereProgram, "in_pos");
gSphereSamplerAttribPointer = glGetAttribLocation(sphereProgram, "in_tc");
gSphereMatrixUniformPointer = glGetUniformLocation(sphereProgram,"matrix");
return true;
}
bool GLHelper::setupMatrix() {
return true;
}
void GLHelper::setRotationMatrix(float *rotation, int length) {
if(length != 16) {
LOGE("wrong rotation matrix");
return;
}
myCamera->setRotateModel(rotation);
}
bool GLHelper::setupFrameBuffer(int screenWidth, int screenHeight, FramebufferDesc &framebufferDesc) {
glCheckError();
// Generate Framebuffer Object ID
glGenFramebuffers(1, &framebufferDesc.m_nRenderFramebufferID);
glGenTextures(1, &framebufferDesc.m_nRenderTextureID);
glGenRenderbuffers(1, &framebufferDesc.m_nDepthBufferID);
// Generate Texture and setup Texture Parameters
glBindTexture(GL_TEXTURE_2D,framebufferDesc.m_nRenderTextureID);
// Framebuffer 里的Texture的宽高通常为屏幕的宽高
glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,screenWidth,screenHeight,0,GL_RGB,GL_UNSIGNED_BYTE,NULL);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_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);
glBindRenderbuffer(GL_RENDERBUFFER,framebufferDesc.m_nDepthBufferID);
glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH_COMPONENT16,screenWidth,screenHeight);
glBindFramebuffer(GL_FRAMEBUFFER, framebufferDesc.m_nRenderFramebufferID);
glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,framebufferDesc.m_nRenderTextureID,0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_RENDERBUFFER,framebufferDesc.m_nDepthBufferID);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
LOGE("Error in creating framebuffer object");
return false;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return true;
}
void GLHelper::renderToTexture(int yuvFrameWidth, int yuvFrameHeight, unsigned char *YData,
unsigned char *UData, unsigned char *VData, EYE eye) {
if (eye == LEFT_EYE) {
glBindFramebuffer(GL_FRAMEBUFFER, leftEyeFrameBufferDesc.m_nRenderFramebufferID);
} else if (eye == RIGHT_EYE) {
glBindFramebuffer(GL_FRAMEBUFFER, rightEyeFrameBufferDesc.m_nRenderFramebufferID);
}
glUseProgram(sphereProgram);
glm::mat4 mvpMat = myCamera->GetMVP(eye);
float mat[16] = {0.0f};
const float *pSrc = (const float *)glm::value_ptr(mvpMat);
for(int i = 0; i < 16;i++) {
mat[i] = pSrc[i];
}
glUniformMatrix4fv(gSphereMatrixUniformPointer,1,GL_FALSE,mat);
n2 = now_ms();
if (eye == LEFT_EYE) {
LOGE("matrix convert costs %f ms.",n2-n1);
}
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glVertexAttribPointer(gSpherePositionAttribPointer,3,GL_FLOAT,GL_FALSE,0,this->vertexCoordinates);
glEnableVertexAttribArray(gSpherePositionAttribPointer);
glVertexAttribPointer(gSphereSamplerAttribPointer,2,GL_FLOAT,GL_FALSE,0,this->uvCoordinates);
glEnableVertexAttribArray(gSphereSamplerAttribPointer);
unsigned char *yuvPlanes[3];
yuvPlanes[0] = YData;
yuvPlanes[1] = UData;
yuvPlanes[2] = VData;
glViewport(0,0,w,h);
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
for (int i = 0; i < 3; i++) {
int w = (i == 0 ? yuvFrameWidth : yuvFrameWidth / 2);
int h = (i == 0 ? yuvFrameHeight : yuvFrameHeight / 2);
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, yuvTextures[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, yuvPlanes[i]);
}
glDrawArrays(GL_TRIANGLES,0,this->vertexCount);
glBindTexture(GL_TEXTURE_2D,0);
glBindFramebuffer(GL_FRAMEBUFFER,0);
glUseProgram(0);
}
void GLHelper::renderToScreen() {
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0,0,w,h);
glUseProgram(leftScreenProgram);
gLeftScreenPositionAttribPointer = glGetAttribLocation(leftScreenProgram, "in_pos");
glEnableVertexAttribArray(gLeftScreenPositionAttribPointer);
glVertexAttribPointer(gLeftScreenPositionAttribPointer,2,GL_FLOAT,GL_FALSE,0,LEFTSCREEN_VERTICES);
gLeftScreenSamplerAttribPointer = glGetAttribLocation(leftScreenProgram, "in_tc");
glEnableVertexAttribArray(gLeftScreenSamplerAttribPointer);
glVertexAttribPointer(gLeftScreenSamplerAttribPointer,2,GL_FLOAT,GL_FALSE,0,SAMPLER_VERTICES);
glUniform1i(glGetUniformLocation(leftScreenProgram,"texture"),3);
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, leftEyeFrameBufferDesc.m_nRenderTextureID);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
glUseProgram(rightScreenProgram);
gRightScreenPositionAttribPointer = glGetAttribLocation(rightScreenProgram, "in_pos");
glEnableVertexAttribArray(gRightScreenPositionAttribPointer);
glVertexAttribPointer(gRightScreenPositionAttribPointer, 2, GL_FLOAT, GL_FALSE, 0, RIGHTSCREEN_VERTICES);
gRightScreenSamplerAttribPointer = glGetAttribLocation(rightScreenProgram, "in_tc");
glEnableVertexAttribArray(gRightScreenSamplerAttribPointer);
glVertexAttribPointer(gRightScreenSamplerAttribPointer,2,GL_FLOAT,GL_FALSE,0,SAMPLER_VERTICES);
glUniform1i(glGetUniformLocation(rightScreenProgram,"texture"),4);
glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_2D, rightEyeFrameBufferDesc.m_nRenderTextureID);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
}
void GLHelper::drawFrame(int frameWidth, int frameHeight, unsigned char *YData,
unsigned char *UData,
unsigned char *VData) {
renderToTexture(frameWidth,frameHeight,YData,UData,VData,LEFT_EYE);
renderToTexture(frameWidth,frameHeight,YData,UData,VData,RIGHT_EYE);
renderToScreen();
eglSwapBuffers(dpy,surface);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment