Files
tactileipc3d/BACKGROUND_GRID.md
2025-12-18 09:19:39 +08:00

135 lines
4.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 屏幕空间背景(白底 + 灰色网格线)说明
这份文档解释项目里新增的“3D 软件常见的白色背景 + 灰色分割线网格”是怎么实现的,以及如何调整效果。
需求点:
- 背景看起来更“科技感”(轻微渐变 + 网格线 + vignette
- **背景不随相机旋转**(不受 yaw/pitch/zoom 影响)
---
## 实现方式概览(为什么它不会旋转)
我们采用“屏幕空间screen-space”绘制方式
1. **画一个全屏 quad**(两个三角形),顶点坐标直接写在裁剪空间 NDC[-1,1]
2. fragment shader 使用 `gl_FragCoord`(屏幕像素坐标)生成网格线
由于整个背景是直接画在屏幕坐标系里,不使用 `uMVP`,所以它不会跟着相机旋转或移动。
---
## 关键文件
- 资源注册:`resources.qrc`
- 新增:`shaders/bg.vert``shaders/bg.frag`
- Shader
- `shaders/bg.vert`:全屏 quad 的顶点 shader裁剪空间直出
- `shaders/bg.frag`:背景颜色 + 抗锯齿网格线(基于 `gl_FragCoord`
- C++OpenGLWidget
- `src/glwidget.h`:新增 `m_bgProg``m_bgVao/m_bgVbo``initBackgroundGeometry_()`
- `src/glwidget.cpp`
- `GLWidget::initPrograms_()`:编译/链接背景 program
- `GLWidget::initBackgroundGeometry_()`:创建全屏 quad VAO/VBO
- `GLWidget::paintGL()`:先画背景(关闭深度写入),再画 3D 内容
---
## 渲染顺序(为什么不会影响 3D 深度)
背景绘制发生在 `GLWidget::paintGL()` 的最前面:
1. `glClear(...)` 清空颜色/深度
2. **绘制背景**(屏幕空间)
- `glDisable(GL_DEPTH_TEST);`
- `glDepthMask(GL_FALSE);`(不写深度)
3. 恢复深度状态
- `glDepthMask(GL_TRUE);`
- `glEnable(GL_DEPTH_TEST);`
4. 再绘制 panel / dots正常 3D 深度测试)
因此:背景永远在最底层,而且不会把深度缓冲弄脏。
---
## 全屏 quad背景几何
`GLWidget::initBackgroundGeometry_()` 创建一个覆盖整个屏幕的矩形(两个三角形):
- 顶点坐标是 NDC裁剪空间
- (-1,-1) 到 (1,1)
- 顶点 shader`shaders/bg.vert`)仅仅把它输出到 `gl_Position`
这样不需要任何相机矩阵,也不会“跟着相机”动。
---
## 网格线怎么画出来的bg.frag
`shaders/bg.frag` 主要做了三件事:
### 1) 背景渐变底色
- 使用 `uv = gl_FragCoord.xy / uViewport` 得到 0..1 的屏幕坐标
- 在 y 方向做一个轻微渐变(上更亮、下稍灰)
### 2) 细网格 + 粗网格(分割线)
`uMinorStep` / `uMajorStep`(单位:像素)控制网格间距:
- `uMinorStep`:细分格子(更淡)
- `uMajorStep`:粗分格子(更明显)
网格线本质是对 `fract(coord / step)` 做“距离最近线”的计算,然后用 `fwidth` 做抗锯齿:
- `fwidth` 会随着屏幕像素密度和视角变化自动调整边缘过渡,避免锯齿
### 3) 轻微 vignette
四角略暗、中心略亮,让画面更聚焦、更像 3D 软件视口。
---
## HiDPI 注意点(为什么要乘 devicePixelRatio
Qt 的 `width()/height()` 是“逻辑像素”;而 `gl_FragCoord` 是“物理像素”。
所以背景在 C++ 里传入:
- `uViewport = (width * dpr, height * dpr)`
- 网格 step 也乘 `dpr`
否则在高 DPI 屏幕上网格会显得“变密/变粗”。
---
## 如何调整外观(常用参数)
### 调整网格密度
`src/glwidget.cpp` 里设置了:
- `uMinorStep = 24 px`(细线间距)
- `uMajorStep = 120 px`(粗线间距)
改这两个值就能让网格更密/更稀。
### 调整颜色/强度/科技感
`shaders/bg.frag` 里可以改:
- `topCol/botCol`:背景渐变颜色
- `minorCol/majorCol`:网格线颜色
- `mix(...)` 的系数:线条深浅
- vignette 强度:`dot(p,p)` 前面的系数
---
## 可选增强(如果你想更像 Blender/Unity
- 加“坐标轴线”X 红、Z 蓝或灰色加深)并在中心画十字
- 增加 UI 开关:显示/隐藏网格、调 step、调强度
- 增加“地平线”或 “ground plane” 的淡淡雾化效果