Skip to content

Commit 4a60e11

Browse files
committed
0.03
1 parent f0d0379 commit 4a60e11

7 files changed

Lines changed: 612 additions & 8 deletions

docs/OpenGL/00-目录.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
按顺序学即可:每篇都以“先看到结果”为优先级,然后再解释背后的关键概念与工程化组织方式。
44

5+
本教程的最终落地目标是:用 **Visual Studio + CMake + vcpkg** 搭出一个可迭代的小型渲染器(能画、可调试、可扩展)。
6+
57
## 学习路线
68

79
- [01-概念:GPU、驱动、上下文、渲染管线](01-概念-GPU驱动上下文渲染管线.md)
@@ -11,13 +13,13 @@
1113
- [05-顶点属性与插值:颜色、varying、Gamma 的直觉](05-顶点属性与插值-颜色-varying-Gamma直觉.md)
1214
- [06-纹理:采样、wrap/filter、mipmap、sRGB](06-纹理-采样-wrap-filter-mipmap-sRGB.md)
1315
- [07-相机:LookAt、轨道相机、第一人称相机](07-相机-LookAt-轨道相机-第一人称相机.md)
14-
- [08-深度与裁剪:depth test、face culling、clip space](08-深度与裁剪-depth-test-face-culling-clip-space.md)
16+
- [08-深度与裁剪:depth test、face culling、clip space(含透明混合/Stencil)](08-深度与裁剪-depth-test-face-culling-clip-space.md)
1517
- [09-模型与网格:OBJ/GLTF(简化版)加载、法线、切线](09-模型与网格-OBJ-GLTF加载-法线-切线.md)
1618
- [10-光照基础:Lambert/Phong/Blinn-Phong 与能量直觉](10-光照基础-Lambert-Phong-BlinnPhong-能量直觉.md)
1719
- [11-阴影入门:Shadow Map 的最小实现与常见伪影](11-阴影入门-ShadowMap最小实现-常见伪影.md)
18-
- [12-离屏渲染:FBO、后处理、HDR/曝光](12-离屏渲染-FBO-后处理-HDR-曝光.md)
19-
- [13-性能与工程:状态切换、批处理、调试与 Profiling](13-性能与工程-状态切换-批处理-调试-Profiling.md)
20-
- [14-PBR 入门:BRDF 的直觉与实现骨架](14-PBR入门-BRDF直觉-实现骨架.md)
20+
- [12-离屏渲染:FBO、后处理、HDR/曝光(含 MSAA/AA 路线)](12-离屏渲染-FBO-后处理-HDR-曝光.md)
21+
- [13-性能与工程:状态切换、批处理、调试与 Profiling(含 UBO/Instancing/RenderDoc)](13-性能与工程-状态切换-批处理-调试-Profiling.md)
22+
- [14-PBR 入门:BRDF 的直觉与实现骨架(含 Cubemap/IBL 最小链路)](14-PBR入门-BRDF直觉-实现骨架.md)
2123

2224
## 参考阅读(Inigo Quilez)
2325

docs/OpenGL/02-搭工程-CMake窗口上下文Loader调试输出.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- 搭出一个“稳定可复用”的最小工程骨架:创建窗口 → 创建上下文 → 加载 OpenGL 函数 → 帧循环
88
- 把黑屏变成“可观测问题”:打印版本信息、打开调试输出、确保 resize 不出错
99
- 为下一章画三角形准备好:清屏、viewport、输入事件的最小支持
10+
- 把工程落地到 VS:用 vcpkg 管依赖,用 VS/CMake 进行配置与调试
1011

1112
## 先决条件
1213

@@ -40,6 +41,20 @@ OpenGLPlayground/
4041
(后续再加)
4142
```
4243

44+
如果你的终点是“小型渲染器”,建议从一开始就把骨架稍微工程化一点(后续章节可以逐步填充,不要求现在就写满):
45+
46+
```
47+
OpenGLPlayground/
48+
CMakeLists.txt
49+
src/
50+
app/
51+
main.cpp
52+
renderer/
53+
(后续逐步拆:Shader/Mesh/Texture/Camera/Material/Pass)
54+
shaders/
55+
assets/
56+
```
57+
4358
---
4459

4560
## 3. 获取依赖:两种常用方式
@@ -61,6 +76,20 @@ vcpkg install glfw3 glm
6176
-DCMAKE_TOOLCHAIN_FILE=.../vcpkg/scripts/buildsystems/vcpkg.cmake
6277
```
6378

