《OpenGL程式設計指南》第11章——Double-Write 案例分析
阿新 • • 發佈:2018-11-04
本案例主要講解在Shader中如何使用影象(Images)物件。
案例通過建立了一維紋理Buffer(TBO)用來作為顏色資料,通過繪製多個圓柱體,將繪製結果快取到另一個Image中,再在後續的模型繪製中使用該影象資料。
程式的主要執行流程:
###1 建立TBO物件,用於儲存1D 紋理資料:
GLuint m_image_palette_buffer; // 紋理緩衝區物件
GLuint m_image_palette_texture; //紋理
2. 建立輸出紋理
// 輸出紋理,渲染第一階段,在shader中寫入資料,渲染第二階段,在shader中讀取資料
GLuint m_output_texture;
// Output iamge and PBO for clear it
GLuint m_output_texture_clear_buffer;
3. 繪製流程:
- 繫結m_iamge_palette_texture繫結到0號繫結點的影象物件,用於Shader中讀取資料
- 使用m_output_texture_clear_buffer,清空m_output_texture中的資料
- 將m_output_texture繫結到1號繫結點的影象物件
- 設定投影、模型、檢視矩陣
- 禁用顏色緩衝區,繪製多個圓柱體,通過shader,將顏色資料寫入到m_output_texture中。
- 將m_output_texture繫結到0號繫結點的影象物件
- 繪製四邊形
主要程式碼:
// DoubleWrite.cpp
#include <vapp.h>
#include <vutils.h>
#include <LoadShaders.h>
#include <vbm.h>
#include <vmath.h>
#include <stdio.h>
#include <iostream>
#define MAX_FRAMEBUFFER_WIDTH 1024
#define MAX_FRAMEBUFFER_HEIGHT 1024
BEGIN_APP_DECLARATION(DoubleWrite)
virtual void Initialize(const char* title);
virtual void Display(bool auto_redraw);
virtual void Finalize();
virtual void Reshape(int width, int height);
void InitShaders();
void InitBuffers();
private:
// Color palette buffer texture TBO
GLuint m_image_palette_buffer; // 紋理緩衝區物件
GLuint m_image_palette_texture; //紋理
// 輸出紋理,渲染第一階段,在shader中寫入資料,渲染第二階段,在shader中讀取資料
GLuint m_output_texture;
// Output iamge and PBO for clear it
GLuint m_output_texture_clear_buffer;
GLuint m_render_program; // 渲染模型著色器
GLuint m_resolve_program; // 重顯示著色器
GLuint m_quad_vao;
GLuint m_quad_vbo;
GLuint m_xfb;
GLfloat m_aspect;
GLuint m_projection_matrix_loc;
GLuint m_model_matrix_loc;
GLuint m_view_matrix_loc;
GLuint m_aspect_loc;
GLuint m_time_loc;
VBObject m_object;
float m_current_height;
float m_current_width;
END_APP_DECLARATION()
DEFINE_APP(DoubleWrite, "DoubleWrite")
void DoubleWrite::InitShaders()
{
ShaderInfo render_shaders[] = {
{GL_VERTEX_SHADER,"Media/Shaders/11/DoubleWrite.vs.glsl"},
{GL_FRAGMENT_SHADER,"Media/Shaders/11/DoubleWrite.fs.glsl"},
{GL_NONE,NULL}
};
m_render_program = LoadShaders(render_shaders);
m_model_matrix_loc = glGetUniformLocation(m_render_program,"model_matrix");
m_view_matrix_loc = glGetUniformLocation(m_render_program,"view_matrix");
m_projection_matrix_loc = glGetUniformLocation(m_render_program,"projection_matrix");
ShaderInfo blit_shaders[] = {
{GL_VERTEX_SHADER,"Media/shaders/11/DoubleWrite_blit.vs.glsl"},
{GL_FRAGMENT_SHADER,"Media/shaders/11/DoubleWrite_blit.fs.glsl"},
{GL_NONE,NULL}
};
m_resolve_program = LoadShaders(blit_shaders);
}
void DoubleWrite::InitBuffers()
{
// TBO
glGenBuffers(1,&m_image_palette_buffer);
glBindBuffer(GL_TEXTURE_BUFFER,m_image_palette_buffer);
// glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage);
glBufferData(GL_TEXTURE_BUFFER,256*4*sizeof(float),NULL,GL_STATIC_DRAW); // 此處分配記憶體大小類似於一個1D紋理,使用256*1 的GL_RGBA32F,每個顏色一個float(4位元組 4*8=32bit)
glGenTextures(1,&m_image_palette_texture);
glBindTexture(GL_TEXTURE_BUFFER,m_image_palette_texture); // 將紋理繫結到紋理緩衝區物件
glTexBuffer(GL_TEXTURE_BUFFER,GL_RGBA32F,m_image_palette_buffer);
vmath::vec4* data = (vmath::vec4*)glMapBuffer(GL_TEXTURE_BUFFER,GL_WRITE_ONLY);
for(int i = 0; i < 256; i++)
{
// 初始化1D紋理資料
//data[i] = vmath::vec4((float)i);
data[i] = vmath::vec4((float)i, (float)(256-i),(float)(rand()&0xFFFF),1.0f);
}
glUnmapBuffer(GL_TEXTURE_BUFFER);
// create output texture
glActiveTexture(GL_TEXTURE0);
glGenTextures(1,&m_output_texture);
glBindTexture(GL_TEXTURE_2D,m_output_texture);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
// glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels);
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA32F,MAX_FRAMEBUFFER_WIDTH,MAX_FRAMEBUFFER_HEIGHT,0,GL_RGBA,GL_FLOAT,NULL);
glBindTexture(GL_TEXTURE_2D,0);
// (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format);
// glBindImageTexture(GLuint unit, GLuint texture, GLint level, GLboolean layerd, GLint layer, GLenum acces, GLenum format);
glBindImageTexture(0,m_output_texture,0,GL_TRUE,0,GL_READ_WRITE,GL_RGBA32F); // 繫結 Image
glGenBuffers(1,&m_output_texture_clear_buffer);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER,m_output_texture_clear_buffer); // 從buffer -->> Framebuffer or texture buffer
// 對buffer 分配空間 ,紅寶書使用GLuint,改成GLFloat也是可以的
glBufferData(GL_PIXEL_UNPACK_BUFFER,MAX_FRAMEBUFFER_WIDTH*MAX_FRAMEBUFFER_HEIGHT* 4 *sizeof(GLfloat),NULL,GL_STATIC_DRAW);
data = (vmath::vec4*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER,GL_WRITE_ONLY);
memset(data,0x00,MAX_FRAMEBUFFER_HEIGHT*MAX_FRAMEBUFFER_WIDTH * 4 *sizeof(GLfloat));
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
// creat vao
static const GLfloat quad_vertices[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f,
};
glGenVertexArrays(1,&m_quad_vao);
glGenBuffers(1,&m_quad_vbo);
glBindVertexArray(m_quad_vao);
glBindBuffer(GL_ARRAY_BUFFER,m_quad_vbo);
glBufferData(GL_ARRAY_BUFFER,sizeof(quad_vertices),quad_vertices,GL_STATIC_DRAW);
glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,0,NULL);
glEnableVertexAttribArray(0);
glClearDepth(1.0f);
glBindVertexArray(0);
}
void DoubleWrite::Initialize(const char* title)
{
base::Initialize(title);
InitShaders();
InitBuffers();
std::string filePath = Utils::instance()->getMediaPath() + "Media/unit_pipe.vbm";
m_object.LoadFromVBM(filePath.c_str(),0,1,2);
}
void DoubleWrite::Display(bool auto_redraw)
{
float t = float(GetTickCount() & 0xFFFF)/0x3FFF;
static vmath::vec3 X(1.0f, 0.0f, 0.0f);
static vmath::vec3 Y(0.0f, 1.0f, 0.0f);
static vmath::vec3 Z(0.0f, 0.0f, 1.0f);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
// bind palette buffer
//glBindBuffer(GL_TEXTURE_BUFFER,m_image_palette_buffer);
//繫結影象資料到0上
glBindImageTexture(0,m_image_palette_texture,0,GL_FALSE,0,GL_READ_ONLY,GL_RGBA32F);
// clear output image 使用 m_output_texture_clear_buffer中的資料,重新覆蓋m_output_texture中的資料,即清空原有資料,準備重新寫入。
glBindBuffer(GL_PIXEL_UNPACK_BUFFER,m_output_texture_clear_buffer);
glBindTexture(GL_TEXTURE_2D,m_output_texture);
glTexSubImage2D(GL_TEXTURE_2D,0,0,0,m_current_width,m_current_height,GL_RGBA,GL_FLOAT,NULL);
glBindTexture(GL_TEXTURE_2D,0);
//Bind output image for read-write
// 繫結影象資料到1上
glBindImageTexture(1,m_output_texture,0,GL_FALSE,0,GL_READ_WRITE,GL_RGBA32F);
vmath::mat4 model_matrix = vmath::translate(0.0f, 0.0f, -15.0f) *
vmath::rotate(t * 360.0f, 0.0f, 0.0f, 1.0f) *
vmath::rotate(t * 435.0f, 0.0f, 1.0f, 0.0f) *
vmath::rotate(t * 275.0f, 1.0f, 0.0f, 0.0f);
vmath::mat4 view_matrix = vmath::mat4::identity();
vmath::mat4 projection_matrix = vmath::frustum(-1.0f, 1.0f, m_aspect, -m_aspect, 1.0f, 40.f);
glUseProgram(m_render_program);
glUniformMatrix4fv(m_projection_matrix_loc,1,GL_FALSE,projection_matrix);
glUniformMatrix4fv(m_model_matrix_loc,1,GL_FALSE,model_matrix);
glUniformMatrix4fv(m_view_matrix_loc,1,GL_FALSE,view_matrix);
/*glColorMask, glColorMaski — enable and disable writing of frame buffer color components
void glColorMask( GLboolean red,
GLboolean green,
GLboolean blue,
GLboolean alpha);
*/
// 禁止寫入顏色快取
glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
m_object.Render(0,4 * 4 * 4);
// 啟用寫入顏色快取
glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
glBindImageTexture(0,m_output_texture,0,GL_FALSE,0,GL_READ_ONLY,GL_RGBA32F);
glBindVertexArray(m_quad_vao);
glUseProgram(m_resolve_program);
glDrawArrays(GL_TRIANGLE_STRIP,0,4);
base::Display(auto_redraw);
}
void DoubleWrite::Finalize()
{
glUseProgram(0);
glDeleteVertexArrays(1,&m_quad_vao);
glDeleteBuffers(1,&m_quad_vbo);
}
void DoubleWrite::Reshape(int width, int height)
{
m_aspect = GLfloat(width)/height;
glViewport(0,0,width,height);
m_current_width = width;
m_current_height = height;
}