PBR OpenGL实现核心是跑通albedo、normal、roughness、metallic四张贴图驱动的Cook-Torrance+Lambert混合BRDF:F用fresnelSchlick近似,G用smithJointGGX,D用GGX并clamp NoH避免除零,纹理绑定须按序到连续unit且法线需切线空间变换,黑块主因常为TBN错误、roughness过小或viewDir未归一化。

直接上手 PBR 的 OpenGL 实现,核心不是“写全所有公式”,而是先跑通 albedo、normal、roughness、metallic 四张贴图驱动的 Cook-Torrance + Lambert 混合 BRDF,其余都是在此基础上叠加。别一上来就搞 IBL 或 GGX 积分,90% 的卡顿和黑块都源于这四张纹理没对齐或法线未切线空间变换。
GLSL 里怎么写 Cook-Torrance 镜面项(别硬套论文)
实际工程中用的是简化版 Cook-Torrance,重点是把 F(菲涅尔)、G(几何遮蔽)、D(法线分布)三部分拆开算,且全部用近似函数避免分支和除零:
-
F用fresnelSchlick:输入dot(H, V)和F0(由metallic插值得到),不推荐用完整 Fresnel 公式 -
G推荐smithJointGGX:传入dot(N, L)和dot(N, V),避免用geometrySchlickGGX单独算再相乘,易溢出 -
D用D_GGX:注意alpha = roughness * roughness,不是直接用roughness;NoH必须 clamp 到[0.001, 1.0],否则除零黑斑
示例关键片段(像素着色器):
vec3 F0 = mix(vec3(0.04), albedo.rgb, metallic); float alpha = roughness * roughness; float NoH = max(dot(N, H), 0.001); float D = alpha * alpha / (M_PI * pow(NoH * NoH * (alpha * alpha - 1.0) + 1.0, 2.0));
OpenGL 端怎么传 PBR 材质参数(别只 glBindTexture)
四张贴图必须按顺序绑定到连续纹理单元,并在着色器里用 sampler2D 数组或独立 uniform 声明——但更稳的做法是分四路 glActiveTexture + glBindTexture,并确保 glUniform1i 设置的采样器索引与着色器声明一致:
立即学习“C++免费学习笔记(深入)”;
-
albedoMap→ texture unit 0 →uAlbedoMap -
normalMap→ texture unit 1 →uNormalMap(注意:法线贴图需手动转切线空间,normalMap.xyz = normalMap.xyz * 2.0 - 1.0) -
metallicRoughnessMap→ texture unit 2 →uMetallicRoughnessMap(通常 RG 通道:R=metallic, G=roughness) -
aoMap(可选)→ texture unit 3 →uAoMap
漏掉 glPixelStorei(GL_UNPACK_FLIP_Y_WEBGL, GL_TRUE)(若用 stb_image 加载)会导致法线贴图上下颠倒,渲染发灰或翻车。
为什么 PBR 渲染出来全是黑/粉/过曝(常见错误链)
黑块最常来自三处,不是光照没开,而是数据流断在中间:
- 顶点着色器没输出正确的
TBN矩阵,导致normalMap采样后法线方向错乱 → 检查mat3 TBN = transpose(mat3(T, B, N))是否用了 world-space 的T/B/N,而非 object-space -
roughness贴图值为 0 或接近 0 → GGX 的D项爆炸 → 用texture(...).g后加max(roughness, 0.04) - 光源方向未归一化,或视图矩阵未传入着色器 →
viewDir变成大数,dot(viewDir, reflectDir)超出 [-1,1] → 在片元着色器里强制normalize(viewDir)
粉红噪点通常是 albedo 贴图被加载为 GL_SRGB8 但采样时没设 GL_TEXTURE_SRGB_DECODE_EXT,或着色器里没做 gamma 校正(PBR 计算必须在线性空间)。
延迟渲染下怎么接 PBR(G-Buffer 布局不能照搬 Phong)
PBR 的 G-Buffer 不能只存 worldPos、normal、diffuse,必须预留物理参数通道:
-
gAlbedoSpec:RGB 存albedo,A 存metallic(节省一个纹理单元) -
gMaterial:RG 存roughness和 AO,BA 通道弃用或复用为 emissive - 法线必须存为
RG编码(如packNormal),不能存 XYZ —— 否则 16-bit 浮点精度不够,重建后法线抖动明显
延迟光照 Pass 中,lighting 计算必须读全四通道,且 specular 分量要从 metallic 和 albedo 共同推导,不能像 Phong 那样直接用 specColor。
真正卡住人的从来不是公式推导,而是 roughness 贴图没 clamp、normalMap 没解包、TBN 矩阵构造错这三行代码。先让单光源 + 平面模型亮起来,再扩多光源、IBL、SSAO——PBR 是管线,不是特效开关。


















