TOC代码仓库入口github源码地址。gitee源码地址。系列文章规划OpenGL渲染与几何内核那点事-项目实践理论补充(一-1-(8)-番外篇当你的 CAD 遇上“活”的零件)OpenGL渲染与几何内核那点事-项目实践理论补充(一-2-(1)-当你的CAD想“联网”时从单机绘图到多人实时协作)OpenGL渲染与几何内核那点事-项目实践理论补充(一-2-(2)-当你的CAD需要处理“百万个螺栓”时从内存爆炸到丝般顺滑)OpenGL渲染与几何内核那点事-项目实践理论补充(一-3-(1)你的 CAD 终于能联网协作了但渲染的“内功心法”到底是什么)OpenGL渲染与几何内核那点事-项目实践理论补充(一-3-(2)当你的CAD学会“偷懒”从“一笔一画”到“一键生成”的OpenGL渲染进化史)OpenGL渲染与几何内核那点事-项目实践理论补充(一-3-(3)GPU 着色器进化史从傻瓜相机到 AI 画师你的显卡里藏着一场战争)OpenGL渲染与几何内核那点事-项目实践理论补充(一-3-(7)从“显卡不听话”到“GPU秒懂你”一个CAD老兵的着色器驯服史))OpenGL渲染与几何内核那点事-项目实践理论补充(一-3-(6)从“搬砖”到“无人仓”一个CAD极客的OpenGL性能压榨史连AI都看呆了——给图形学新手的VBO/VAO全攻略)OpenGL渲染与几何内核那点事-项目实践理论补充(一-3-(8)给CAD装上一双“看得懂世界”的眼睛从画个三角到百万模型丝滑渲染的十年进化血泪史)巨人的肩膀deepseekgemini那些年我们为了“画快一点”而填的坑你在上一篇文章里聊到了 AI 和图形学的融合说到 OpenGL 会成为 AI 的“翻译官”。但有一个问题你一直没展开讲为什么 OpenGL 的渲染管线长成现在这个样子你公司新来的实习生小 A第一次打开你写的shader.glsl文件看到满屏的layout(location0)、in、out、uniform一脸懵地问你“哥为什么我们不能直接写glColor3f(1,0,0)画一个红点为什么要搞这么复杂的着色器”你笑了。这个问题得从 1992 年那个只有固定管线的“上古时代”说起。第一阶段固定函数管线 —— 被“焊死”的开关你让小 A 想象一个场景1992 年你买了一台顶配 486 电脑装了第一版 OpenGL 1.0。你想在屏幕上画一个会转的红色立方体。你翻开手册看到的是这样的代码glEnable(GL_LIGHTING);// 打开光照glEnable(GL_LIGHT0);// 打开第0号灯glLightfv(GL_LIGHT0,GL_POSITION,lightPos);// 设置灯的位置glMaterialfv(GL_FRONT,GL_DIFFUSE,redMaterial);// 设置材质为红色glutSolidCube(1.0);// 画一个立方体你发现你能做的事情非常有限想换一种光照模型对不起只有Gouraud 着色基于顶点计算颜色然后插值和Phong 着色基于像素计算光照而且 Phong 着色当时还没硬件支持卡得要死。想让物体表面有凹凸感那时候连“法线贴图”这个词都没发明因为没有片元着色器让你改法线。想实现卡通渲染你只能把光照关了用glColor3f硬画。为什么设计成这样因为当年的显卡比如 3dfx Voodoo本质上是一个固定功能的硬件状态机。它的电路是“焊死”的顶点变换用硬件 TL 单元光栅化用硬件扫描线单元纹理采样用硬件双线性插值单元。你只能通过glEnable和glDisable去“拨动开关”不能自己写代码控制每一个像素。这个阶段的痛点开发者的创造力被硬件完全限制。你只能使用显卡厂商“施舍”给你的功能。如果你想要一种新的视觉效果唯一的办法是——等下一代显卡发布。深度扩展固定函数管线的硬件本质与 API 设计1. 固定函数管线的硬件架构顶点变换单元 (TL)专用的矩阵乘法器执行position MVP * vertex。这是 1999 年 GeForce 256 才首次硬件化的功能之前全靠 CPU 算。纹理采样单元 (TMU)硬件双线性/三线性插值器每个时钟周期可以取一个纹素。光栅化器 (ROP)把三角形转成像素执行深度测试、模板测试、混合。光照单元硬件实现的固定光照方程环境光 漫反射 镜面反射只支持方向光、点光源、聚光灯三种类型。2. OpenGL 1.x 的状态机模型整个 OpenGL 上下文是一个巨大的全局状态机。glEnable(GL_LIGHTING)实际上是在修改一个硬件寄存器。状态泄漏是常见 Bug你在这个函数里改了颜色下个函数画的物体也会变成这个颜色因为状态是全局的。性能优化的核心是减少状态切换把所有红色物体一次性画完再切换到蓝色。这就是“批处理渲染”的雏形。3. 扩展机制 (GL_EXT / GL_ARB)因为硬件演进速度远超 OpenGL 标准更新速度厂商开始通过“扩展”提前暴露新功能。例如GL_ARB_multitexture允许同时使用多张纹理这是后来“纹理混合”和“光照贴图”的基础。开发者必须写这样的代码if (GLEW_ARB_multitexture) { glActiveTextureARB(...); } else { /* 降级方案 */ }4. 固定管线的“终极形态”——OpenGL 2.12006 年发布的 OpenGL 2.1 是固定管线的最后辉煌包含了所有你能想象到的“开关”雾效、剪裁平面、纹理矩阵、颜色材质、光照模型参数……但它的 API 已经臃肿到近 300 个函数却依然无法实现一个简单的“卡通渲染”。开发者怨声载道。第二阶段基础可编程管线 —— 把“开关”换成“代码”2004 年OpenGL 2.0 发布带来了一个革命性的变化GLSL (OpenGL Shading Language)。你现在可以写这样的代码了顶点着色器 (Vertex Shader)#version 120 attribute vec3 position; attribute vec3 normal; varying vec3 fragNormal; void main() { gl_Position gl_ModelViewProjectionMatrix * vec4(position, 1.0); fragNormal normalize(gl_NormalMatrix * normal); }片元着色器 (Fragment Shader)#version 120 varying vec3 fragNormal; void main() { vec3 lightDir normalize(vec3(1.0, 1.0, 0.0)); float diff max(dot(fragNormal, lightDir), 0.0); gl_FragColor vec4(diff, diff, diff, 1.0); }你终于可以自己控制每一个顶点的位置每一个像素的颜色了你兴奋地实现了法线贴图、卡通渲染、边缘光……这些以前想都不敢想的效果。但很快你又发现了新问题几何体太僵硬你想做一片随风摆动的草地。如果用 CPU 计算每一根草的顶点位置然后每一帧都重新上传到 GPU总线带宽瞬间爆炸——一帧 60 次 × 10 万根草 × 每个顶点 12 字节 每秒 72MB 的数据传输。这在 2004 年的 AGP 总线上是不可能的。数据冗余你想做一个粒子系统有 10 万个粒子。每个粒子只是位置不同但形状一样都是一个四边形。你必须在 CPU 端生成 10 万个四边形的顶点数据然后全部传给 GPU。这太蠢了——明明 GPU 有几百个核心为什么不能让它自己“复制”四边形于是第三个“填坑者”来了几何着色器 (Geometry Shader)。深度扩展基础可编程管线的核心机制与性能陷阱1. 顶点着色器的“输入输出契约”attribute每个顶点独有的数据位置、法线、UV。从 CPU 的 VBO 中读取。uniform所有顶点共享的数据变换矩阵、光照参数。在整个 DrawCall 期间不变。varying从顶点着色器输出传递给片元着色器。关键点它会被硬件光栅化器自动插值。2. 光栅化器的插值原理透视校正插值因为 3D 投影是非线性的直接在屏幕空间线性插值会导致纹理扭曲比如地板上格子的纹理看起来歪歪扭扭。硬件光栅化器会在世界空间做插值然后除以深度值得到正确的结果。插值公式对于三角形三个顶点的属性f0, f1, f2屏幕空间某点的属性为(f0/w0 f1/w1 f2/w2) / (1/w0 1/w1 1/w2)其中w是顶点的齐次坐标分量与深度成反比。性能代价每一个varying变量都要经过这个插值计算。如果你声明了 8 个varying变量硬件就要为每个像素做 8 次插值。这也是为什么移动端 GPU 对varying数量极其敏感。3. 早期片元测试 (Early Fragment Test)为了性能GPU 会在执行片元着色器之前先做深度测试和模板测试。如果片元被遮挡就直接丢弃不跑昂贵的着色器代码。但有一个例外如果你的片元着色器里修改了gl_FragDepthGPU 就无法提前做深度测试必须等你的代码跑完才知道最终深度。这会导致性能下降应尽量避免。4. 纹理采样与 Mipmaptexture2D(sampler, uv)并不是直接取一个纹素。GPU 会根据 uv 的导数ddx,ddy自动计算应该用哪一级 Mipmap。三线性插值在两个 Mipmap 级别之间再做一次线性插值避免 Mipmap 切换时的明显接缝。这些操作全部由纹理单元硬件完成几乎不消耗额外时间。各向异性过滤当纹理以极斜的角度被观察时比如地板延伸到远方普通的 Mipmap 会导致过度模糊。各向异性过滤会沿着视线方向做多次采样硬件成本与采样数成正比如 16x 就是 16 次采样。5. 第一个性能瓶颈DrawCall在固定管线时代你画 1000 个物体就是 1000 次glDrawArrays。每一次 DrawCall 都有 CPU 到 GPU 的命令提交开销。可编程管线时代这个瓶颈依然存在。后来的解决方案是实例化渲染和间接绘制。第三阶段几何与细分着色器 —— GPU 学会“无中生有”几何着色器 (Geometry Shader)出现在 OpenGL 3.2 (2009 年)。它的位置在顶点着色器之后、光栅化之前。它可以接收一个图元点、线、三角形输出零个或多个图元。动态生成几何体。你终于可以实现那片草地了CPU 只上传 1 万个“草根位置”几何着色器负责把每个点扩展成一个四边形并根据风向弯曲。#version 150 layout(points) in; layout(triangle_strip, max_vertices4) out; void main() { vec4 pos gl_in[0].gl_Position; // 输出四个顶点形成一个四边形 gl_Position pos vec4(-0.1, 0.0, 0.0, 0.0); EmitVertex(); gl_Position pos vec4( 0.1, 0.0, 0.0, 0.0); EmitVertex(); gl_Position pos vec4(-0.1, 1.0, 0.0, 0.0); EmitVertex(); gl_Position pos vec4( 0.1, 1.0, 0.0, 0.0); EmitVertex(); EndPrimitive(); }你欣喜若狂但很快发现当草的密度增加到 100 万时帧率暴跌到个位数。几何着色器的致命弱点它的输出是“不规则”的。一个输入三角形可能输出 0 个、1 个或 100 个三角形。硬件无法预测输出量导致缓存效率极低。它是在图元级别串行执行的无法充分利用 GPU 的并行性。更重要的是几何着色器搞不定 LOD细节层次。你想实现一个功能当相机靠近一个球体时球体自动变精细当相机远离时球体自动变粗糙。用几何着色器做这件事意味着你必须在 GPU 里“细分”这个球体生成更多的三角形。几何着色器每输出一个三角形就要调用一次EmitVertex对于成千上万个三角形的细分来说性能简直是灾难。于是真正的“细分大师”——细分曲面着色器 (Tessellation Shaders)登场了OpenGL 4.02010 年。它专门为了解决“动态增加几何复杂度”而设计分为两个阶段细分控制着色器 (Tessellation Control Shader, TCS)决定“把这块曲面切成多少小块”。细分评估着色器 (Tessellation Evaluation Shader, TES)计算每一小块里新顶点的精确位置比如在贝塞尔曲面上插值。有了它你可以把一个只有几百个面的低模球体传给 GPU然后让 GPU 根据距离动态生成几万个面的高精度球体。CPU 和总线完全解放性能暴增。深度扩展几何着色器与细分曲面着色器的硬件实现与设计哲学1. 几何着色器的硬件限制输出缓冲区几何着色器的输出先存在一个片上 FIFO 缓冲区中。如果缓冲区满了比如你输出了大量顶点整个管线就会停顿。顶点顺序几何着色器输出的顶点顺序必须与输入保持一致比如三角形条带否则会破坏背面剔除和光栅化顺序。为什么几何着色器在移动 GPU 上性能极差移动 GPU 是 Tiled-Based Rendering瓦片渲染依赖提前知道每个瓦片有多少几何体。几何着色器的“不可预测输出量”打乱了调度导致频繁刷新瓦片缓冲区。替代方案在现代图形 APIVulkan、Metal中几何着色器已经被Mesh Shader取代后者通过协作线程组的方式更高效地生成几何体。2. 细分曲面着色器的硬件流水线细分图元生成器 (Tessellator)这是一个固定功能的硬件单元。它接收 TCS 输出的“细分因子”然后生成一个规则的网格三角形或四边形。这个单元是高度优化的可以每个时钟周期生成多个顶点。为什么 TCS 和 TES 是可编程的TCS 负责“策略”根据距离决定切多少刀TES 负责“位置”把生成的网格顶点映射到曲面上。硬件只负责“执行切分”这个机械劳动。典型应用地形渲染远处的瓦片细分因子低比如 1x1 个四边形近处的瓦片细分因子高比如 64x64 个四边形。Displacement Mapping置换贴图在 TES 中采样高度图真正改变顶点位置而不是像法线贴图那样“假装”有凹凸。平滑曲面把低模的控制点网格如 Catmull-Clark 细分曲面转换为光滑的极限曲面。3. 管线顺序顶点 → TCS → 细分图元生成器 → TES → 几何着色器 → 光栅化数据流顶点着色器输出的数据如位置、法线会原封不动地传递给 TCS。TCS 可以修改这些数据然后交给细分图元生成器。TES 接收细分后的重心坐标用它们插值出新的顶点属性。gl_TessLevelInner和gl_TessLevelOuter这两个内置变量在 TCS 中设置控制三角形或四边形边缘和内部的细分密度。值越大三角形越多。4. 性能考量过细分如果相机离得很远你依然把球体细分成 10 万个面那大部分三角形在屏幕上都小于一个像素白白浪费 GPU 算力。这就是为什么需要自适应细分根据屏幕空间大小动态调整因子。背面剔除在细分之前如果整个 Patch细分曲面的基本单元都在视锥体外或背面GPU 会直接跳过 TCS 和 TES节省大量计算。第四阶段计算着色器 —— 打破“图形”的枷锁你的 CAD 软件越做越大用户的需求也越来越“不务正业”。有一天一个搞仿真的客户问你“我能不能用你的 CAD 实时模拟 10 万个零件在振动盘里的运动就像那些物理引擎一样。”你第一反应是用 CPU 算10 万个零件每个零件要检测与周围零件的碰撞还要更新速度和位置一帧都算不完。用 GPU 算怎么算用片元着色器把数据伪装成纹理然后在上面跑物理公式你试了一下“伪 Compute Shader”的做法把每个零件的位置编码成纹理的 RGB 值比如 R位置 XG位置 YB位置 Z。画一个全屏的四边形让片元着色器读取这个纹理计算物理然后把结果写回另一张纹理。来回 Ping-Pong。这确实能跑但太痛苦了你必须把物理公式硬塞进片元着色器还要处理纹理坐标和像素对齐调试时根本不知道哪出了问题。直到 OpenGL 4.3 (2012 年)计算着色器 (Compute Shader) 横空出世。它彻底脱离了传统的“画三角形”流程。你可以直接这样写#version 430 layout(local_size_x 256) in; layout(std430, binding0) buffer ParticleBuffer { vec4 positions[]; // 位置 质量 vec4 velocities[]; // 速度 阻尼 }; void main() { uint idx gl_GlobalInvocationID.x; if (idx particleCount) return; // 直接读写缓冲区做物理计算 vec3 force computeForce(idx); velocities[idx].xyz force * deltaTime; positions[idx].xyz velocities[idx].xyz * deltaTime; }然后你只需要在 C 里调用glDispatchCompute(workGroupsX, 1, 1)GPU 的 2560 个核心就会并行处理这 10 万个粒子。你终于明白计算着色器的革命性意义它不分阶段没有顶点、片元的概念只有“线程组”和“缓冲区”。它可以直接读写任何缓冲区不管是 VBO、纹理还是自定义的 SSBOShader Storage Buffer Object。它可以做通用计算 (GPGPU)物理、AI 推理、图像处理、加密解密……只要你能并行的算法都能扔给它。你在你的 CAD 里用计算着色器实现了实时碰撞检测BVH 遍历点云的法线估计物理仿真预览甚至一个小型神经网络推理引擎用来做 AI 降噪深度扩展计算着色器的架构、同步与高级应用1. 计算空间与线程组织gl_GlobalInvocationID全局线程 ID在一维、二维或三维空间中唯一标识一个线程。gl_LocalInvocationID在线程组内的局部 ID。gl_WorkGroupID线程组 ID。local_size_x/y/z指定每个线程组包含多少个线程。总线程数 线程组数 × 局部大小。硬件调度每个 SM流式多处理器可以并发执行多个线程组。线程组内的线程可以通过共享内存 (shared)通信并通过barrier()同步。2. 内存模型与同步shared变量线程组内共享的低延迟内存通常在 L1 缓存中适合做并行规约比如求和。barrier()线程组内所有线程必须到达这个点才能继续。注意不能跨线程组同步不同线程组的执行顺序是不确定的。原子操作atomicAdd、atomicExchange等。可以在全局内存上实现线程安全的数据更新但有性能开销。Incoherent 访问计算着色器的内存访问默认是不保证一致性的。你需要显式调用memoryBarrier()或groupMemoryBarrier()来确保一个线程的写入对其他线程可见。3. SSBO (Shader Storage Buffer Object)std430布局类似 C 结构体的内存布局可以直接映射 GPU 内存到 CPU 指针通过glMapBuffer。原子操作支持可以对 SSBO 中的整型变量进行原子加、交换等。容量限制理论上可达 GPU 显存上限实际中单个 SSBO 通常限制在 2GB。与 Uniform Buffer 的区别Uniform Buffer 是只读的容量小通常 64KB适合存储常量SSBO 可读写容量大适合存储动态数据。4. 计算着色器 vs. 片元着色器做 GPGPU特性计算着色器片元着色器“伪 GPGPU”数据输入任意缓冲区必须伪装成纹理数据输出直接写缓冲区必须渲染到纹理同步barrier()shared无只能依赖 Render Pass 边界随机写入支持通过 SSBO不支持只能写入固定像素位置线程组织1D/2D/3D 灵活隐式的 2D 屏幕网格性能专为计算优化受限于光栅化器开销5. 高级应用GPU 驱动的渲染GPU 端视锥剔除计算着色器遍历场景 BVH把可见物体的 ID 写入一个“间接绘制缓冲区”。glMultiDrawElementsIndirect这个函数可以直接从 GPU 缓冲区读取绘制命令CPU 完全不需要知道画了什么。这是现代 AAA 游戏引擎如虚幻 5 的 Nanite的基础。粒子系统计算着色器更新粒子位置然后直接作为顶点缓冲区被顶点着色器使用全程数据不出 GPU。6. 调试计算着色器RenderDoc可以捕获计算着色器的调度查看输入输出缓冲区。NVIDIA Nsight Graphics支持对计算着色器进行逐线程调试查看shared内存内容。printf调试某些驱动支持在计算着色器里用printf需要扩展但会严重影响性能。第五阶段SPIR-V —— 终结“驱动翻译大战”你的 CAD 在 Windows 上跑得飞快但客户要求在 Linux 和 macOS 上也能用。你信心满满地把代码移植过去结果在 Nvidia Linux 驱动上GLSL 编译报错“layout(binding0)不能用于 UBO”。在 AMD Windows 驱动上同样的代码运行正常但性能只有 Nvidia 的三分之一。在 Intel 集显上直接黑屏。你崩溃了。为什么同样一份 GLSL 代码在不同显卡上表现天差地别根本原因每个显卡厂商的 OpenGL 驱动里都内置了自己的 GLSL 编译器。它们对标准的解读各有偏差优化策略也完全不同。更糟的是这些编译器是在游戏运行时才工作的——每次你加载一个着色器驱动就要现场把它翻译成显卡能执行的机器码。这个过程不仅慢还可能导致游戏启动时的“着色器编译卡顿”。终极解决方案SPIR-V (Standard Portable Intermediate Representation)它是由 Khronos GroupOpenGL 和 Vulkan 的制定者推出的中间二进制格式。它的工作流程是这样的开发阶段你用glslangValidator或glslc工具把 GLSL 源代码编译成.spv文件SPIR-V 二进制。运行阶段你的程序直接加载.spv文件调用glShaderBinary或 Vulkan 的vkCreateShaderModule传给驱动。驱动只需要做把 SPIR-V 二进制翻译成最终的 GPU 机器码。这带来了三个革命性的好处编译速度极快因为驱动不再需要做词法分析、语法分析、语义检查这些“前端”工作只需要做“后端”代码生成。游戏启动时的卡顿消失了。行为完全一致SPIR-V 的语义是严格定义的不存在“不同编译器解读不同”的问题。你在 Nvidia 上调试好的着色器在 AMD 和 Intel 上一定表现相同。多语言支持不仅 GLSL 可以编译成 SPIR-VHLSL通过 DXC和 Rust 的rust-gpu也可以。这意味着你可以用自己喜欢的语言写着色器最终都统一成 SPIR-V。你终于可以睡个好觉了你只需要在你的构建脚本里加一步glslc生成.spv文件然后和程序一起发布。跨平台、跨显卡的着色器噩梦终结于 SPIR-V。深度扩展SPIR-V 的技术细节与生态系统1. SPIR-V 的二进制结构魔数文件头以0x07230203开头SPIR-V 1.0 版本。指令流由 32 位字组成的线性序列。每条指令的前 16 位是操作码后 16 位是操作数数量。SSA 形式SPIR-V 采用静态单赋值形式每个虚拟寄存器只被赋值一次有利于驱动后端的优化。类型系统包含标量、向量、矩阵、数组、结构体、指针、图像、采样器等完整类型。2. 编译工具链glslangValidatorKhronos 官方 GLSL → SPIR-V 编译器支持所有 OpenGL 和 Vulkan 版本的 GLSL。glslcGoogle 开发的类 GCC 风格的编译器前端背后也是 glslang但命令行接口更友好。dxc微软的 HLSL 编译器从 Shader Model 6.0 开始支持输出 SPIR-V通过-spirv标志。spirv-cross一个强大的 SPIR-V 反编译工具可以把 SPIR-V 转回 GLSL、HLSL、MSLMetal Shading Language。这对于调试和跨平台开发极其有用。3. SPIR-V 的扩展机制类似 OpenGL 扩展SPIR-V 也支持扩展比如SPV_KHR_ray_tracing光线追踪、SPV_EXT_demote_to_helper_invocation更高效的片元丢弃。扩展在 SPIR-V 二进制中通过OpExtension指令声明。4. SPIR-V 与 VulkanVulkan 是唯一只接受 SPIR-V 作为着色器输入的图形 APIOpenGL 4.6 也支持但不是强制。这意味着在 Vulkan 中你必须在开发阶段生成 SPIR-V运行时不接受 GLSL 源码。Vulkan 的 Pipeline Cache 机制还可以把 SPIR-V 的编译结果缓存到磁盘下次启动直接加载机器码实现“零编译时间”。5. SPIR-V 的反射 (Reflection)在传统 OpenGL 中你通过glGetUniformLocation查询着色器里有什么 Uniform。在 Vulkan 中你必须提前知道这些信息才能创建管线布局。解决方案使用spirv-cross或SPIRV-Reflect库从 SPIR-V 二进制中提取所有的描述符集、绑定、Push Constant 信息自动生成 C 结构体。6. 未来WGSL 和 WebGPUWebGPU 使用的是WGSL (WebGPU Shading Language)一种类 Rust 语法的文本语言。但 WebGPU 的底层实现比如 Dawn 和 wgpu-native依然可以将 WGSL 编译成 SPIR-V再交给 Vulkan 后端。SPIR-V 依然是业界的“事实标准中间语言”。总结你现在站在哪里小 A 听完你这一通“历史课”恍然大悟“原来我写的这几行layout(location0) in vec3 pos;背后是三十年显卡架构和 API 设计的血泪史啊”你点点头指着你 CAD 项目里shaders/目录下的文件说phong.vert/.frag是第二阶段的可编程管线处理最基本的 3D 渲染。grass.geom是第三阶段的几何着色器用来生成草地虽然性能不佳但胜在简单。terrain.tesc/.tese是第三阶段的细分着色器用来动态生成地形细节。particle.comp是第四阶段的计算着色器用来做物理仿真。所有的.glsl文件在 CMake 构建时都会被glslc编译成.spv这是第五阶段的 SPIR-V。你告诉他“这就是为什么我们能在普通笔记本电脑上实时渲染一个包含几十万个零件的装配体还能同时做物理碰撞检测。因为我们站在了这五个阶段所有‘填坑者’的肩膀上。”而当你再回头看上一篇文章里提到的 AI 渲染、世界模型——你会发现计算着色器正是 AI 推理能在 GPU 上高效运行的基石SPIR-V则保证了你的 AI 模型在不同设备上的行为一致。OpenGL 渲染管线的演进史就是一部“不断把控制权从硬件厂商手里夺回交给开发者”的革命史。而你正是这场革命的受益者和参与者。如果想了解一些成像系统、图像、人眼、颜色等等的小知识快去看看视频吧 抖音数字图像哪些好玩的事咱就不照课本念轻轻松松谝闲传快手数字图像哪些好玩的事咱就不照课本念轻轻松松谝闲传B站数字图像哪些好玩的事咱就不照课本念轻轻松松谝闲传认准一个头像保你不迷路您要是也想站在文章开头的巨人的肩膀啦可以动动您发财的小指头然后把您的想要展现的名称和公开信息发我这些信息会跟随每篇文章屹立在文章的顶部哦