我想在OpenGL中实现延迟着色。
我有一个支持OpenGL的框架。使用典型的顶点着色器和片段着色器进行前向着色可以正常工作,缩放和变换是没有问题的。
但实施延迟着色会导致我看不到任何问题而且我不知道是否在FBO中写入纹理。
在我的渲染方法中,我取消绑定FBO直接写入屏幕。它有效,一切看起来都很棒。但绑定FBO,写入纹理和尝试将纹理写入屏幕只会给我一个空白屏幕。
所以我的问题是:我是否正确处理不同纹理的并行写入?
initialize方法创建所有着色器程序并将VBO绑定到着色器的输入。
render方法分为延迟{}块和非延迟{}着色器块。非延迟着色是一个简单的前向着色概念。
我使用的是OpenGL 3.2 / GLSL 1.50。
由于GLSL 1.50不支持布局(位置),我认为应该将片段着色器输出绑定到不同的颜色(参见initialize()方法)。
创建缓冲区会导致GL_FRAMEBUFFER_COMPLETE成功。
下面你找到我的initialize()方法:
gettimeofday(&oldTime, NULL);
addLightToScene();
printError("Beginning of initialize");
/*
* Enabling standard features in OpenGL
*/
glEnable(GL_DEPTH_TEST);
// Solange die Normalen nicht genutzt werden, sollte openGL nicht selber entscheiden dürfen in welche Richtung ein Vertex zeigt...
//glEnable(GL_CULL_FACE);
/*
* Create ID for texturing
*/
glGenTextures(1, &texture_ID);
/*
* Loading pixel- and vertex shader program and compile and link them
*/
createProgram(vertex_ID, pixel_ID, program_ID,
"shaders//VertexShader.glsl", "shaders//FragmentShader.glsl");
glBindFragDataLocation(program_ID, 0, "fragColor");
glLinkProgram(program_ID);
printProgramInfoLog(program_ID);
/*
* Get vertex attribute IDs and uniform IDs
*/
getAttributeLocation(inPosition_ID, program_ID, "in_position");
getAttributeLocation(inColor_ID, program_ID, "in_color");
getAttributeLocation(inTexCoord_ID, program_ID, "in_texcoord");
getAttributeLocation(inNormal_ID, program_ID, "in_normal");
getUniformLocation(lightPos_ID, program_ID, "light_position");
getUniformLocation(lightInt_ID, program_ID, "light_intensity");
getUniformLocation(lightAmb_ID, program_ID, "light_ambient");
getUniformLocation(mvpMatrix_ID, program_ID, "MVPMatrix");
getUniformLocation(viewportMatrix_ID, program_ID, "ViewportMatrix");
getUniformLocation(samplerTexture_ID, program_ID, "myTexture");
/*
* Create VAO and VBOs
*/
glGenVertexArrays (1 , &VAO); // Erzeugen des VAOs
glBindVertexArray(VAO); // Zustandsautomat: Aktiviere das VAO
glGenBuffers(4, &VBO[0]); // Erzeugen der 4 VBOs
// vertex position --> vec4
bindBuffer(VAO, VBO[0], inPosition_ID, 4);
// vertex color --> vec3
bindBuffer(VAO, VBO[1], inColor_ID, 3);
// vertex normal --> vec3
bindBuffer(VAO, VBO[2], inNormal_ID, 3);
// vertex texture coordinates --> vec2
bindBuffer(VAO, VBO[3], inTexCoord_ID, 2);
cam_c = QVector3D(0.f, 0.f, 1.f);
cam_n = QVector3D(0.f, 0.f, -1.f);
cam_v = QVector3D(0.f, -1.f, 0.f);
oldCamRotX = 0;
oldCamRotY = 0;
oldCamDist = 0;
printError("End of linking forward shading program");
/*
* Deferred shading from here
*/
// First stage program
createProgram(firstStageVertex_ID, firstStageFragment_ID, firstStageProgram_ID,
"shaders//FirstStageVertexShader.glsl",
"shaders//FirstStageFragmentShader.glsl");
// Bind fragment shader outputs to the textures
glBindFragDataLocation(firstStageProgram_ID, 0, "out_diffuse");
glBindFragDataLocation(firstStageProgram_ID, 1, "out_position");
glBindFragDataLocation(firstStageProgram_ID, 2, "out_normal");
glLinkProgram(firstStageProgram_ID);
printProgramInfoLog(firstStageProgram_ID);
printError("End of linking first stage shader program");
/*
* Get the attribute locations of the variables of the first stage vertex shader
*/
getAttributeLocation(in_normal_first_stage_ID, firstStageProgram_ID,
"in_normal", "first stage vertex shader");
getAttributeLocation(in_position_first_stage_ID, firstStageProgram_ID,
"in_position", "first stage vertex shader");
getAttributeLocation(in_color_first_stage_ID, firstStageProgram_ID,
"in_color", "first stage vertex shader");
getAttributeLocation(in_texCoord_first_stage_ID, firstStageProgram_ID,
"in_texCoord", "first stage vertex shader");
getAttributeLocation(in_intensity_first_stage_ID, firstStageProgram_ID,
"in_intensity", "first stage vertex shader");
getAttributeLocation(in_ambientLight_first_stage_ID, firstStageProgram_ID,
"in_ambientLight", "first stage vertex shader");
/*
* Get the locations of the uniforms of the first stage vertex shader
*/
getUniformLocation(uniform_projectionMatrix_ID, firstStageProgram_ID,
"projectionMatrix", "first stage vertex shader");
getUniformLocation(uniform_modelMatrix_ID, firstStageProgram_ID,
"modelMatrix", "first stage vertex shader");
getUniformLocation(uniform_viewMatrix_ID, firstStageProgram_ID,
"viewMatrix", "first stage vertex shader");
getUniformLocation(uniform_mvpMatrix_first_stage_ID, firstStageProgram_ID,
"MVPMatrix", "first stage vertex shader");
getUniformLocation(uniform_viewportMatrix_first_stage_ID, firstStageProgram_ID,
"viewportMatrix", "first stage vertex shader");
/*
* Create VAO_deferred and VBO_deffered
*/
glGenVertexArrays (1 , &VAO_deferred); // Erzeugen des VAOs
glBindVertexArray(VAO_deferred); // Zustandsautomat: Aktiviere das VAO
glGenBuffers(4, &VBO_deferred[0]); // Erzeugen der 4 VBOs
bindBuffer(VAO_deferred, VBO_deferred[0], in_position_first_stage_ID, 4);
bindBuffer(VAO_deferred, VBO_deferred[1], in_color_first_stage_ID, 3);
bindBuffer(VAO_deferred, VBO_deferred[2], in_normal_first_stage_ID, 3);
bindBuffer(VAO_deferred, VBO_deferred[3], in_texCoord_first_stage_ID, 2);
printError("End of attaching VBO_deferred objects to first stage shader program");
// TODO: insert in_intensity and in_ambientLight into first stage vertex shader
/*
* Deferred stage program from here
*/
createProgram(deferredStageVertex_ID, deferredStageFragment_ID, deferredStageProgram_ID,
"shaders//DeferredStageVertexShader.glsl",
"shaders//DeferredStageFragmentShader.glsl");
// Bind fragment shader outputs
glBindFragDataLocation(deferredStageProgram_ID, 0, "fragColor");
glLinkProgram(deferredStageProgram_ID);
printProgramInfoLog(deferredStageProgram_ID);
printError("End of linking deferred stage shader program");
getAttributeLocation(in_vertex_deferred_stage_ID, deferredStageProgram_ID,
"in_texture_vertex", "deferred stage vertex shader");
getUniformLocation(uniform_positionTexture_ID, deferredStageProgram_ID,
"positionTexture", "deferred stage vertex shader");
getUniformLocation(uniform_normalTexture_ID, deferredStageProgram_ID,
"normalTexture", "deferred stage vertex shader");
getUniformLocation(uniform_diffuseTexture_ID, deferredStageProgram_ID,
"diffuseTexture", "deferred stage vertex shader");
getUniformLocation(uniform_camera_ID, deferredStageProgram_ID, "viewMatrix", "deferred stage vertex shader");
printError("End of getting uniform locations of the deferred stage shader program");
glGenVertexArrays (1 , &VAO_texture);
glBindVertexArray(VAO_texture);
glGenBuffers(1, &VBO_texture[0]);
// vertex position --> vec4
bindBuffer(VAO_texture, VBO_texture[0], in_vertex_deferred_stage_ID, 4);
这是我的渲染方法:
doLightAnimationStep();
// Create matrices
//QMatrix4x4 modelMatrix = createMouseViewMatrix();
//QMatrix4x4 mouseViewMatrix = createViewMatrix();
//QMatrix4x4 projectionMatrix = createProjectionMatrix();
QMatrix4x4 mvpMatrix = createProjectionMatrix() * createMouseViewMatrix();
QMatrix4x4 viewportMatrix = MatrixHandler::createScaleMatrix(
10.f / scaling, 10.f / scaling, 10.f / scaling);
if(deferredShading) {
// Bind FBO to context and clear all buffers before rendering
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
//glViewport(0, 0, viewWidth, viewHeight);
GLenum windowBufferSelector[] = {
GL_COLOR_ATTACHMENT0,
GL_COLOR_ATTACHMENT1,
GL_COLOR_ATTACHMENT2 };
glDrawBuffers(3, windowBufferSelector); // Select all buffers
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Set everything to zero.
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
printError("Bound FBO and depth buffer");
// First render stage -> writing all data into the textures and the depth buffer
glUseProgram(firstStageProgram_ID);
// Set uniforms
glUniformMatrix4fv(uniform_mvpMatrix_first_stage_ID, 1, GL_FALSE, mvpMatrix.data());
glUniformMatrix4fv(uniform_viewportMatrix_first_stage_ID, 1, GL_FALSE, viewportMatrix.data());
printError("Prepared buffer 0, 1 and 2 for writing");
//DrawTheWorld(); // Will also produce depth data in the depth buffer
glBindVertexArray(VAO_deferred); // VAO aktivieren
glDrawArrays(GL_TRIANGLES, 0, vertexCount);
glBindVertexArray(0);
printError("Wrote into buffer 0, 1 and 2");
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Deferred render stage -> read the data for all pixels from the textures and the depth buffer to calculate the color for the pixel
// The depth buffer from stage 1 is not used now as the fbo is disabled.
// Clearing the active buffer means clearing the screen
glClearColor(255.0f, 255.0f, 255.0f, 0.0f); // Set everything to zero.
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//EnableRenderProgramDeferredStage();
glUseProgram(deferredStageProgram_ID);
//SetupDeferredStageUniforms();
printError("Setting uniforms for deferred stage vertex shader.");
glBindTexture(GL_TEXTURE_2D, fDiffuseTexture);
glUniform1i(uniform_diffuseTexture_ID, fDiffuseTexture);
//glUniform1i(uniform_positionTexture_ID, fPositionTexture);
//glUniform1i(uniform_normalTexture_ID, fNormalsTexture);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, fNormalsTexture);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, fPositionTexture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, fDiffuseTexture);
printError("Setted uniforms for deferred stage vertex shader.");
glBindVertexArray(VAO_texture); // VAO aktivieren
glDrawArrays(GL_TRIANGLES, 0, 2);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
printError("End of deffered shading rendering");
} else {
// Enable and disable z-buffering and backface culling
if(zBuffering) {
if(!(glIsEnabled(GL_DEPTH_TEST) == GL_TRUE)) {
glEnable(GL_DEPTH_TEST);
}
} else {
if(glIsEnabled(GL_DEPTH_TEST) == GL_TRUE) {
glDisable(GL_DEPTH_TEST);
}
}
if(backfaceCulling) {
if(!(glIsEnabled(GL_CULL_FACE) == GL_TRUE)) {
glEnable(GL_CULL_FACE);
}
} else {
if(glIsEnabled(GL_CULL_FACE) == GL_TRUE) {
glDisable(GL_CULL_FACE);
}
}
glClearColor(255, 255, 255, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Use shader program
glUseProgram(program_ID);
glBindTexture(GL_TEXTURE_2D, texture_ID);
glUniform1i(samplerTexture_ID, 0);
if(textureMode == CLAMP) {
// Werte auf kleiner 0 auf 0 beschränken , Werte größer 1 auf 1:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
} else if(textureMode == REPEAT) {
// Texturen wiederholen
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
} else if(textureMode == MIRRORED_REPEAT) {
// Texturen wiederholen, die Koordinaten jedoch an den Grenzen ”spiegeln ”
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
} else {
// Should never be reached!
assert(false);
}
if(filterMode == NEAREST_NEIGHBOUR) {
// Für Nearest−Neighbor Filterung (wie im Software−Renderer)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
} else if (filterMode == LINEAR) {
// Für Lineare Filterung (qualitativhochwertiger)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} else if (filterMode == MIPMAPPING) {
// Mipmapping (weniger Aliasing Artefakte, aber höherer Speicherbedarf)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
} else {
// Should never be reached!
assert(false);
}
glUniformMatrix4fv(mvpMatrix_ID, 1, GL_FALSE, mvpMatrix.data());
glUniformMatrix4fv(viewportMatrix_ID, 1, GL_FALSE, viewportMatrix.data());
//float light_dist_f = (float)light_dist/100.f;
//GLfloat lightPos__vec [3] = {light_dist_f*(float) cos(Math::toRadian(light_angle)), light_dist_f*(float) sin(Math::toRadian(light_angle)),light_dist_f};
//glUniform3fv(lightPos_ID, 1, lightPos__vec);
GLfloat lightPos__vec [3] = {lights[0].position.x(), lights[0].position.y(), lights[0].position.z()};
glUniform3fv(lightPos_ID, 1, lightPos__vec);
GLfloat intensity = ((GLfloat)light_intensity/(GLfloat)100);
glUniform1fv(lightInt_ID, 1, &intensity);
GLfloat ambient = ((GLfloat)light_ambient/(GLfloat)100);
glUniform1fv(lightAmb_ID, 1, &ambient);
glBindVertexArray(VAO); // VAO aktivieren
glDrawArrays(GL_TRIANGLES, 0 , vertexCount);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
}
每次我改变框架的大小时,我都会改变视口大小和FBO +纹理。
sizeChanged()
:
// Prevent frame buffer objects to be corrupted
if(width <= 0 || height <= 0) {
return;
}
if(origin_width == 0) {
origin_width = width;
origin_height = height;
}
multiplier_lr = (float)viewWidth/(float) origin_width;
multiplier_tb = (float)viewHeight/(float) origin_height;
viewWidth = width;
viewHeight = height;
screenVertices.clear();
screenVertices << 0.f << 0.f;
screenVertices << viewWidth << 0.f;
screenVertices << 0.f << viewHeight;
screenVertices << viewWidth << 0.f;
screenVertices << 0.f << viewHeight;
screenVertices << viewWidth << viewHeight;
glBindBuffer(GL_ARRAY_BUFFER, VBO_texture[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * screenVertices.size(), screenVertices.data(), GL_STATIC_DRAW);
glViewport(0,0,width,height);
/*
* Generate framebuffer object, renderbuffer object and color buffers
*/
// Generate framebuffer object FBO
glGenFramebuffers(1, &FBO);
// The FBO does not have a depth buffer or anything else.
// Therefore it is necessary to implement a depth buffer and all the
// texture buffers in the initialize method
// Generate and adjust depth buffer
glGenRenderbuffers(1, &fDepthBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, fDepthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
printError("Generated frame buffer object and depth buffer");
// Generate diffuse buffer
generateTexture(fDiffuseTexture, GL_RGBA, width, height);
printError("Generated diffuse texture buffer");
// Generate and bind the texture for positions
generateTexture(fPositionTexture, GL_RGBA32F, width, height);
printError("Generated position texture buffer");
// Generate and bind the texture for normals
generateTexture(fNormalsTexture, GL_RGBA16F, width, height);
printError("Generated normal texture buffer");
// Bind the FBO so that the next operations will be bound to it.
glBindFramebuffer(GL_FRAMEBUFFER , FBO);
// Attach the depth buffer to the FBO
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fDepthBuffer);
// Attach the textures to the FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fDiffuseTexture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, fPositionTexture, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, fNormalsTexture, 0);
printError("Bound all textures to the FBO");
// Check FBO for errors
GLenum fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (fboStatus != GL_FRAMEBUFFER_COMPLETE) {
printf("DeferredLighting::Init: FrameBuffer incomplete: 0x%x\n", fboStatus);
exit(1);
}
// Unbind FBO
glBindFramebuffer(GL_FRAMEBUFFER , 0);
printError("Unbound all textures to the FBO");
Screenvertices是一个QVector,与我的前向阴影VAO和我的VAO_deferred相同的结构类型。
第一阶段顶点着色器:
#version 150
precision mediump float;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform mat4 MVPMatrix;
uniform mat4 viewportMatrix;
in vec4 in_position;
in vec3 in_color;
in vec3 in_normal;
in vec2 in_texCoord;
in float in_intensity; // Sun light
in float in_ambient;
out vec4 position_varying;
out vec3 color_varying;
out vec3 normal_varying;
out vec2 texCoord_varying;
out float extIntensity_varying;
out float extAmbientLight_varying;
/*
* The first stage shader program performs geometric calculations.
* Light calculations are done in the second stage, the deferred stage shader
* program.
*/
// Texture coordinates are applied to the first stage fragment shader.
// The normal vector is being transformed in world coordinates.
// gl_Position is being calculated with the MVP matrix in screen coordinates
// The intensity of the sun is in range [0;255], perhaps this has to be adjusted
// The ambient light is in the range of [0;255] aswell, perhaps this has to be
// adjusted too.
void main(void) {
gl_Position = viewportMatrix * (MVPMatrix * in_position);
color_varying = in_color;
normal_varying = in_normal;
texCoord_varying = in_texCoord;
// Not used right now
//extIntensity_varying = in_intensity;
//extAmbientLight_varying = in_ambient;
/*
normal_varying = normalize((modelMatrix*vec4(in_normal, 0.0)));
gl_Position = projectionMatrix * viewMatrix * modelMatrix * in_vertex;
position_varying = vec3(modelMatrix * in_vertex); // Copy position to the fragment shader
extIntensity_varying = in_intensity/255.0; // Scale the intensity from [0..255] to [0..1].
extAmbientLight_varying = in_ambientLight/255.0;
*/
}
第一阶段片段着色器:
#version 150
precision mediump float;
uniform sampler2D firstTexture;
in vec4 position_varying; // The model coordinate, as given by the vertex shader
in vec3 color_varying; // The interpolated color of the fragment
in vec3 normal_varying; // The interpolated normal of the fragment
in vec2 texCoord_varying; // The interpolated texture coordinates of the fragment
// To be written into texture buffers
out vec4 out_position; // layout(location = 0)
out vec3 out_normal; // layout(location = 1)
out vec3 out_diffuse; // layout(location = 2)
/*
* The first stage shader program performs geometric calculations.
* Light calculations are done in the second stage, the deferred stage shader
* program.
*/
// Output the model-transformed vertex-coordinates
// Output normal (normalized)
void main(void) {
out_position = position_varying; // Position given by the vertext shader
out_normal = normal_varying;
out_diffuse = color_varying;
/*
// The blending is not necessary in calculation with the GDV framework
// as there are no transparent models
vec4 clr = texture(firstTexture, texCoord_varying);
out_diffuse = clr;
*/
}
延迟阶段顶点着色器:
#version 150
precision mediump float;
in vec2 in_texture_vertex;
out vec2 texture_varying;
/*
* The first stage shader program performs geometric calculations.
* Light calculations are done in the second stage, the deferred stage shader
* program.
*/
void main(void) {
// Why is there a calculation in_vertex*2-1?
gl_Position = vec4(in_texture_vertex.xy, 0.f, 0.f);
// Copy position to the fragment shader. Only x and y is needed.
texture_varying = in_texture_vertex;
}
延迟阶段片段着色器:
#version 150
precision mediump float;
uniform sampler2D positionTexture; // World position
uniform sampler2D normalTexture; // Normals
uniform sampler2D diffuseTexture; // The color information
// Not used?
//uniform vec3 camera; // The coordinate of the camera
in vec2 texture_varying; // The world position
out vec4 fragColor; // layout(location = 0)
/*
* The first stage shader program performs geometric calculations.
* Light calculations are done in the second stage, the deferred stage shader
* program.
*/
void main(void) {
// Load data, stored in textures, from the first stage rendering.
// Color
fragColor = texture2D(diffuseTexture, texture_varying);
//fragColor = vec4(0, 0, 0, 1);
//vec4 worldPos = texture(positionTexture, texture_varying.xy);
// Normals
//fragColor = texture(normalTexture, texture_varying.xy);
// Use information about lamp coordinate (not shown here), the pixel
// coordinate (worldpos.xyz), the normal of this pixel (normal.xyz)
// to compute a lighting effect.
// Use this lighting effect to update 'diffuse'
//vec4 preBlend = diffuse * lamp + specularGlare;
//fragColor = (normal+1)/2;
//fragColor = diffuse;
//fragColor = worldPos; // Scaling may be needed to range [0,1]
//fragColor = lamp*vec4(1,1,1,1);
}