
在 OpenGL 中多重渲染Multiple Render Targets, MRT是一种高级渲染技术允许在一次绘制调用中同时向多个颜色缓冲区写入数据。这项技术在现代图形编程中非常重要尤其用于延迟渲染Deferred ShadingG-Buffer 生成多通道输出如颜色 法线 深度等 一、什么是多重渲染MRT传统上一个渲染管线的 fragment shader 只能输出到一个颜色缓冲out vec4 fragColor;而启用了 MRT 后可以输出到多个颜色缓冲比如layout(location 0) out vec4 outColor0; layout(location 1) out vec4 outColor1; layout(location 2) out vec4 outColor2;这些输出将同时写入绑定的多个帧缓冲目标。 二、如何使用 MRT基本步骤1. 创建并绑定 FBOFrame Buffer ObjectGLuint fbo;glGenFramebuffers(1,fbo);glBindFramebuffer(GL_FRAMEBUFFER,fbo);2. 创建并附加多个颜色纹理作为输出目标GLuint tex0,tex1;// 创建颜色附件glGenTextures(1,tex0);glBindTexture(GL_TEXTURE_2D,tex0);glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA16F,width,height,0,GL_RGBA,GL_FLOAT,nullptr);glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,tex0,0);// 第二个输出目标glGenTextures(1,tex1);glBindTexture(GL_TEXTURE_2D,tex1);glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA16F,width,height,0,GL_RGBA,GL_FLOAT,nullptr);glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,tex1,0);3. 设置绘制的输出目标GLenum drawBuffers[2]{GL_COLOR_ATTACHMENT0,GL_COLOR_ATTACHMENT1};glDrawBuffers(2,drawBuffers);4. 在 Fragment Shader 中输出多个值layout(location 0) out vec4 outColor0; layout(location 1) out vec4 outColor1; void main() { outColor0 vec4(1.0, 0.0, 0.0, 1.0); // 红色 outColor1 vec4(0.0, 1.0, 0.0, 1.0); // 绿色 }5. 使用这些纹理进行后续渲染处理如合成、光照 三、MRT 应用场景示例 延迟渲染 G-Buffer 输出渲染目标Attachment存储内容COLOR_ATTACHMENT0片元位置vec3COLOR_ATTACHMENT1法线vec3COLOR_ATTACHMENT2漫反射颜色COLOR_ATTACHMENT3镜面反射等这些缓冲用于后续的光照 pass从而避免重复几何处理。 注意事项必须检查 FBO 状态if(glCheckFramebufferStatus(GL_FRAMEBUFFER)!GL_FRAMEBUFFER_COMPLETE){std::cerrFramebuffer not complete!std::endl;}着色器输出的location必须和glDrawBuffers顺序一致。确保 GPU 支持 MRTOpenGL 3.0 一般都支持。代码// Deferred Shading with MRT - Basic Example using GLFW GLAD OpenGL 3.3 Core// Requirements: GLFW, GLAD, stb_image, GLM#includeglad/glad.h#includeGLFW/glfw3.h#includeglm/glm.hpp#includeglm/gtc/matrix_transform.hpp#includeglm/gtc/type_ptr.hpp#includeiostream#includevector// Shader loading utility functions here (omitted for brevity, use your own loader)GLuintLoadShader(constchar*vertexPath,constchar*fragmentPath);// Screen quad verticesfloatquadVertices[]{// positions // texcoords-1.0f,1.0f,0.0f,1.0f,-1.0f,-1.0f,0.0f,0.0f,1.0f,-1.0f,1.0f,0.0f,-1.0f,1.0f,0.0f,1.0f,1.0f,-1.0f,1.0f,0.0f,1.0f,1.0f,1.0f,1.0f};GLuint quadVAO,quadVBO;voidrenderQuad(){if(quadVAO0){glGenVertexArrays(1,quadVAO);glGenBuffers(1,quadVBO);glBindVertexArray(quadVAO);glBindBuffer(GL_ARRAY_BUFFER,quadVBO);glBufferData(GL_ARRAY_BUFFER,sizeof(quadVertices),quadVertices,GL_STATIC_DRAW);glEnableVertexAttribArray(0);glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,4*sizeof(float),(void*)0);glEnableVertexAttribArray(1);glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,4*sizeof(float),(void*)(2*sizeof(float)));}glBindVertexArray(quadVAO);glDrawArrays(GL_TRIANGLES,0,6);glBindVertexArray(0);}intmain(){glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR,3);glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);GLFWwindow*windowglfwCreateWindow(1280,720,Deferred Shading,nullptr,nullptr);glfwMakeContextCurrent(window);gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);glEnable(GL_DEPTH_TEST);// Load shadersGLuint geometryPassShaderLoadShader(geometry.vs,geometry.fs);GLuint lightingPassShaderLoadShader(lighting.vs,lighting.fs);// Create G-bufferGLuint gBuffer;glGenFramebuffers(1,gBuffer);glBindFramebuffer(GL_FRAMEBUFFER,gBuffer);GLuint gPosition,gNormal,gAlbedoSpec;glGenTextures(1,gPosition);glBindTexture(GL_TEXTURE_2D,gPosition);glTexImage2D(GL_TEXTURE_2D,0,GL_RGB16F,1280,720,0,GL_RGB,GL_FLOAT,nullptr);glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,gPosition,0);glGenTextures(1,gNormal);glBindTexture(GL_TEXTURE_2D,gNormal);glTexImage2D(GL_TEXTURE_2D,0,GL_RGB16F,1280,720,0,GL_RGB,GL_FLOAT,nullptr);glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,gNormal,0);glGenTextures(1,gAlbedoSpec);glBindTexture(GL_TEXTURE_2D,gAlbedoSpec);glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,1280,720,0,GL_RGBA,GL_UNSIGNED_BYTE,nullptr);glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,GL_TEXTURE_2D,gAlbedoSpec,0);GLuint attachments[3]{GL_COLOR_ATTACHMENT0,GL_COLOR_ATTACHMENT1,GL_COLOR_ATTACHMENT2};glDrawBuffers(3,attachments);GLuint rboDepth;glGenRenderbuffers(1,rboDepth);glBindRenderbuffer(GL_RENDERBUFFER,rboDepth);glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH_COMPONENT,1280,720);glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_RENDERBUFFER,rboDepth);if(glCheckFramebufferStatus(GL_FRAMEBUFFER)!GL_FRAMEBUFFER_COMPLETE)std::coutFramebuffer not complete!std::endl;glBindFramebuffer(GL_FRAMEBUFFER,0);while(!glfwWindowShouldClose(window)){glfwPollEvents();// Geometry PassglBindFramebuffer(GL_FRAMEBUFFER,gBuffer);glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);glUseProgram(geometryPassShader);// renderScene(geometryPassShader); // draw models hereglBindFramebuffer(GL_FRAMEBUFFER,0);// Lighting PassglClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);glUseProgram(lightingPassShader);glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D,gPosition);glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D,gNormal);glActiveTexture(GL_TEXTURE2);glBindTexture(GL_TEXTURE_2D,gAlbedoSpec);renderQuad();glfwSwapBuffers(window);}这个是使用 GLFW GLAD 编写的完整延迟渲染Deferred Shading框架包括几何阶段Geometry Pass生成 G-Buffer位置、法线、颜色镜面信息光照阶段Lighting Pass从 G-Buffer 中采样进行光照计算使用屏幕空间四边形渲染最终图像着色器文件geometry.glsl#version 330 core layout (location 0) in vec3 aPos; layout (location 1) in vec3 aNormal; layout (location 2) in vec2 aTexCoords; uniform mat4 model; uniform mat4 view; uniform mat4 projection; out VS_OUT { vec3 FragPos; vec3 Normal; vec2 TexCoords; } vs_out; void main() { vs_out.FragPos vec3(model * vec4(aPos, 1.0)); vs_out.Normal mat3(transpose(inverse(model))) * aNormal; vs_out.TexCoords aTexCoords; gl_Position projection * view * vec4(vs_out.FragPos, 1.0); }geometry.glsl#version 330 core layout (location 0) out vec3 gPosition; layout (location 1) out vec3 gNormal; layout (location 2) out vec4 gAlbedoSpec; in VS_OUT { vec3 FragPos; vec3 Normal; vec2 TexCoords; } fs_in; uniform sampler2D texture_diffuse; uniform sampler2D texture_specular; void main() { gPosition fs_in.FragPos; gNormal normalize(fs_in.Normal); vec3 albedo texture(texture_diffuse, fs_in.TexCoords).rgb; float spec texture(texture_specular, fs_in.TexCoords).r; gAlbedoSpec vec4(albedo, spec); }lighting.glsl#version 330 core layout (location 0) in vec2 aPos; layout (location 1) in vec2 aTexCoords; out vec2 TexCoords; void main() { TexCoords aTexCoords; gl_Position vec4(aPos.x, aPos.y, 0.0, 1.0); }lighting.glsl#version 330 core out vec4 FragColor; in vec2 TexCoords; uniform sampler2D gPosition; uniform sampler2D gNormal; uniform sampler2D gAlbedoSpec; void main() { vec3 FragPos texture(gPosition, TexCoords).rgb; vec3 Normal normalize(texture(gNormal, TexCoords).rgb); vec3 Albedo texture(gAlbedoSpec, TexCoords).rgb; float Specular texture(gAlbedoSpec, TexCoords).a; vec3 lightPos vec3(10.0, 10.0, 10.0); vec3 lightColor vec3(1.0); vec3 viewPos vec3(0.0, 0.0, 5.0); // diffuse vec3 lightDir normalize(lightPos - FragPos); float diff max(dot(Normal, lightDir), 0.0); // specular vec3 viewDir normalize(viewPos - FragPos); vec3 reflectDir reflect(-lightDir, Normal); float spec pow(max(dot(viewDir, reflectDir), 0.0), 32.0) * Specular; vec3 lighting (diff spec) * lightColor * Albedo; FragColor vec4(lighting, 1.0); }