使用 OpenGL ES 3 和 EGL、帧缓冲区对象或 EGL 像素缓冲区进行离屏渲染?
我尝试将带有纹理的四边形渲染到带有纹理 GL_COLOR_ATTACHMENT0 附件的帧缓冲区对象中。然后我调用 glReadPixels 将帧缓冲区的内容复制回主内存。
我正在使用 EGL 提供 OpenGL 上下文。
我注意到,除非我创建一个 EGL 像素缓冲区(通过调用eglCreatePbufferSurface)并将其作为当前上下文的一部分,否则glReadPixels 将返回一个完全黑色的缓冲区。
我已在此处附上我的代码,该代码会产生当前编写的所需结果。但是,如果我将 eglMakeCurrent(display, surface, surface, context);
行更改为 eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, context);
,那么我得到的输出是完全黑色的。
我需要 EGL 像素缓冲区和 OpenGL FBO 来启用离屏渲染,这正常吗?
我一直在读的一本教科书,即 Dan Ginsburg 编写的《OpenGL ES 3.0 编程指南》,似乎表明 OpenGL FBO 是 EGL 像素缓冲区的替代品,我们不应该两者都需要。如果是这样,我的测试代码做错了什么?
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <vector>
#include <algorithm>
#include "dlib/image_io.h"
#include "dlib/image_transforms.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include <EGL/egl.h>
#include "GLES3/gl31.h"
using namespace std;
namespace {
void assertOpenGLError(const std::string& msg) {
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
stringstream s;
s << "OpenGL error 0x" << std::hex << error << " at " << msg;
throw runtime_error(s.str());
}
}
void assertEGLError(const std::string& msg) {
EGLint error = eglGetError();
if (error != EGL_SUCCESS) {
stringstream s;
s << "EGL error 0x" << std::hex << error << " at " << msg;
throw runtime_error(s.str());
}
}
template<typename T>
unsigned char SafeUCharCast(T input);
template<>
unsigned char SafeUCharCast<float>(float input)
{
return static_cast<unsigned char>(std::clamp<float>(input*255, 0.f, 255.f));
}
template<>
unsigned char SafeUCharCast<unsigned char>(unsigned char input)
{
return input;
}
template<typename T>
void SaveRGBImageToDisk(std::string fileName, size_t width, size_t height, const T* dataRaw)
{
constexpr size_t inputDepth = 3;
dlib::array2d<dlib::rgb_pixel> img(height, width);
int idx = 0;
for(auto& pix: img)
{
pix.red = SafeUCharCast(dataRaw[idx]);
pix.green = SafeUCharCast(dataRaw[idx+1]);
pix.blue = SafeUCharCast(dataRaw[idx+2]);
idx += inputDepth;
}
dlib::save_png(img, fileName+".png");
}
const static std::string vertexShader =
"#version 300 es\n"
"layout(location = 0) in vec4 position;\n"
"layout(location = 1) in vec2 texCoord;\n"
"out vec2 v_TexCoord;\n"
"uniform mat4 u_MVP;\n"
"void main() {\n"
" gl_Position = u_MVP*position;\n"
" v_TexCoord = texCoord;\n"
"}\n";
const static std::string fragmentShader =
"#version 300 es\n"
"precision highp float;\n"
"precision highp sampler2D;\n"
"layout(location = 0) out vec4 color;\n"
"in vec2 v_TexCoord;\n"
"uniform sampler2D u_Texture;\n"
"void main() {\n"
" vec4 texColor = texture(u_Texture, v_TexCoord);\n"
" color = texColor;\n"
"}\n";
unsigned int CompileShader(unsigned int type, const std::string& source) {
unsigned int id = glCreateShader(type);
const char* src = source.c_str();
glShaderSource(id, 1, &src, nullptr);
glCompileShader(id);
int result;
glGetShaderiv(id, GL_COMPILE_STATUS, &result);
if(result == GL_FALSE) {
int length;
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
char* message = (char*) alloca(length*sizeof(char));
glGetShaderInfoLog(id, length, &length, message);
std::cout << "Failed to compile "<< (type == GL_VERTEX_SHADER ? "vertex" : "fragment")<<" shader"<<std::endl;
std::cout << message << std::endl;
glDeleteShader(id);
return 0;
}
return id;
}
unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader){
unsigned int program = glCreateProgram();
unsigned int vs = CompileShader(GL_VERTEX_SHADER, vertexShader);
unsigned int fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);
glAttachShader(program, vs);
assertOpenGLError("glAttachShader");
glAttachShader(program, fs);
assertOpenGLError("glAttachShader");
glLinkProgram(program);
assertOpenGLError("glLinkProgram");
glValidateProgram(program);
assertOpenGLError("glValidateProgram");
glDeleteShader(vs);
glDeleteShader(fs);
return program;
}
std::vector<unsigned char> LoadImageInterleaved(std::string fileName, size_t& width, size_t& height)
{
constexpr auto numChannels = 3;
dlib::array2d<dlib::rgb_pixel> img;
dlib::load_png(img, fileName);
width = img.nc();
height = img.nr();
std::vector<unsigned char> inputBuffer(img.nr()*img.nc()*numChannels);
unsigned char* inputBufferRaw = &inputBuffer[0];
for(auto pix: img)
{
inputBufferRaw[0] = pix.red;
inputBufferRaw[1] = pix.green;
inputBufferRaw[2] = pix.blue;
inputBufferRaw += numChannels;
}
return inputBuffer;
}
}
int main() {
unsetenv( "DISPLAY" );
size_t imgWidth, imgHeight;
auto imgData = LoadImageInterleaved("../res/textures/penguin.png", imgWidth, imgHeight);
/*
* EGL initialization and OpenGL context creation.
*/
EGLDisplay display;
EGLConfig config;
EGLContext context;
EGLSurface surface;
EGLint num_config;
const EGLint DISPLAY_ATTRIBS[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
EGL_BLUE_SIZE, 5, EGL_GREEN_SIZE, 6, EGL_RED_SIZE, 5,
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_NONE
};
const EGLint CONTEXT_ATTRIBS[] = {
EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE
};
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
assertEGLError("eglGetDisplay");
eglInitialize(display, nullptr, nullptr);
assertEGLError("eglInitialize");
eglChooseConfig(display, DISPLAY_ATTRIBS, &config, 1, &num_config);
assertEGLError("eglChooseConfig");
eglBindAPI(EGL_OPENGL_API);
assertEGLError("eglBindAPI");
context = eglCreateContext(display, config, EGL_NO_CONTEXT, CONTEXT_ATTRIBS);
assertEGLError("eglCreateContext");
constexpr int winWidth = 256;
constexpr int winHeight = 256;
EGLint SURFACE_ATTRIBS[] = {
EGL_WIDTH, winWidth,
EGL_HEIGHT, winHeight,
EGL_NONE
};
surface = eglCreatePbufferSurface(display, config, SURFACE_ATTRIBS);
assertEGLError("eglCreatePbufferSurface");
eglMakeCurrent(display, surface, surface, context);
//eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, context);
assertEGLError("eglMakeCurrent");
std::string versionString = std::string((const char*)glGetString(GL_VERSION));
std::cout<<versionString<<std::endl;
//Enable blending
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
auto CreateTexture = [](GLuint& tex, int width, int height, void* data) {
glGenTextures(1, &tex);
assertOpenGLError("glGenTextures");
glBindTexture(GL_TEXTURE_2D, tex);
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);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
assertOpenGLError("glTexImage2D");
glBindTexture(GL_TEXTURE_2D, 0);
};
/*
* Create an OpenGL framebuffer as render target.
*/
GLuint frameBuffer;
glGenFramebuffers(1, &frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
assertOpenGLError("glBindFramebuffer");
/*
* Create a texture as color attachment.
*/
GLuint outputTex;
CreateTexture(outputTex, winWidth, winHeight, nullptr);
glBindTexture(GL_TEXTURE_2D, outputTex);
/*
* Attach the texture to the framebuffer.
*/
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, outputTex, 0);
assertOpenGLError("glFramebufferTexture2D");
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
throw std::runtime_error("frame buffer went kaput");
/*
* Create an OpenGL texture and load it with image data.
*/
GLuint inputTex;
CreateTexture(inputTex, imgWidth, imgHeight, imgData.data());
glBindTexture(GL_TEXTURE_2D, inputTex);
constexpr int textureSlot = 0;
glActiveTexture(GL_TEXTURE0+textureSlot);
assertOpenGLError("glActiveTexture");
//Build and use the shaders
auto program = CreateShader(vertexShader, fragmentShader);
glUseProgram(program);
assertOpenGLError("glUseProgram");
glUniform1i(glGetUniformLocation(program, "u_Texture"), textureSlot);
assertOpenGLError("glUniform1i, glGetUniformLocation");
glm::mat4 proj = glm::ortho(0.f, (float)winWidth, 0.f, (float)winHeight, -1.f, 1.f);
glUniformMatrix4fv(glGetUniformLocation(program, "u_MVP"), 1, GL_FALSE, &proj[0][0]);
assertOpenGLError("glUniformMatrix4fv, glGetUniformLocation");
//Define geometry to render and texture mapping
float positions[] = {0.f, 0.f, 0.f, 0.f,
winWidth, 0.f, 1.f, 0.f,
winWidth, winHeight, 1.f, 1.f,
0.f, winHeight, 0.f, 1.f
};
unsigned int indices[] = {
0, 1, 2,
2, 3, 0
};
GLuint ibo;
glGenBuffers(1, &ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (const void*) 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (const void*) (2*sizeof(GLfloat)));
/*
* Render something.
*/
//glClearColor(0.3, 0.8, 0.5, 1.0);
glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(unsigned int), GL_UNSIGNED_INT, nullptr);
//glClear(GL_COLOR_BUFFER_BIT);
glFlush();
/*
* Read the framebuffer and save to disk as an image
*/
std::vector<unsigned char> buf(winWidth*winHeight*3);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glReadPixels(0, 0, winWidth, winHeight, GL_RGB, GL_UNSIGNED_BYTE, buf.data());
assertOpenGLError("glReadPixels");
SaveRGBImageToDisk("output", winWidth, winHeight, buf.data());
/*
* Destroy context.
*/
glDeleteBuffers(1, &ibo);
glDeleteBuffers(1, &vbo);
glDeleteTextures(1, &inputTex);
glDeleteFramebuffers(1, &frameBuffer);
glDeleteTextures(1, &outputTex);
glDeleteProgram(program);
eglDestroySurface(display, surface);
assertEGLError("eglDestroySurface");
eglDestroyContext(display, context);
assertEGLError("eglDestroyContext");
eglTerminate(display);
assertEGLError("eglTerminate");
return 0;
}
I try to render a quad with texture into a framebuffer object with a texture GL_COLOR_ATTACHMENT0 attachment. Then I call glReadPixels
to copy the content of the framebuffer back to main memory.
I am using EGL to provide an OpenGL context.
What I noticed is that unless I create an EGL pixel buffer (by calling eglCreatePbufferSurface
) and make that part of the current context, glReadPixels
will return a buffer that is completely black.
I have attached my code here, and the code produces the desired result as it is currently written. However, if I changes the line eglMakeCurrent(display, surface, surface, context);
to eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, context);
, then the output I get is completely black.
Is it normal that I need both an EGL pixel buffer and OpenGL FBO to enable offscreen rendering?
A textbook I've been reading, OpenGL ES 3.0 Programming Guide by Dan Ginsburg, seems to suggest that OpenGL FBO is an alternative to EGL pixel buffer and we should not need both. If that's the case, what am I doing wrong in my test code?
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <vector>
#include <algorithm>
#include "dlib/image_io.h"
#include "dlib/image_transforms.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include <EGL/egl.h>
#include "GLES3/gl31.h"
using namespace std;
namespace {
void assertOpenGLError(const std::string& msg) {
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
stringstream s;
s << "OpenGL error 0x" << std::hex << error << " at " << msg;
throw runtime_error(s.str());
}
}
void assertEGLError(const std::string& msg) {
EGLint error = eglGetError();
if (error != EGL_SUCCESS) {
stringstream s;
s << "EGL error 0x" << std::hex << error << " at " << msg;
throw runtime_error(s.str());
}
}
template<typename T>
unsigned char SafeUCharCast(T input);
template<>
unsigned char SafeUCharCast<float>(float input)
{
return static_cast<unsigned char>(std::clamp<float>(input*255, 0.f, 255.f));
}
template<>
unsigned char SafeUCharCast<unsigned char>(unsigned char input)
{
return input;
}
template<typename T>
void SaveRGBImageToDisk(std::string fileName, size_t width, size_t height, const T* dataRaw)
{
constexpr size_t inputDepth = 3;
dlib::array2d<dlib::rgb_pixel> img(height, width);
int idx = 0;
for(auto& pix: img)
{
pix.red = SafeUCharCast(dataRaw[idx]);
pix.green = SafeUCharCast(dataRaw[idx+1]);
pix.blue = SafeUCharCast(dataRaw[idx+2]);
idx += inputDepth;
}
dlib::save_png(img, fileName+".png");
}
const static std::string vertexShader =
"#version 300 es\n"
"layout(location = 0) in vec4 position;\n"
"layout(location = 1) in vec2 texCoord;\n"
"out vec2 v_TexCoord;\n"
"uniform mat4 u_MVP;\n"
"void main() {\n"
" gl_Position = u_MVP*position;\n"
" v_TexCoord = texCoord;\n"
"}\n";
const static std::string fragmentShader =
"#version 300 es\n"
"precision highp float;\n"
"precision highp sampler2D;\n"
"layout(location = 0) out vec4 color;\n"
"in vec2 v_TexCoord;\n"
"uniform sampler2D u_Texture;\n"
"void main() {\n"
" vec4 texColor = texture(u_Texture, v_TexCoord);\n"
" color = texColor;\n"
"}\n";
unsigned int CompileShader(unsigned int type, const std::string& source) {
unsigned int id = glCreateShader(type);
const char* src = source.c_str();
glShaderSource(id, 1, &src, nullptr);
glCompileShader(id);
int result;
glGetShaderiv(id, GL_COMPILE_STATUS, &result);
if(result == GL_FALSE) {
int length;
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
char* message = (char*) alloca(length*sizeof(char));
glGetShaderInfoLog(id, length, &length, message);
std::cout << "Failed to compile "<< (type == GL_VERTEX_SHADER ? "vertex" : "fragment")<<" shader"<<std::endl;
std::cout << message << std::endl;
glDeleteShader(id);
return 0;
}
return id;
}
unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader){
unsigned int program = glCreateProgram();
unsigned int vs = CompileShader(GL_VERTEX_SHADER, vertexShader);
unsigned int fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);
glAttachShader(program, vs);
assertOpenGLError("glAttachShader");
glAttachShader(program, fs);
assertOpenGLError("glAttachShader");
glLinkProgram(program);
assertOpenGLError("glLinkProgram");
glValidateProgram(program);
assertOpenGLError("glValidateProgram");
glDeleteShader(vs);
glDeleteShader(fs);
return program;
}
std::vector<unsigned char> LoadImageInterleaved(std::string fileName, size_t& width, size_t& height)
{
constexpr auto numChannels = 3;
dlib::array2d<dlib::rgb_pixel> img;
dlib::load_png(img, fileName);
width = img.nc();
height = img.nr();
std::vector<unsigned char> inputBuffer(img.nr()*img.nc()*numChannels);
unsigned char* inputBufferRaw = &inputBuffer[0];
for(auto pix: img)
{
inputBufferRaw[0] = pix.red;
inputBufferRaw[1] = pix.green;
inputBufferRaw[2] = pix.blue;
inputBufferRaw += numChannels;
}
return inputBuffer;
}
}
int main() {
unsetenv( "DISPLAY" );
size_t imgWidth, imgHeight;
auto imgData = LoadImageInterleaved("../res/textures/penguin.png", imgWidth, imgHeight);
/*
* EGL initialization and OpenGL context creation.
*/
EGLDisplay display;
EGLConfig config;
EGLContext context;
EGLSurface surface;
EGLint num_config;
const EGLint DISPLAY_ATTRIBS[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
EGL_BLUE_SIZE, 5, EGL_GREEN_SIZE, 6, EGL_RED_SIZE, 5,
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_NONE
};
const EGLint CONTEXT_ATTRIBS[] = {
EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE
};
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
assertEGLError("eglGetDisplay");
eglInitialize(display, nullptr, nullptr);
assertEGLError("eglInitialize");
eglChooseConfig(display, DISPLAY_ATTRIBS, &config, 1, &num_config);
assertEGLError("eglChooseConfig");
eglBindAPI(EGL_OPENGL_API);
assertEGLError("eglBindAPI");
context = eglCreateContext(display, config, EGL_NO_CONTEXT, CONTEXT_ATTRIBS);
assertEGLError("eglCreateContext");
constexpr int winWidth = 256;
constexpr int winHeight = 256;
EGLint SURFACE_ATTRIBS[] = {
EGL_WIDTH, winWidth,
EGL_HEIGHT, winHeight,
EGL_NONE
};
surface = eglCreatePbufferSurface(display, config, SURFACE_ATTRIBS);
assertEGLError("eglCreatePbufferSurface");
eglMakeCurrent(display, surface, surface, context);
//eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, context);
assertEGLError("eglMakeCurrent");
std::string versionString = std::string((const char*)glGetString(GL_VERSION));
std::cout<<versionString<<std::endl;
//Enable blending
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
auto CreateTexture = [](GLuint& tex, int width, int height, void* data) {
glGenTextures(1, &tex);
assertOpenGLError("glGenTextures");
glBindTexture(GL_TEXTURE_2D, tex);
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);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
assertOpenGLError("glTexImage2D");
glBindTexture(GL_TEXTURE_2D, 0);
};
/*
* Create an OpenGL framebuffer as render target.
*/
GLuint frameBuffer;
glGenFramebuffers(1, &frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
assertOpenGLError("glBindFramebuffer");
/*
* Create a texture as color attachment.
*/
GLuint outputTex;
CreateTexture(outputTex, winWidth, winHeight, nullptr);
glBindTexture(GL_TEXTURE_2D, outputTex);
/*
* Attach the texture to the framebuffer.
*/
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, outputTex, 0);
assertOpenGLError("glFramebufferTexture2D");
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
throw std::runtime_error("frame buffer went kaput");
/*
* Create an OpenGL texture and load it with image data.
*/
GLuint inputTex;
CreateTexture(inputTex, imgWidth, imgHeight, imgData.data());
glBindTexture(GL_TEXTURE_2D, inputTex);
constexpr int textureSlot = 0;
glActiveTexture(GL_TEXTURE0+textureSlot);
assertOpenGLError("glActiveTexture");
//Build and use the shaders
auto program = CreateShader(vertexShader, fragmentShader);
glUseProgram(program);
assertOpenGLError("glUseProgram");
glUniform1i(glGetUniformLocation(program, "u_Texture"), textureSlot);
assertOpenGLError("glUniform1i, glGetUniformLocation");
glm::mat4 proj = glm::ortho(0.f, (float)winWidth, 0.f, (float)winHeight, -1.f, 1.f);
glUniformMatrix4fv(glGetUniformLocation(program, "u_MVP"), 1, GL_FALSE, &proj[0][0]);
assertOpenGLError("glUniformMatrix4fv, glGetUniformLocation");
//Define geometry to render and texture mapping
float positions[] = {0.f, 0.f, 0.f, 0.f,
winWidth, 0.f, 1.f, 0.f,
winWidth, winHeight, 1.f, 1.f,
0.f, winHeight, 0.f, 1.f
};
unsigned int indices[] = {
0, 1, 2,
2, 3, 0
};
GLuint ibo;
glGenBuffers(1, &ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (const void*) 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (const void*) (2*sizeof(GLfloat)));
/*
* Render something.
*/
//glClearColor(0.3, 0.8, 0.5, 1.0);
glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(unsigned int), GL_UNSIGNED_INT, nullptr);
//glClear(GL_COLOR_BUFFER_BIT);
glFlush();
/*
* Read the framebuffer and save to disk as an image
*/
std::vector<unsigned char> buf(winWidth*winHeight*3);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glReadPixels(0, 0, winWidth, winHeight, GL_RGB, GL_UNSIGNED_BYTE, buf.data());
assertOpenGLError("glReadPixels");
SaveRGBImageToDisk("output", winWidth, winHeight, buf.data());
/*
* Destroy context.
*/
glDeleteBuffers(1, &ibo);
glDeleteBuffers(1, &vbo);
glDeleteTextures(1, &inputTex);
glDeleteFramebuffers(1, &frameBuffer);
glDeleteTextures(1, &outputTex);
glDeleteProgram(program);
eglDestroySurface(display, surface);
assertEGLError("eglDestroySurface");
eglDestroyContext(display, context);
assertEGLError("eglDestroyContext");
eglTerminate(display);
assertEGLError("eglTerminate");
return 0;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论