news 2026/9/11 11:33:23

OpenGL多重渲染

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenGL多重渲染

在 OpenGL 中,多重渲染(Multiple Render Targets, MRT)是一种高级渲染技术,允许在一次绘制调用中同时向多个颜色缓冲区写入数据。这项技术在现代图形编程中非常重要,尤其用于:

  • 延迟渲染(Deferred Shading)
  • G-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. 创建并绑定 FBO(Frame Buffer Object)

GLuint 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片元位置(vec3)
COLOR_ATTACHMENT1法线(vec3)
COLOR_ATTACHMENT2漫反射颜色
COLOR_ATTACHMENT3镜面反射等

这些缓冲用于后续的光照 pass,从而避免重复几何处理。


🔧 注意事项

  • 必须检查 FBO 状态:

    if(glCheckFramebufferStatus(GL_FRAMEBUFFER)!=GL_FRAMEBUFFER_COMPLETE){std::cerr<<"Framebuffer not complete!"<<std::endl;}
  • 着色器输出的location必须和glDrawBuffers顺序一致。

  • 确保 GPU 支持 MRT(OpenGL 3.0+ 一般都支持)。

代码

// Deferred Shading with MRT - Basic Example using GLFW + GLAD + OpenGL 3.3 Core// Requirements: GLFW, GLAD, stb_image, GLM#include<glad/glad.h>#include<GLFW/glfw3.h>#include<glm/glm.hpp>#include<glm/gtc/matrix_transform.hpp>#include<glm/gtc/type_ptr.hpp>#include<iostream>#include<vector>// 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(quadVAO==0){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*window=glfwCreateWindow(1280,720,"Deferred Shading",nullptr,nullptr);glfwMakeContextCurrent(window);gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);glEnable(GL_DEPTH_TEST);// Load shadersGLuint geometryPassShader=LoadShader("geometry.vs","geometry.fs");GLuint lightingPassShader=LoadShader("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::cout<<"Framebuffer 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); }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 11:32:20

GESP Python一级网络协议与互联网概念备考全攻略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 11:26:04

Dolphin 安装上手教程:5 分钟从源码跑通 GameCube 与 Wii 模拟器

Dolphin 安装上手教程&#xff1a;5 分钟从源码跑通 GameCube 与 Wii 模拟器 【免费下载链接】dolphin Dolphin is a GameCube / Wii emulator, allowing you to play games for these two platforms on PC with improvements. 项目地址: https://gitcode.com/GitHub_Trendin…

作者头像 李华
网站建设 2026/9/11 11:25:25

HTOOL-SL6H便携信号源深度体验:按键、双通道与SCPI程控实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华