RenderPass与RenderGraph
需要预先了解: IDRenderer的设计与实现 RenderPass与RenderGraph 一帧画面不是"把物体画上去"一句话能概括的:先从光源视角渲染阴影贴图,再画不透明物体,然后天空盒、透明物体,最后后处理。每一步都是一个RenderPass(渲染工序);这些工序之间有依赖——透明混合必须等不透明物体和天空都画完才能开始。手动维护这个顺序,Pass 一多就容易错。 IDGameEngine 的做法是把这些工序和它们的依赖组织成一张图:RenderGraph(渲染图)。Pass 声明自己读写哪些资源,图负责推导依赖、排出执行顺序、剔除无用的 Pass。这篇笔记梳理这套机制的实现。 RenderPass:渲染工序的抽象 RenderPass 是一个抽象基类,子类只需要关心两件事: class RenderPass { public: RenderPass(const std::string& name = "RenderPass"); // 声明阶段:声明本 Pass 读/写哪些资源(add_pass 时...
标准库(二):JSON 与 HTTP 服务
需要预先了解: 结构体与指针 标准库(一):IO 与系统交互 context 标准库(二):JSON 与 HTTP 服务 encoding/json 和 net/http 是写服务端程序的两块基石。这一篇从"结构体怎么变成 JSON"讲到"一个带中间件、超时和优雅关闭的迷你 REST 服务",把 反射与 unsafe 里看到的 tag 机制落到实际用法上。 encoding/json 基础 两个函数覆盖 90% 的场景: b, err := json.Marshal(v) // 值 -> JSON 字节切片 err = json.Unmarshal(data, &v) // JSON -> 值(必须传指针) u := User{Name: "一撮灰", Score: 98.5, Created: time.Date(2026, 9, 9, 12, 0, 0, 0, time.UTC)} b, _ := json.Marshal(u) fmt.Println...
半透明物体渲染
需要预先学习: 着色与渲染管线 深度缓冲 帧缓冲 透明与混合 玻璃、水面、烟雾、粒子——真实感场景离不开半透明。不透明物体的遮挡由深度缓冲解决,谁近谁显示,与顺序无关(见 深度缓冲);半透明则不同:它的颜色要与背后已经画好的内容按比例叠加,而"按比例叠加"天然依赖顺序。这篇笔记讨论半透明渲染的所有关键问题:混合怎么做、顺序怎么排、什么时候可以不排序、以及不排序的精确方案(OIT)。 alpha 通道 颜色由 RGBA 四个分量组成,第四个分量 alpha 表示不透明程度,范围 [0,1][0, 1][0,1]: alpha 含义 1 完全不透明,正常覆盖背后内容 0 完全透明,不可见 0 ~ 1 半透明,与背后内容按比例混合 over 混合方程 半透明合成使用 over 操作符:源颜色(source,即将写入的片元)以自身 alpha 为权重,覆盖目标颜色(destination,颜色缓冲中已有的内容): Cresult=αs⋅Csrc+(1−αs)⋅CdstC_{result} = \alph...
函数
函数 函数是 C++ 程序中用于封装可执行逻辑的基本单元,将一段完成特定功能的代码组织为可复用的整体。定义函数的语法如下: 返回值类型 函数名(参数类型1 参数1, 参数类型2 参数2, ...) { // 函数体,执行具体的逻辑 return 返回值; } 注: 对于返回值类型为void的函数,可以不写return语句,或直接写return;提前终止。 函数的声明与定义 函数的声明和定义是两个不同的概念,遵循单一定义规则(ODR):一个程序中函数只能有一次定义,但可以有多次声明。 函数声明:也叫函数原型,仅告诉编译器函数的名称、返回类型和参数列表,不包含函数体。 函数定义:包含完整的函数体,是函数的具体实现。 int add(int a, int b); // 函数声明 int main() { int res = add(10, 2); // 在声明后即可调用函数 return 0; } int add(int a, int b) // 函数定义 { ...
光照相关逻辑
需要预先了解: IDRenderer的设计与实现 RenderPass与RenderGraph 光照相关逻辑 这篇笔记梳理 IDGameEngine 里所有和光照有关的逻辑:光源在 CPU 侧的封装与上传、前向与延迟两条渲染路径的组织,以及 Phong / Blinn-Phong / PBR 三种光照模型在 shader 里的实现。光照代码分布在前向的 geometry.fsl 和延迟的 deferred_lighting.fsl 两份 shader 里,两者公式逐段一致——任一路径的画面都可以作为另一条的验收基准。 光源的封装 引擎里一个光源就是一个 Light 结构体: enum class LightType : uint8_t { Directional = 0, // 平行光 Point, // 点光源 Spot, // 聚光灯 }; struct Light : public SerializableBase { Ligh...
测试
需要预先了解: 包与模块:代码组织 channel 标准库(二):JSON 与 HTTP 服务 测试 Go 把测试做成了语言工具链的一部分:_test.go 文件、go test 命令、testing 包,不需要额外框架。这一篇讲单元测试、表驱动测试、基准测试、模糊测试、示例测试和 HTTP 测试的写法,以及覆盖率怎么读。 测试文件的组织 测试代码放在以 _test.go 结尾的文件里,和被测试代码同一个目录: calc/ ├── calc.go // package calc ├── calc_test.go // package calc(同包测试) └── calc_ext_test.go // package calc_test(外部测试包) 两种包名的区别: 写法 能访问 适合 package calc 包内所有标识符(含未导出的) 测试内部实现 package calc_test 只有导出的标识符 测试对外 API,避免依赖...
函数参数的值传递与引用传递、指针传递的区别
需要预先了解: 函数 引用与左右值 指针 形参与实参 形式参数(Parameter) 形参是函数定义或函数声明时,在参数列表中声明的变量,是函数内部用来接收外部数据的占位符。 函数未被调用时,不占用实际内存 函数被调用时,创建并初始化 函数执行结束后立即销毁 实际参数(Argument) 实参是调用函数时传递给函数的具体数据。 // a、b是形参 int add(int a, int b) { return a + b; } int main() { int x = 10, y = 20; // x+y、x*y是实参 int res = add(x + y, x * y); return 0; } 值传递 形参是实参的一份独立拷贝,形参与实参占有不同的内存地址。 函数内对形参的修改都不会作用于外部的实参 如果参数的内存占用过大,频繁地拷贝会导致明显的性能问题 void swap(int x, int y) { int temp =...
阴影相关逻辑
需要预先了解: RenderPass与RenderGraph 光照相关逻辑 阴影相关逻辑 阴影是光照的"这道光被挡住了没"那一半。IDGameEngine 的阴影基于经典 Shadow Map(阴影贴图),在这之上实现了 Hard / PCF / PCSS 三档滤波和最多 4 层的 CSM 级联。这篇笔记按数据流梳理:CPU 侧的封装 → ShadowPass 的渲染流程 → GPU 侧 shader 的采样与滤波。 阴影贴图原理 Shadow Map 是两遍渲染: 深度遍:把相机换成光源,从光源位置看向场景,只把深度写进一张贴图——记录"沿这道光方向,最先碰到的东西有多远"; 着色遍:正常渲染时,把每个像素变换到光源视角,比较它的深度和贴图里的深度。比贴图近说明它就是挡光者(照亮),比贴图远说明它前面有东西(阴影)。 平行光的光源视角用正交投影(平行光没有透视衰减),这是后面 ShadowCamera 的核心工作。 CPU 侧的封装 ShadowConfig:参数配置 inline constexpr uin...
性能分析与调优
需要预先了解: 内存分配与逃逸分析 垃圾回收 测试 性能分析与调优 性能优化的第一条原则是先测量,再优化。凭直觉猜"哪里慢"十有八九猜错,而且容易把代码改得更复杂却没有任何收益。Go 工具链把测量能力做得很完整:testing 做基准、pprof 找热点、trace 看调度。 方法论 建立基线:用 testing.B 写基准测试,记录当前的 ns/op、B/op、allocs/op; 找到瓶颈:用 pprof 看 CPU/内存/阻塞的分布,而不是猜; 只改一个变量:一次改一处,改完重新测量; 验证收益:与基线对比,没有明显提升就回退; 防止退化:把关键基准保留在仓库里,配合 CI 定期跑。 优化的收益通常遵循帕累托分布:少数几处热点贡献了大部分开销。先解决它们,再考虑细节。 采集 profile 文件式采集(runtime/pprof) 在程序里显式启动: f, _ := os.Create("cpu.prof") pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() ...
PostProcess相关逻辑
需要预先了解: RenderPass与RenderGraph IDRenderer的设计与实现 PostProcess相关逻辑 光照输出的是线性 HDR(High Dynamic Range,高动态范围)颜色——亮度可以超过 1.0,这是 Bloom 效果的前提。但显示器是 LDR 的 sRGB 设备,直接显示 HDR 数据会整体发灰发白。从"线性 HDR"到"可显示"之间隔着一整条后处理链:Bloom 提取辉光、ToneMapping 压缩动态范围、Gamma 校正转回 sRGB。这篇笔记梳理 IDGameEngine 的 PostProcessPass 如何组织这些效果。 HDR 渲染管线 先把整条链路铺开,后处理处于末端: ForwardPass / LightingPass PostProcessPass 窗口 ┌──────────────┐ 采样 ┌──────────────────┐ blit ┌─────────┐ │ sc...





