|
| 1 | +# 01-概念:GPU、驱动、上下文、渲染管线 |
| 2 | + |
| 3 | +目录页:见 [OpenGL 教程目录](00-目录.md) |
| 4 | + |
| 5 | +## 目标 |
| 6 | + |
| 7 | +- 建立 OpenGL 学习的“地图”:你在操作什么、谁在执行、哪里会出错 |
| 8 | +- 搞清楚“为什么我明明写了 glXXX 却什么也没显示”的常见原因分层 |
| 9 | +- 为后续搭窗口与画三角形准备一套稳定的术语与直觉 |
| 10 | + |
| 11 | +## 先决条件 |
| 12 | + |
| 13 | +- 会写并编译运行一个最小 C++ 程序 |
| 14 | +- 了解 CMake 的最基础用法(能生成并构建一个可执行程序) |
| 15 | + |
| 16 | +--- |
| 17 | + |
| 18 | +## 1. OpenGL 不是一个库:它更像一套“协议” |
| 19 | + |
| 20 | +很多新手第一步就会卡在“我到底要装哪个 OpenGL”。关键点是: |
| 21 | + |
| 22 | +- **OpenGL API 头文件**:只是声明(告诉编译器函数长什么样) |
| 23 | +- **OpenGL 实现**:在显卡驱动里(Windows/Linux/macOS 的实现方式不同) |
| 24 | +- **你调用的 gl* 函数**:最终会进入驱动,驱动再把工作下发给 GPU 执行 |
| 25 | + |
| 26 | +这也解释了两个现象: |
| 27 | + |
| 28 | +- 你换一台机器/换一个驱动,行为可能会不一样(尤其是 debug/兼容性部分) |
| 29 | +- 没有“创建了窗口/上下文”,调用 gl* 往往要么失败要么啥也不发生 |
| 30 | + |
| 31 | +--- |
| 32 | + |
| 33 | +## 2. 你需要先拿到“上下文”(Context),才谈得上渲染 |
| 34 | + |
| 35 | +OpenGL 的大多数能力都绑定在一个上下文上。上下文里至少包含: |
| 36 | + |
| 37 | +- 你要使用的 OpenGL 版本与 Profile(比如 3.3 Core) |
| 38 | +- 大量 GPU 状态(深度测试开关、混合模式、绑定的 buffer/texture 等) |
| 39 | +- 与窗口系统相关的交换链(swap buffers)能力 |
| 40 | + |
| 41 | +关键结论: |
| 42 | + |
| 43 | +- OpenGL 上下文通常由“窗口/平台库”创建(比如 GLFW/SDL/Qt 等) |
| 44 | +- 没有当前上下文(current context),调用 gl* 不是“空操作”,而是**未定义/报错/崩溃**的起点 |
| 45 | + |
| 46 | +后面搭工程时,我们会用“窗口库 + loader”把它固定成可复用骨架。 |
| 47 | + |
| 48 | +--- |
| 49 | + |
| 50 | +## 3. 为什么需要 OpenGL Loader(GLAD/GLEW 等) |
| 51 | + |
| 52 | +现代 OpenGL 的大量函数在不同平台上不是直接链接得到的,而是需要: |
| 53 | + |
| 54 | +- 先创建上下文 |
| 55 | +- 再通过平台 API 获取函数指针 |
| 56 | + |
| 57 | +OpenGL Loader 做的事就是:在你有了上下文之后,帮你把 `glCreateShader`、`glGenVertexArrays` 这类函数指针加载出来。 |
| 58 | + |
| 59 | +这件事如果顺序错了,最经典症状是: |
| 60 | + |
| 61 | +- 程序启动正常,但一调用某些 gl* 就崩溃(空函数指针) |
| 62 | +- 或者所有扩展函数都“不可用” |
| 63 | + |
| 64 | +--- |
| 65 | + |
| 66 | +## 4. 渲染管线:从数据到像素的最短路径(现代 OpenGL 视角) |
| 67 | + |
| 68 | +把“画一个三角形”拆成最小闭环,你需要的只是下面这条链路: |
| 69 | + |
| 70 | +1. **CPU 侧准备顶点数据**(三个点的坐标) |
| 71 | +2. **把数据上传到 GPU**(Buffer:VBO) |
| 72 | +3. **告诉 GPU 怎么解释这段数据**(Vertex Attribute + VAO) |
| 73 | +4. **准备 Shader**(最少需要:顶点着色器 + 片元着色器) |
| 74 | +5. **发起绘制命令**(draw call) |
| 75 | +6. **交换前后缓冲**(你才能在窗口里看到结果) |
| 76 | + |
| 77 | +你会发现:这条链路里有两类东西贯穿始终: |
| 78 | + |
| 79 | +- **资源**:buffer、shader、texture……(需要创建、使用、释放) |
| 80 | +- **状态**:当前绑定了哪个 VAO/VBO、当前用哪个 program、是否启用深度测试…… |
| 81 | + |
| 82 | +后面每一章的“工程化”,本质就是把资源与状态管理变得可控、可调试、可扩展。 |
| 83 | + |
| 84 | +--- |
| 85 | + |
| 86 | +## 5. “黑屏”到底在黑什么:按层排查 |
| 87 | + |
| 88 | +OpenGL 初学的痛点几乎都能按层归类,这里先给一张排查地图,后面会逐步把每一层变成“可观测”: |
| 89 | + |
| 90 | +### 5.1 工程层(编译/链接/依赖) |
| 91 | + |
| 92 | +- 没链接窗口库 |
| 93 | +- loader 没加进工程或初始化没调用 |
| 94 | + |
| 95 | +### 5.2 上下文层(版本/Profile/创建顺序) |
| 96 | + |
| 97 | +- 你请求了 3.3 Core,但实际拿到的是别的版本/兼容模式 |
| 98 | +- 调用 loader 初始化前没创建上下文 |
| 99 | + |
| 100 | +### 5.3 Shader 层(编译失败但你没看到日志) |
| 101 | + |
| 102 | +- glsl 版本不匹配(`#version` 与上下文版本不一致) |
| 103 | +- 语法错误/变量没用导致被优化掉,输出不如预期 |
| 104 | + |
| 105 | +### 5.4 顶点输入层(数据/布局错) |
| 106 | + |
| 107 | +- stride/offset 写错,导致顶点坐标读乱 |
| 108 | +- VAO 没配好就 draw |
| 109 | + |
| 110 | +### 5.5 状态层(viewport/清屏/深度等) |
| 111 | + |
| 112 | +- 没设置 viewport 或 resize 后没更新 |
| 113 | +- 没清 color/depth buffer,产生残影或看似“没画” |
| 114 | + |
| 115 | +这套分层会贯穿全教程:每引入一个新概念,都要把它放回“可观测的排查层级”里。 |
| 116 | + |
| 117 | +--- |
| 118 | + |
| 119 | +## 小练习 |
| 120 | + |
| 121 | +### 练习 1:用自己的话复述“OpenGL 是什么” |
| 122 | + |
| 123 | +- 题目:用 3 句话解释 OpenGL、驱动、上下文之间的关系 |
| 124 | +- 目标:把“我要装 OpenGL 吗”这个误区从脑子里删掉 |
| 125 | + |
| 126 | +### 练习 2:写一份黑屏排查清单 |
| 127 | + |
| 128 | +- 题目:把第 5 节按层写成一份 checklist(每层 3 条) |
| 129 | +- 目标:训练“先证伪底层假设,再看上层逻辑”的调试习惯 |
| 130 | + |
| 131 | +--- |
| 132 | + |
| 133 | +## 小结 |
| 134 | + |
| 135 | +- OpenGL 更像一套协议:实现主要在驱动里,不是你直接链接的单个库 |
| 136 | +- 先有上下文,再加载函数指针,再谈渲染 |
| 137 | +- 现代 OpenGL 的最小闭环:数据 → buffer → attribute/VAO → shader → draw → swap |
| 138 | +- 黑屏要分层排查:工程 → 上下文 → shader → 顶点输入 → 状态 |
| 139 | + |
| 140 | +下一篇:见 [02-搭工程:CMake + 窗口 + OpenGL Loader + 调试输出](02-搭工程-CMake窗口上下文Loader调试输出.md) |
0 commit comments