# OpenGL01 **Repository Path**: weekend/OpenGL01 ## Basic Information - **Project Name**: OpenGL01 - **Description**: Hello Triangle - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-04-09 - **Last Updated**: 2026-03-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## RoadMap - [x] Triangle 三角形 - [x] Texture 纹理 - [x] Camera 相机 - [x] Lighting 基础光照, Blinn-Phong - [x] 模型加载, Assimp - [x] Streaming 流式加载 - [x] 多线程共享上下文 - [x] Async loading 异步加载 - [ ] Scene 场景管理 - [x] 帧缓冲 - [ ] 高级光照 - [ ] 文本渲染 - [ ] glTF支持 - [ ] ShadowPass 阴影, Shadow Map - [ ] RenderPass, FrameGraph/GBuffer/PostProcess - [ ] PBR 真实材质, PBRShader, Cook-Torrance/IBL - [ ] Instancing 批量渲染 - [ ] MeshBatch - [ ] ECS ## OpenGL Basic ### [OpenGL基本概念](Triangle.md) ### [OpenGL坐标系](CoordinateSystems.md) ### [纹理](Textures.md) ### [光照](Lighting.md) ### [Assimp 模型加载](Model.md) ### [共享上下文](SharedContext.md) ## [Advanced OpenGL](AdvancedOpengl.md) ### 天空盒 * 天空盒 ![](res/0136.png) * 环境映射(Environment Mapping) 反射(Reflection) * 环境映射(Environment Mapping) 折射(Refraction) ### 帧缓冲对象 - [x] FBO 帧缓冲对象 ![](res/0119.png) - [x] FBO 帧缓冲对象, 线框模式 ![](res/0121.png) ```cpp glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glLineWidth(50.0f); ``` - [x] FBO 帧缓冲对象, 后处理(Post-processing)- 反相 Inversion ![](res/0122.png) ```CPP void main() { FragColor = vec4(vec3(1.0 - texture(screenTexture, TexCoords)), 1.0); } ``` - [x] FBO 帧缓冲对象, 后处理(Post-processing)- 灰度 Grayscale ![](res/0123.png) ```cpp void main() { FragColor = texture(screenTexture, TexCoords); float average = (FragColor.r + FragColor.g + FragColor.b) / 3.0; FragColor = vec4(average, average, average, 1.0); } ``` - [x] FBO 帧缓冲对象, 后处理(Post-processing)- 核效果 Kernel effects ![](res/0124.png) ```cpp const float offset = 1.0 / 300.0; void main() { vec2 offsets[9] = vec2[]( vec2(-offset, offset), // 左上 vec2( 0.0f, offset), // 正上 vec2( offset, offset), // 右上 vec2(-offset, 0.0f), // 左 vec2( 0.0f, 0.0f), // 中 vec2( offset, 0.0f), // 右 vec2(-offset, -offset), // 左下 vec2( 0.0f, -offset), // 正下 vec2( offset, -offset) // 右下 ); float kernel[9] = float[]( -1, -1, -1, -1, 9, -1, -1, -1, -1 ); vec3 sampleTex[9]; for(int i = 0; i < 9; i++) { sampleTex[i] = vec3(texture(screenTexture, TexCoords.st + offsets[i])); } vec3 col = vec3(0.0); for(int i = 0; i < 9; i++) col += sampleTex[i] * kernel[i]; FragColor = vec4(col, 1.0); } ``` - [x] FBO 帧缓冲对象, 后处理(Post-processing)- 核效果 Kernel effects: 模糊 Blur ![](res/0125.png) ```cpp const float offset = 1.0 / 300.0; void main() { // 卷积矩阵(Convolution Matrix) vec2 offsets[9] = vec2[]( vec2(-offset, offset), // 左上 vec2( 0.0f, offset), // 正上 vec2( offset, offset), // 右上 vec2(-offset, 0.0f), // 左 vec2( 0.0f, 0.0f), // 中 vec2( offset, 0.0f), // 右 vec2(-offset, -offset), // 左下 vec2( 0.0f, -offset), // 正下 vec2( offset, -offset) // 右下 ); float kernel[9] = float[]( 1.0 / 16, 2.0 / 16, 1.0 / 16, 2.0 / 16, 4.0 / 16, 2.0 / 16, 1.0 / 16, 2.0 / 16, 1.0 / 16 ); vec3 sampleTex[9]; for(int i = 0; i < 9; i++) { sampleTex[i] = vec3(texture(screenTexture, TexCoords.st + offsets[i])); } vec3 col = vec3(0.0); for(int i = 0; i < 9; i++) col += sampleTex[i] * kernel[i]; FragColor = vec4(col, 1.0); } ``` - [x] FBO 帧缓冲对象, 后处理(Post-processing)- 核效果 Kernel effects: 边缘检测(Edge-detection) ![](res/0127.png) ![](res/0126.png) ```cpp const float offset = 1.0 / 300.0; void main() { // 卷积矩阵(Convolution Matrix) vec2 offsets[9] = vec2[]( vec2(-offset, offset), // 左上 vec2( 0.0f, offset), // 正上 vec2( offset, offset), // 右上 vec2(-offset, 0.0f), // 左 vec2( 0.0f, 0.0f), // 中 vec2( offset, 0.0f), // 右 vec2(-offset, -offset), // 左下 vec2( 0.0f, -offset), // 正下 vec2( offset, -offset) // 右下 ); float kernel[9] = float[]( 1, 1, 1, 1, -9, 1, 1, 1, 1 ); vec3 sampleTex[9]; for(int i = 0; i < 9; i++) { sampleTex[i] = vec3(texture(screenTexture, TexCoords.st + offsets[i])); } vec3 col = vec3(0.0); for(int i = 0; i < 9; i++) col += sampleTex[i] * kernel[i]; FragColor = vec4(col, 1.0); } ``` - [x] FBO 帧缓冲对象, 后处理(Post-processing), 后视镜效果 > 在自定义FBO上绘制一次箱子,然后在默认FBO上再绘制一次箱子+自定义FBO ![](res/0129.png) * 绘制后视镜FBO时,将相机旋转180度实现后视镜效果 ![](res/0130.png) ```cpp void paintGL() override { camera.yaw += 180.0f; camera.updateVectors(); draw1stRenderPass(); camera.yaw -= 180.0f; camera.updateVectors(); draw2ndRenderPass(); } ``` - [x] FBO 帧缓冲对象, 后处理(Post-processing), 像素风格 > 初始化FBO的时候使用小尺寸的纹理以及RenderBuffer,纹理过滤使用GL_NEAREST,绘制到这个FBO的时候使用同样小尺寸的viewport,绘制主FBO的时候恢复正常的viewport ![](res/0131.png) ### 面剔除 * glCullFace(GL_BACK); glFrontFace(GL_CW); ![](res/0114.png) ![](res/0115.png) ![](res/0116.png) * glCullFace(GL_BACK); glFrontFace(GL_CCW); ![](res/0117.png) * glCullFace(GL_FRONT); ![](res/0118.png) ### 混合 * before discard ![](res/0105.png) * after discard ![](res/0106.png) * 标准 Alpha 混合(最常用) ![](res/0109.png) ```cpp glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // 标准透明 // 效果: // 最终RGB = 源RGB × 源A + 目标RGB × (1 - 源A) // 最终A = 源A × 1 + 目标A × (1 - 源A) ``` * 加法混合(发光效果) ![](res/0110.png) ```cpp glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE); // 简单相加 // 效果:颜色叠加变亮,适合光效 ``` * 乘法混合(变暗/调制) ![](res/0111.png) ```cpp glEnable(GL_BLEND); glBlendFunc(GL_DST_COLOR, GL_ZERO); // 相乘 // 或 glBlendFunc(GL_ZERO, GL_SRC_COLOR); // 效果:最终 = 源 × 目标,结果更暗 ``` * 预乘 Alpha(Premultiplied Alpha) ![](res/0112.png) ```cpp glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // 源已是预乘 // 纹理数据:RGB 已乘以 Alpha // 避免标准混合的暗边问题 ``` * 复杂分离控制 ![](res/0113.png) ```cpp glEnable(GL_BLEND); glBlendFuncSeparate( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, // RGB 混合 GL_ONE, GL_ONE_MINUS_SRC_ALPHA // Alpha 混合不同 ); glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_MAX); // 方程也可分离 ``` * sorted + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); ![](res/0107.png) * 玻璃之间看起来没有相互混合,深度测试会丢掉后面的片段 ![](res/0108.png) * 通过排序来解决玻璃之间的混合问题 ![](res/0109.png) ### 模板测试 * 绘制模板的时候关闭深度缓冲 ![](res/0101.png) ![](res/0102.png) * 绘制模板的时候不关闭深度缓冲 ![](res/0103.png) ![](res/0104.png) ### 深度测试 * `glEnable(GL_DEPTH_TEST);` 启用深度测试 ![](res/0084.png) * `glDepthMask(GL_FALSE);` (天空盒应用,永远在后面) ![](res/0085.png) * `glDepthFunc(GL_ALWAYS);` 永远通过,相当于是没有深度测试,默认`glDepthFunc(GL_LESS);` ![](res/0086.png) * 深度冲突,地板和箱子距离太近,以至于它们的三角形会重叠 ![](res/0090.png) ![](res/0091.png) ![](res/0092.png) * 深度冲突解决方案 1:增大近远裁剪面比值(效果一般) ![](res/0092.png) * 深度冲突解决方案 2:多精度深度渲染,对数深度(效果很好,但是副作用很大) ![](res/0093.png) * 特定角度下地板显示不全 ![](res/0094.png) * 深度冲突解决方案 3:多边形偏移(Polygon Offset)效果显著 ![](res/0096.png) * 深度冲突解决方案 4:提高深度缓冲精度,请求 32 位深度缓冲 效果很差 ![](res/0097.png) * 深度冲突解决方案 5:避免共面(模型层面)glEnable(GL_CULL_FACE); 剔除背面,效果很糟糕 ![](res/0098.png) ![](res/0099.png) * 深度冲突解决方案 5:避免共面,物理分离,增加0.001单位距离,效果显著 ![](res/0100.png) ## build ```bash # install dependencies conan install . --output-folder=conan_build --build=missing --settings build_type=Release --deployer=direct_deploy # gen project cmake -DCMAKE_INSTALL_PREFIX=./install -S ./ -B ./build # build RelWithDebInfo cmake --build ./build --config Release # install cmake --install ./build --config Release # run .\install\bin\HelloQtOpenGL.exe ``` ## conan config ```bash # install conan pip install conan # install dependencies conan install . --output-folder=conan_build --build=missing --settings build_type=Release -s compiler.cppstd=20 --deployer=direct_deploy ``` ## ref - [x] [conan/qt](https://conan.io/center/recipes/qt?version=5.15.16) - [x] [conan/glm](https://conan.io/center/recipes/glm) - [x] [learnopengl.com code repository](https://github.com/JoeyDeVries/LearnOpenGL) - [x] [欢迎来到OpenGL的世界](https://learnopengl-cn.github.io/) - [x] [Welcome to OpenGL](https://learnopengl.com/)