
中级OpenGL教程 030gl_FragCoord解锁区域渲染与深度可视化新姿势 前言那些年我们一起踩过的深度坑 哈喽各位图形学的小伙伴们大家好呀相信学过 OpenGL 基础的童鞋们对 ** 深度检测Depth Test** 这个词一定不陌生 —— 毕竟它可是 3D 渲染的 守门员没有它你的 3D 世界就会乱成一锅粥远处的物体穿墙跑到前面来近处的物体反而被挡住简直就是视觉灾难现场️但是今天咱们要聊的可不是初级课程里那点 开胃小菜。深度检测这玩意儿水可深着呢今天就带大家深挖一下gl_FragCoord这个 GLSL 内置变量的神奇用法保证让你大呼哇塞原来深度检测还能这么玩温馨提示本文适合有 OpenGL 基础的童鞋阅读如果连glEnable(GL_DEPTH_TEST)都没听过建议先去补补基础课再来 “修仙” 哦Bilibili 同步视频中级OpenGL教程 030gl_FragCoord解锁区域渲染与深度可视化新姿势一、温故知新深度检测基础快速回顾在正式开搞之前咱们先 “复习复习”把基础知识点再过一遍毕竟地基打牢了才能盖高楼嘛️1.1 什么是深度检测所谓深度Depth说白了就是当前片元Fragment距离摄像机的远近程度。就像你站在山顶往下看离你近的树看得清楚离你远的山朦朦胧胧 —— 深度值就是用来度量这个 “远近程度” 的数值。深度检测的核心逻辑超级简单当两个片元像素位置相同时谁离摄像机近深度值小谁就显示在前面谁离得远深度值大谁就被挡住。就这么简单粗暴但就是这么一个简单的机制撑起了整个 3D 渲染的立体感1.2 核心 API 四件套️初级课咱们讲过四个核心 API今天再拎出来晒晒API 函数作用常用参数glEnable(GL_DEPTH_TEST)开启深度检测-glDepthFunc(GLenum func)设置深度比较规则GL_LESS/GL_GREATER/GL_ALWAYS等glClear(GL_DEPTH_BUFFER_BIT)清空深度缓冲-glClearDepth(GLdouble depth)设置深度缓冲清空值默认 1.0// ✨ 经典深度检测初始化代码模板 ✨glEnable(GL_DEPTH_TEST);// 开启深度检测大门glDepthFunc(GL_LESS);// 深度小的才能通过近的挡住远的glClearDepth(1.0f);// 用最大值1.0清空深度缓冲// 渲染循环中每一帧都要清空哦glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);小知识深度缓冲的值范围是[0.0, 1.0]0.0 代表离摄像机最近1.0 代表最远。默认清空值是 1.0因为每一帧开始时我们假设所有像素都 “最远”然后逐步被更近的物体覆盖。二、今天的主角登场gl_FragCoord 深度解析好了基础复习完毕接下来有请今天的绝对主角——gl_FragCoord闪亮登场✨2.1 gl_FragCoord 是什么️gl_FragCoord是 GLSL 中一个内置的三维向量变量vec3它在片元着色器Fragment Shader中自动可用不需要你声明直接拿来用就行是不是感觉很神奇就像你的 Shader 里偷偷藏了一个 “间谍”随时告诉你当前片元的位置情报️♂️2.2 三个分量的秘密gl_FragCoord的三个分量各有分工咱们一个一个扒gl_FragCoord.x分量屏幕横向像素坐标.y分量屏幕纵向像素坐标.z分量当前片元深度值范围[0.0, 1.0] xy 分量屏幕像素坐标gl_FragCoord.x和gl_FragCoord.y存储的是当前片元在屏幕上的像素位置。举个栗子假设你的窗口是 1600×1200 像素当渲染到屏幕中间那个像素时gl_FragCoord.x 800.0横向中间gl_FragCoord.y 600.0纵向中间就这么直观你想知道当前片元在屏幕的哪个位置直接读这俩值就完事儿了 z 分量深度值gl_FragCoord.z存储的是当前片元的深度值范围在 [0.0, 1.0] 之间越接近0.0→ 离摄像机越近越接近1.0→ 离摄像机越远划重点这个 z 值和深度缓冲里存的值是同一个东西也就是说你可以在片元着色器里直接读取当前片元的深度想怎么玩就怎么玩三、实战一用 gl_FragCoord.xy 实现 “区域渲染” 效果理论说了一大堆是骡子是马拉出来遛遛咱们先来第一个实战区域渲染效果。3.1 效果预览什么是区域渲染简单说就是屏幕不同区域显示不同的颜色滤镜。比如咱们实现一个效果屏幕左半部分偏红色右半部分偏蓝色中间一条锐利的分界线。就像把屏幕劈成了两半一半是火焰一半是海洋想象一下一个 3D 模型在屏幕上移动时跑到左边就发红跑到右边就发蓝是不是很酷3.2 实现原理实现思路超级简单三步走x 800 左边x 800 右边获取当前片元屏幕X坐标gl_FragCoord.x与分界线800比较step函数判断左右返回0 → 红色返回1 → 蓝色mix函数混合颜色与原始颜色相乘叠加滤镜效果核心用到两个 GLSL 内置函数step(edge, x)阶跃函数x edge 返回 0x edge 返回 1mix(a, b, t)线性插值返回 a*(1-t) b*t3.3 完整代码实现片元着色器代码GLSL#version 330 core out vec4 FragColor; in vec3 ourColor; // 从顶点着色器传来的原始颜色 in vec2 TexCoord; // 纹理坐标 uniform sampler2D ourTexture; // 纹理采样器 void main() { // 1️⃣ 先采样纹理拿到原始颜色 vec4 originalColor texture(ourTexture, TexCoord); // 2️⃣ 用step函数判断左右区域 // 分界线设为800像素假设窗口宽1600 float threshold 800.0f; float regionFlag step(threshold, gl_FragCoord.x); // regionFlag 0 表示左边 1 表示右边 // 3️⃣ 定义两种滤镜颜色左红右蓝 vec3 colorLeft vec3(1.0f, 0.0f, 0.0f); // 红色滤镜 vec3 colorRight vec3(0.0f, 0.0f, 1.0f); // 蓝色滤镜 // 4️⃣ mix函数混合两种滤镜颜色 vec3 filterColor mix(colorLeft, colorRight, regionFlag); // 5️⃣ 原始颜色 × 滤镜颜色 最终效果 FragColor vec4(originalColor.rgb * filterColor, originalColor.a); }C 端关键代码// // ️ OpenGL初始化部分// voidinitOpenGL(){// 开启深度检测必须的glEnable(GL_DEPTH_TEST);glDepthFunc(GL_LESS);// 设置窗口大小假设1600x1200constintSCR_WIDTH1600;constintSCR_HEIGHT1200;// 创建窗口...略// 编译链接着色器程序ShaderourShader(vertex.glsl,fragment.glsl);// 加载模型和纹理...略}// // 渲染循环// voidrenderLoop(){while(!glfwWindowShouldClose(window)){// 清空颜色缓冲和深度缓冲glClearColor(0.1f,0.1f,0.1f,1.0f);glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);// 激活着色器ourShader.use();// 设置MVP矩阵...略// 绘制模型ourModel.Draw(ourShader);// 交换缓冲处理事件glfwSwapBuffers(window);glfwPollEvents();}}3.4 原理解析为什么这么写就能实现区域分色咱们来拆解一下1️⃣step 函数的妙用当gl_FragCoord.x 800时regionFlag 0当gl_FragCoord.x 800时regionFlag 1就像一个开关非 0 即 1分界线锐利无比⚡2️⃣mix 函数的插值魔法mix(colorLeft, colorRight, 0)→ 完全返回colorLeft红色mix(colorLeft, colorRight, 1)→ 完全返回colorRight蓝色如果 regionFlag 是 0.5那就是 50% 红 50% 紫哦不对是 50% 红 50% 蓝 紫色3️⃣颜色相乘 滤镜效果原始颜色 × 红色滤镜 (1,0,0) 只保留红色通道原始颜色 × 蓝色滤镜 (0,0,1) 只保留蓝色通道就像给模型戴了一副有色眼镜️拓展思考如果想实现上下分色怎么办很简单把gl_FragCoord.x改成gl_FragCoord.y就行如果想实现四象限分色那就 x 和 y 都判断一下组合起来用发挥你的想象力吧四、实战二用 gl_FragCoord.z 实现 “深度可视化”看完了 xy 分量的玩法咱们再来看看 z 分量能搞出什么名堂4.1 什么是深度可视化️深度可视化顾名思义把深度值直接当成颜色显示出来。因为深度值是 [0.0, 1.0] 的灰度值所以渲染出来就是一张黑白灰的深度图离相机近的地方 → 颜色深偏黑离相机远的地方 → 颜色浅偏白就像给你的 3D 场景拍了一张 X 光片能直观看到每个像素的深度信息4.2 实现原理这个更简单一句话就能说清楚把gl_FragCoord.z同时赋值给 RGB 三个通道输出灰度图。因为 RGB 三个值相等的时候显示的就是灰色读取gl_FragCoord.z同时赋值给R通道同时赋值给G通道同时赋值给B通道输出灰度颜色4.3 完整代码实现片元着色器代码GLSL#version 330 core out vec4 FragColor; void main() { // 核心代码就一行 // 把深度值同时赋值给RGB三个通道 float depth gl_FragCoord.z; FragColor vec4(depth, depth, depth, 1.0f); // 或者更简洁的写法 // FragColor vec4(vec3(gl_FragCoord.z), 1.0); }C 端辅助代码深度值调试输出// // 调试工具读取屏幕中心像素的深度值// voidprintCenterDepth(intscreenWidth,intscreenHeight){// 读取屏幕中心像素的深度值floatdepthValue;glReadPixels(screenWidth/2,// x坐标中心screenHeight/2,// y坐标中心1,1,// 读取1x1像素GL_DEPTH_COMPONENT,// 读取深度分量GL_FLOAT,// 浮点格式depthValue// 输出到变量);std::cout 屏幕中心深度值: depthValuestd::endl;// 深度值解读if(depthValue0.3f){std::cout → 离相机很近哦std::endl;}elseif(depthValue0.7f){std::cout → 距离适中std::endl;}else{std::cout → 离相机比较远std::endl;}}4.4 效果演示说明运行程序后你会看到初始状态下整个画面偏白→ 说明模型离相机比较远深度值接近 1.0当你把相机拉近模型时画面逐渐变灰→ 深度值在减小当相机贴到模型脸上时画面几乎变黑→ 深度值接近 0.0就像你在调整相机的 “焦距” 一样实时看到深度的变化是不是很直观进阶知识你可能会发现深度值的变化不是线性的 —— 大部分时候都是偏白的只有离很近时才会明显变深。这是因为深度缓冲使用了非线性的深度分布近处精度高远处精度低这是为了更好地匹配人眼的视觉特性哦五、进阶思考性能分析与更多玩法5.1 性能影响分析⚡看到这里可能有小伙伴会问“在片元着色器里读 gl_FragCoord 会不会很耗性能”好问题咱们来分析一下操作性能开销说明读取gl_FragCoord.xy 极低硬件原生支持几乎零开销读取gl_FragCoord.z 极低深度值本来就要计算读取只是顺便的事step()函数 极低硬件级指令单周期完成mix()函数 极低同样是硬件级插值指令✅结论放心大胆地用这些操作在现代 GPU 上都是 洒洒水啦只要不是在片元着色器里写复杂的循环基本不会影响性能。5.2 gl_FragCoord 的更多应用场景gl_FragCoord的玩法可不止今天讲的这两个它的应用场景超级广泛屏幕后处理效果径向模糊中心清晰边缘模糊暗角效果Vignette屏幕扭曲、水波纹效果基于深度的特效雾效Fog深度越大雾越浓景深Depth of Field模拟相机焦外虚化边缘检测通过深度差找出物体轮廓调试与可视化深度缓冲可视化今天讲的法线可视化UV 坐标可视化可以说gl_FragCoord 是你通往高级后处理和特效世界的第一把钥匙掌握了它你就打开了新世界的大门5.3 注意事项⚠️最后再提醒几个容易踩的坑1️⃣坐标原点问题gl_FragCoord的原点在左下角和 Windows 窗口的左上角原点不一样哦如果你的纹理是上下颠倒的可能和这个有关2️⃣深度值非线性前面提过gl_FragCoord.z不是线性分布的如果需要线性深度值需要自己转换用投影矩阵反推3️⃣多渲染目标MRT在高级渲染中你可能会同时输出颜色、深度、法线等多个缓冲gl_FragCoord 只是其中一个信息源六、总结回顾好了今天的内容就到这里啦咱们来总结一下今天学到的知识点 核心知识点清单知识点关键内容难度gl_FragCoordGLSL 内置三维向量片元着色器中直接可用⭐.xy分量当前片元在屏幕上的像素坐标⭐.z分量当前片元的深度值范围 [0.0, 1.0]⭐⭐step()函数阶跃函数用于判断边界⭐mix()函数线性插值函数颜色混合神器⭐区域渲染利用屏幕坐标实现分区域滤镜效果⭐⭐深度可视化把深度值直接当灰度图输出⭐⭐ 今天的两个实战✅区域渲染效果用gl_FragCoord.xstepmix实现屏幕左右分色✅深度可视化用gl_FragCoord.z直接输出深度灰度图是不是感觉收获满满 写在最后今天咱们从深度检测的基础出发深入探索了gl_FragCoord这个神奇的内置变量还动手实现了两个超有意思的效果。但这只是冰山一角哦图形学的世界博大精深深度检测、片元着色器这些东西越挖越有料后续还会给大家带来更多进阶内容比如深度值的非线性分布原理与线性化基于深度的边缘检测与轮廓渲染阴影映射Shadow Mapping技术后处理管线的搭建敬请期待哦如果这篇文章对你有帮助别忘了点赞、收藏⭐、评论三连支持一下你的鼓励就是我持续创作的最大动力 如果发现文中有什么错误或者遗漏欢迎在评论区指正大家一起进步 推荐阅读《OpenGL 编程指南》红宝书- 深度检测章节LearnOpenGL 官网 - Depth Testing 教程《Real-Time Rendering》- 深度与可见性算法✨ 愿你在图形学的世界里永远保持好奇心永远热泪盈眶我们下期再见技术博客已创作完成✨ 以下是这篇 CSDN 博客的核心亮点 博客核心亮点 标题《【OpenGL 硬核进阶】深度检测还能这么玩gl_FragCoord 解锁区域渲染与深度可视化新姿势✨》 全文结构六大章节前言幽默引入勾起阅读兴趣深度检测基础回顾温故知新API 四件套表格化梳理gl_FragCoord 深度解析三维分量逐一拆解配 Mermaid 结构图实战一区域渲染左红右蓝分屏效果含完整 GLSLC 代码实战二深度可视化深度灰度图输出含深度值调试工具代码进阶思考性能分析、更多应用场景、注意事项 特色元素元素数量说明 Emoji 符号60 个覆盖各章节标题、重点提示、代码注释 代码块6 段GLSL 着色器 C OpenGL 代码 Mermaid 流程图3 张分量拆解图、区域渲染流程图、深度可视化流程图 表格4 张API 对比、性能分析、知识点总结等 实战案例2 个区域渲染 深度可视化 扩展增值内容✅性能影响分析各操作 GPU 开销评级✅更多应用场景后处理、景深、雾效等 6 扩展方向✅注意事项坐标原点、非线性深度等踩坑提醒✅推荐阅读红宝书、LearnOpenGL 等权威资料