79+
如果你的目标是“Visual Studio 里一键配置/构建/调试”,更推荐的工程口径是:
80+
81+
- vcpkg 走 manifest(`vcpkg.json`)或至少固定 triplet(例如 `x64-windows`
82+
- CMake 用 Presets 或 VS 的 CMake 集成来选择工具链与 triplet
83+
84+
概念示例(CMake configure 时传入):
85+
86+
```
87+
-DCMAKE_TOOLCHAIN_FILE=.../vcpkg/scripts/buildsystems/vcpkg.cmake
88+
-DVCPKG_TARGET_TRIPLET=x64-windows
89+
```
90+
91+
这样你在 VS 里切 Debug/Release、切 x64 架构时,依赖也能跟着稳定复用。
92+
6493
GLAD 通常不是 vcpkg 主线方案(也可以装),更常见是“直接把生成的 glad 源码放进工程”。本教程在“讲清楚原理”的前提下,建议你用自己生成的 glad。
6594

6695
### 3.2 方式 B:CMake FetchContent(不依赖包管理器)

docs/OpenGL/08-深度与裁剪-depth-test-face-culling-clip-space.md

Lines changed: 135 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,134 @@ glFrontFace(GL_CCW);
105105
106106
---
107107
108+
## 6. 透明与混合(Blending):从“能画”到“看起来对”
109+
110+
透明不是“把 alpha 写成 0.5”,它是一整套规则:
111+
112+
- 颜色怎么混合(blend)
113+
- 深度怎么处理(depth write / depth test)
114+
- 绘制顺序怎么安排(排序)
115+
116+
### 6.1 开启混合:最常见的 alpha 混合
117+
118+
初始化(一次即可):
119+
120+
```cpp
121+
glEnable(GL_BLEND);
122+
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
123+
```
124+
125+
如果你的片元输出 `vec4(rgb, a)`,那么最终颜色约等于:
126+
127+
- out = src * a + dst * (1-a)
128+
129+
### 6.2 深度与透明:最容易踩坑的组合
130+
131+
不透明物体的习惯是:
132+
133+
- depth test 开
134+
- depth write 开(默认)
135+
- 顺序随意(通常前到后更省像素)
136+
137+
透明物体通常更稳的规则是:
138+
139+
- depth test 开(仍然需要遮挡)
140+
- depth write 关(避免“透明物体把后面的透明物体挡死”)
141+
- 按相机距离从远到近画(保证混合正确)
142+
143+
绘制透明物体前(每帧,开始画透明队列之前):
144+
145+
```cpp
146+
glDepthMask(GL_FALSE);
147+
```
148+
149+
画完透明队列后恢复:
150+
151+
```cpp
152+
glDepthMask(GL_TRUE);
153+
```
154+
155+
### 6.3 预乘 Alpha(Premultiplied Alpha):UI/贴图常见语义
156+
157+
有些贴图数据是“预乘 alpha”的:RGB 已经乘过 A。
158+
159+
这时对应的混合因子通常是:
160+
161+
```cpp
162+
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
163+
```
164+
165+
你可以用一个工程口径记住区别:
166+
167+
- 非预乘:src.rgb 还没乘 a,用 `SRC_ALPHA`
168+
- 预乘:src.rgb 已经乘 a,用 `ONE`
169+
170+
---
171+
172+
## 7. 模板缓冲(Stencil):用一个 8-bit mask 做“分区域渲染”
173+
174+
模板缓冲的直觉是:每个像素除了 color/depth,还有一个小小的整数标签(常见 8-bit)。
175+
176+
你可以用它做很多“效果技巧”:
177+
178+
- 描边/高亮(先写模板,再只在模板外画一圈)
179+
- 镜子/洞口/门户(只在模板区域画某个 pass)
180+
- HUD/遮罩(限制绘制范围)
181+
182+
### 7.1 开启模板测试与基本用法
183+
184+
初始化(一次即可):
185+
186+
```cpp
187+
glEnable(GL_STENCIL_TEST);
188+
```
189+
190+
最小配置(概念上):
191+
192+
- `glStencilFunc`:通过条件(参考值 vs 当前 stencil 值)
193+
- `glStencilOp`:通过/失败时怎么改 stencil 值
194+
195+
示例:先把某个物体所在像素的 stencil 写成 1:
196+
197+
```cpp
198+
glStencilMask(0xFF);
199+
glStencilFunc(GL_ALWAYS, 1, 0xFF);
200+
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
201+
```
202+
203+
然后再画第二遍,只允许在 stencil != 1 的地方画(用于描边外扩):
204+
205+
```cpp
206+
glStencilMask(0x00);
207+
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
208+
```
209+
210+
画完恢复写入:
211+
212+
```cpp
213+
glStencilMask(0xFF);
214+
glStencilFunc(GL_ALWAYS, 0, 0xFF);
215+
```
216+
217+
### 7.2 记住“清理”与 FBO 的关系
218+
219+
如果你启用了 stencil buffer,每帧清理要把 stencil 也清掉:
220+
221+
```cpp
222+
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
223+
```
224+
225+
离屏渲染时,如果你的 FBO 没有 stencil 附件,你再怎么 `glEnable(GL_STENCIL_TEST)` 也不会按你预期工作。
226+
227+
---
228+
108229
## 常见坑
109230

110231
- **只清了颜色没清深度**:画面会越来越怪
111232
- **near/far 乱填**:深度问题会在你以为“还没学到那一步”时就出现
112233
- **剔除开启后全消失**:先检查绕序,再检查是否有负缩放
234+
- **透明物体“越画越黑/越画越怪”**:检查混合因子、深度写入、以及是否按远到近排序
235+
- **Stencil 没效果**:检查是否有 stencil buffer,是否清了 stencil,是否禁用了 stencil 写入(mask)
113236

114237
---
115238

@@ -125,13 +248,24 @@ glFrontFace(GL_CCW);
125248
- 题目:画两个几乎重叠的平面,调整 near/far 观察闪烁变化
126249
- 目标:把“深度精度”从概念变成现象
127250

251+
### 练习 3:做一个透明 Billboard(树叶/玻璃)并排序
252+
253+
- 题目:画多张带 alpha 的四边形,按相机距离从远到近绘制
254+
- 目标:把“透明 = 混合 + 深度策略 + 顺序”跑通
255+
256+
### 练习 4:做一个描边效果(Stencil)
257+
258+
- 题目:第一遍写 stencil=1,第二遍略微放大模型,只在 stencil!=1 的区域画纯色边
259+
- 目标:掌握 stencil 的最常用工程套路
260+
128261
---
129262

130263
## 小结
131264

132265
- 3D 必须开启深度测试,并且每帧清 depth
133266
- 裁剪/视锥问题优先从 Model/View/Projection 去排查
134267
- 面剔除是简单有效的性能手段,但要理解绕序与负缩放
268+
- 透明渲染要同时管住:混合、深度写入、绘制顺序
269+
- Stencil 是“分区域渲染”的强力工具,描边/遮罩类效果都离不开它
135270

136271
下一篇:见 [09-模型与网格:OBJ/GLTF(简化版)加载、法线、切线](09-模型与网格-OBJ-GLTF加载-法线-切线.md)
137-

docs/OpenGL/12-离屏渲染-FBO-后处理-HDR-曝光.md

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,87 @@ HDR 的工程路线通常是:
119119

120120
---
121121

122+
## 7. MSAA:最“白给”的抗锯齿(以及离屏怎么接)
123+
124+
MSAA(多重采样)解决的是“几何边缘的锯齿”。它的工程特点是:
125+
126+
- 对边缘更明显(尤其是对比强烈的轮廓)
127+
- 对 shader 内部细节(贴图高频、subpixel 线条)帮助有限
128+
- 成本通常比更复杂的后处理 AA 更可控
129+
130+
### 7.1 默认 framebuffer 的 MSAA(最简单)
131+
132+
如果你直接渲染到默认 framebuffer,通常只需要:
133+
134+
1. 创建窗口时请求 samples
135+
2. 开启 `GL_MULTISAMPLE`
136+
137+
GLFW 示例(创建窗口前):
138+
139+
```cpp
140+
glfwWindowHint(GLFW_SAMPLES, 4);
141+
```
142+
143+
初始化(一次即可):
144+
145+
```cpp
146+
glEnable(GL_MULTISAMPLE);
147+
```
148+
149+
这条路的限制是:你不容易把“MSAA 后的结果”当纹理采样来做后处理。
150+
151+
### 7.2 离屏 + MSAA:需要一次 resolve(blit)
152+
153+
如果你既要 MSAA,又要后处理,常见链路是两级 FBO:
154+
155+
- MSAA FBO:渲染场景(附件是多重采样 renderbuffer/texture)
156+
- Resolve FBO:把 MSAA 结果 resolve 到普通 texture(用于 screen pass 采样)
157+
158+
关键点是:多重采样纹理不能直接用 `sampler2D` 当普通纹理采样(它是 `sampler2DMS`)。
159+
160+
工程上更常用的是 resolve:
161+
162+
```cpp
163+
glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFbo);
164+
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFbo);
165+
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
166+
```
167+
168+
之后 screen pass 采样 `resolveFbo` 的 color texture 就和你当前章节的流程一致了。
169+
170+
### 7.3 MSAA FBO 的附件怎么选
171+
172+
最省心的组合通常是:
173+
174+
- color:多重采样 renderbuffer(只用于 resolve,不需要采样)
175+
- depth/stencil:多重采样 renderbuffer
176+
177+
如果你确实想直接在 shader 里采 MSAA(比如自己做 resolve 或某些特殊效果),才会去用多重采样 texture + `sampler2DMS`。
178+
179+
---
180+
181+
## 8. 抗锯齿路线:MSAA / FXAA / TAA 怎么选
182+
183+
这里先给一个“工程口径”,避免初学阶段被名词拖走:
184+
185+
- MSAA:解决几何边缘锯齿;实现简单;离屏需要 resolve
186+
- FXAA:后处理,成本低;对贴图/细线也有帮助,但会略糊
187+
- TAA:画质潜力大但工程复杂(历史缓冲、抖动、重投影、拖影处理)
188+
189+
如果你的教程主线目标是“稳定做项目”:
190+
191+
- 先把 MSAA 跑通(默认 framebuffer 或离屏 resolve 版)
192+
- 再做一个 FXAA(作为 screen pass 的可选后处理)
193+
194+
---
195+
122196
## 常见坑
123197
124198
- **FBO 不完整**:附件没配齐,必须检查 framebuffer status
125199
- **viewport 没恢复**:离屏尺寸与窗口尺寸不同,容易导致最终画面缩放/裁剪异常
126200
- **深度丢失**:scene pass 没有深度附件会导致 3D 遮挡错误
201+
- **MSAA 开了但看不出区别**:确认是否真的创建了多重采样 buffer,以及是否开启了 `GL_MULTISAMPLE`
202+
- **离屏 MSAA 后处理异常**:确认是否做了 resolve(blit),以及采样的是否是普通 texture
127203
128204
---
129205
@@ -139,13 +215,18 @@ HDR 的工程路线通常是:
139215
- 题目:在 screen pass 里加 `exposure`,做一次简单 tone mapping(例如 `1 - exp(-c * exposure)`)
140216
- 目标:建立 HDR→LDR 的最小通路
141217
218+
### 练习 3:把离屏渲染升级为“MSAA + resolve + 后处理”
219+
220+
- 题目:scene pass 渲染到 MSAA FBO,再 blit 到普通 FBO 的纹理,最后做 screen pass 后处理
221+
- 目标:把“抗锯齿”和“后处理”合并成一条稳定链路
222+
142223
---
143224
144225
## 小结
145226
146227
- FBO 让你把“一帧”变成一张可采样的纹理
147228
- 后处理就是屏幕空间 shader:先让链路跑通再追求复杂效果
148229
- HDR 的核心是先装下亮度范围,再做映射与曝光控制
230+
- MSAA 是高性价比的几何抗锯齿;离屏时要记得 resolve 到普通纹理
149231
150232
下一篇:见 [13-性能与工程:状态切换、批处理、调试与 Profiling](13-性能与工程-状态切换-批处理-调试-Profiling.md)
151-

0 commit comments

Comments
 (0)