-
-
Save Volcanoscar/4b8d88a4c54ca4d2fbc225d6ccf765ba to your computer and use it in GitHub Desktop.
OpenGL RGB > YUV420P shader/class (doesn't do much more. implementation/usage is up to you)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <assert.h> | |
#include <roxlu/core/Utils.h> | |
#include <roxlu/core/Log.h> | |
#include "YUV420PGrabber.h" | |
YUV420PGrabber::YUV420PGrabber() | |
:y_prog(0) | |
,y_vert(0) | |
,y_frag(0) | |
,uv_prog(0) | |
,uv_frag(0) | |
,scene_fbo(0) | |
,scene_depth(0) | |
,scene_tex(0) | |
,y_tex(0) | |
,u_tex(0) | |
,v_tex(0) | |
,window_w(0) | |
,window_h(0) | |
,video_w(0) | |
,video_h(0) | |
,uv_w(0) | |
,uv_h(0) | |
,vbo(0) | |
,vao(0) | |
{ | |
} | |
YUV420PGrabber::~YUV420PGrabber() { | |
if(y_vert) { | |
glDeleteShader(y_vert); | |
} | |
if(y_frag) { | |
glDeleteShader(y_frag); | |
} | |
if(uv_frag) { | |
glDeleteShader(uv_frag); | |
} | |
if(y_prog) { | |
glDeleteProgram(y_prog); | |
} | |
if(uv_prog) { | |
glDeleteProgram(uv_prog); | |
} | |
if(scene_fbo) { | |
glDeleteFramebuffers(1, &scene_fbo); | |
} | |
if(scene_depth) { | |
glDeleteRenderbuffers(1, &scene_depth); | |
} | |
if(scene_tex) { | |
glDeleteTextures(1, &scene_tex); | |
} | |
if(y_tex) { | |
glDeleteTextures(1, &y_tex); | |
} | |
if(u_tex) { | |
glDeleteTextures(1, &u_tex); | |
} | |
if(v_tex) { | |
glDeleteTextures(1, &v_tex); | |
} | |
if(vbo) { | |
glDeleteBuffers(1, &vbo); | |
} | |
if(vao) { | |
glDeleteVertexArrays(1, &vao); | |
} | |
window_w = 0; | |
window_h = 0; | |
video_w = 0; | |
video_h = 0; | |
uv_w = 0; | |
uv_h = 0; | |
y_prog = 0; | |
y_vert = 0; | |
y_frag = 0; | |
uv_prog = 0; | |
uv_frag = 0; | |
scene_fbo = 0; | |
scene_depth = 0; | |
scene_tex = 0; | |
y_tex = 0; | |
u_tex = 0; | |
v_tex = 0; | |
vbo = 0; | |
vao = 0; | |
} | |
bool YUV420PGrabber::setup(int winW, int winH, int vidW, int vidH) { | |
assert(winW); | |
assert(winH); | |
assert(vidW); | |
assert(vidH); | |
window_w = winW; | |
window_h = winH; | |
video_w = vidW; | |
video_h = vidH; | |
uv_w = video_w * 0.25; | |
uv_h = video_h * 0.25; | |
if(!setupFBO()) { | |
return false; | |
} | |
if(!setupShaders()) { | |
return false; | |
} | |
if(!setupBuffers()) { | |
return false; | |
} | |
return true; | |
} | |
bool YUV420PGrabber::setupFBO() { | |
assert(window_h && window_w); | |
assert(video_w && video_h); | |
glGenFramebuffers(1, &scene_fbo); | |
glBindFramebuffer(GL_FRAMEBUFFER, scene_fbo); | |
glGenRenderbuffers(1, &scene_depth); | |
glBindRenderbuffer(GL_RENDERBUFFER, scene_depth); | |
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, window_w, window_h); | |
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, scene_depth); | |
glGenTextures(1, &scene_tex); | |
glBindTexture(GL_TEXTURE_2D, scene_tex); | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, window_w, window_h, 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); | |
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, scene_tex, 0); | |
glGenTextures(1, &y_tex); | |
glBindTexture(GL_TEXTURE_2D, y_tex); | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, video_w, video_h, 0, GL_RED, 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); | |
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, y_tex, 0); | |
glGenTextures(1, &u_tex); | |
glBindTexture(GL_TEXTURE_2D, u_tex); | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, uv_w, uv_h, 0, GL_RED, 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); | |
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, u_tex, 0); | |
glGenTextures(1, &v_tex); | |
glBindTexture(GL_TEXTURE_2D, v_tex); | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, uv_w, uv_h, 0, GL_RED, 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); | |
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, v_tex, 0); | |
glBindFramebuffer(GL_FRAMEBUFFER, 0); | |
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); | |
if(status != GL_FRAMEBUFFER_COMPLETE) { | |
RX_ERROR("Framebuffer is not yet complete"); | |
return false; | |
} | |
return true; | |
} | |
bool YUV420PGrabber::setupShaders() { | |
assert(window_w && window_h); | |
assert(video_w && video_h); | |
// y - shader | |
y_vert = glCreateShader(GL_VERTEX_SHADER); | |
glShaderSource(y_vert, 1, &YUV420P_Y_VS, NULL); | |
glCompileShader(y_vert); eglGetShaderInfoLog(y_vert); | |
y_frag = glCreateShader(GL_FRAGMENT_SHADER); | |
glShaderSource(y_frag, 1, &YUV420P_Y_FS, NULL); | |
glCompileShader(y_frag); eglGetShaderInfoLog(y_frag); | |
y_prog = glCreateProgram(); | |
glAttachShader(y_prog, y_vert); | |
glAttachShader(y_prog, y_frag); | |
glBindAttribLocation(y_prog, 0, "a_pos"); | |
glBindAttribLocation(y_prog, 1, "a_tex"); | |
glLinkProgram(y_prog); eglGetShaderLinkLog(y_prog); | |
glUseProgram(y_prog); | |
GLint u_tex = glGetUniformLocation(y_prog, "u_tex"); | |
if(u_tex < 0) { | |
RX_ERROR("Error while trying to get u_tex."); | |
return false; | |
} | |
glUniform1i(u_tex, 0); | |
// uv - shader | |
uv_frag = glCreateShader(GL_FRAGMENT_SHADER); | |
glShaderSource(uv_frag, 1, &YUV420P_UV_FS, NULL); | |
glCompileShader(uv_frag); eglGetShaderInfoLog(uv_frag); | |
uv_prog = glCreateProgram(); | |
glAttachShader(uv_prog, y_vert); | |
glAttachShader(uv_prog, uv_frag); | |
glBindAttribLocation(uv_prog, 0, "a_pos"); | |
glBindAttribLocation(uv_prog, 1, "a_tex"); | |
glBindFragDataLocation(uv_prog, 0, "u_col"); | |
glBindFragDataLocation(uv_prog, 1, "v_col"); | |
glLinkProgram(uv_prog); eglGetShaderLinkLog(uv_prog); | |
glUseProgram(uv_prog); | |
u_tex = glGetUniformLocation(uv_prog, "u_tex"); | |
if(u_tex < 0) { | |
RX_ERROR("Cannot get u_tex for uv program"); | |
return false; | |
} | |
glUniform1i(u_tex, 0); | |
return true; | |
} | |
bool YUV420PGrabber::setupBuffers() { | |
assert(window_w && window_h); | |
assert(video_w && video_h); | |
assert(y_prog && y_vert && y_frag); | |
GLfloat vertices[] = { | |
-1.0, -1.0, 0.0, 0.0, // a | |
1.0, -1.0, 1.0, 0.0, // b | |
1.0, 1.0, 1.0, 1.0, // c | |
-1.0, -1.0, 0.0, 0.0,// a | |
1.0, 1.0, 1.0, 1.0, // c | |
-1.0, 1.0, 0.0, 1.0 // d | |
}; | |
glGenBuffers(1, &vbo); | |
glBindBuffer(GL_ARRAY_BUFFER, vbo); | |
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, vertices, GL_STATIC_DRAW); | |
glGenVertexArrays(1, &vao); | |
glBindVertexArray(vao); | |
glBindBuffer(GL_ARRAY_BUFFER, vbo); | |
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4, (GLvoid*)NULL); | |
glEnableVertexAttribArray(0); | |
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4, (GLvoid*)8); | |
glEnableVertexAttribArray(1); | |
return true; | |
} | |
void YUV420PGrabber::beginGrab() { | |
assert(scene_fbo); | |
assert(window_w); | |
assert(window_h); | |
glViewport(0, 0, window_w, window_h); | |
GLenum bufs[] = { GL_COLOR_ATTACHMENT0 }; | |
glBindFramebuffer(GL_FRAMEBUFFER, scene_fbo); | |
glDrawBuffers(1, bufs); | |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
} | |
void YUV420PGrabber::endGrab() { | |
assert(scene_fbo); | |
// render Y | |
GLenum y_buf[] = { GL_COLOR_ATTACHMENT1 }; | |
glDrawBuffers(1, y_buf); | |
glClear(GL_COLOR_BUFFER_BIT); | |
glViewport(0,0,video_w, video_h); | |
glActiveTexture(GL_TEXTURE0); | |
glBindTexture(GL_TEXTURE_2D, scene_tex); | |
glBindVertexArray(vao); | |
glUseProgram(y_prog); | |
glDrawArrays(GL_TRIANGLES, 0, 6); | |
// render U + V | |
GLenum uv_bufs[] = { GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 }; | |
glDrawBuffers(2, uv_bufs); | |
glClear(GL_COLOR_BUFFER_BIT); | |
glViewport(0,0,uv_w, uv_h); | |
glUseProgram(uv_prog); | |
glDrawArrays(GL_TRIANGLES, 0, 6); | |
glBindFramebuffer(GL_FRAMEBUFFER, 0); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef ROXLU_YUV420P_GRABBER_H | |
#define ROXLU_YUV420P_GRABBER_H | |
#include <roxlu/opengl/GL.h> | |
static const char* YUV420P_Y_VS = "" | |
"#version 150\n" | |
"in vec4 a_pos;" | |
"in vec2 a_tex;" | |
"out vec2 v_tex;" | |
"void main() {" | |
" gl_Position = a_pos; " | |
" v_tex = a_tex;" | |
"}"; | |
static const char* YUV420P_Y_FS = "" | |
"#version 150\n" | |
"uniform sampler2D u_tex;" | |
"in vec2 v_tex; " | |
"out float fragcol;" | |
"float Y(vec3 c) {" | |
" float result = (0.257 * c.r) + (0.504 * c.g) + (0.098 * c.b) + 0.0625;" | |
" return result;" | |
"}" | |
"void main() {" | |
" fragcol = Y(texture(u_tex, v_tex).rgb);" | |
"}"; | |
static const char* YUV420P_UV_FS = "" | |
"#version 150\n" | |
"uniform sampler2D u_tex;" | |
"in vec2 v_tex;" | |
"out float u_col;" | |
"out float v_col;" | |
"float V(vec3 c) {" | |
" float result = (0.439 * c.r) - (0.368 * c.g) - (0.071 * c.b) + 0.5; " | |
" return result;" | |
"}" | |
"float U(vec3 c) {" | |
" float result = -(0.148 * c.r) - (0.291 * c.g) + (0.439 * c.b) + 0.5; " | |
" return result; " | |
"}" | |
"void main() {" | |
" vec3 col = texture(u_tex, v_tex).rgb;" | |
" u_col = U(col); " | |
" v_col = V(col); " | |
"}"; | |
class YUV420PGrabber { | |
public: | |
YUV420PGrabber(); | |
~YUV420PGrabber(); | |
bool setup(int windowW, int windowH, int videoW, int videoH); | |
void beginGrab(); | |
void endGrab(); | |
private: | |
bool setupFBO(); | |
bool setupShaders(); | |
bool setupBuffers(); | |
public: | |
int window_w; | |
int window_h; | |
int video_w; /* the resulting video width, the y plane has the same width */ | |
int video_h; /* the resulting video height, the y plane has the same height */ | |
int uv_w; /* the width of the u and v planes */ | |
int uv_h; /* the height of the u and v planes */ | |
GLuint y_prog; | |
GLuint y_vert; | |
GLuint y_frag; | |
GLuint uv_prog; | |
GLuint uv_frag; /* fragment shader for uv pass, we reuse y_vert */ | |
GLuint scene_fbo; | |
GLuint scene_depth; | |
GLuint scene_tex; | |
GLuint y_tex; | |
GLuint u_tex; | |
GLuint v_tex; | |
GLuint vbo; | |
GLuint vao; | |
}; | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment