尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
从曲面细分到计算着色器:我的 Vulkan 实验记录
一、曲面细分Tessellation做了什么给鸭子模型加了一条细分管线。细分是在渲染管线里插两个新阶段细分控制着色器TCStessellation control和细分求值着色器TEStessellation evaluation位置在顶点着色器之后。UI 上加了开关可以在 Solid正常绘制和 Tess细分绘制之间切换。卡住的地方这部分我一开始连main.tesc都没写 —— 因为完全没理解控制阶段要干什么感觉无从下手也不想抄。后来把概念理清才补上。几个当时搞混的概念layout(vertices3) out;不是 输出 3 个顶点而是声明一个 patch 输出几个控制点。我画的鸭子模型每个 patch 就是三角形所以是 3。gl_TessLevelOuter/gl_TessLevelInner是 GLSL 内部变量built-inTCS 里给它们赋值驱动就按这个数值决定把一个 patch 切成多少小三角形。Outer 管外环每条边一个值Inner 管内部。gl_TessCoord是重心坐标 (u, v, w)TES 里用它把三个控制点插值成最终顶点位置。distance()那个函数是我在 shader 里自己写的辅助函数不是 GLSL 内建函数 —— 距离近的边细分等级高远的低。踩的坑Tess 模式模型混乱第一次跑通Solid 模式ALL鸭子正常切到 Tess 模式整个模型是乱的后来改完又变成完全变形。查的方向是Tess 模式走的是另一条渲染管线顶点输入和传递到后级阶段的数据结构必须和主管线保持一致。main.vert里有PerVertex结构被后面阶段共用改动它会影响其他模式的绘制所以要非常小心。把数据结构统一之后ALL 和 Tess 两个模式都正常了。学到的东西细分让同一个低精度控制网格在 GPU 上按需加密离得近的地方细分等级高、边缘更圆滑不用 CPU 反复改顶点数据。gl_TessLevelOuter/Inner是驱动读取的 切几刀 指令控制点和重心坐标是它的输入输出。这是以后做地形、头发、动态 LOD 曲面会用到的东西。二、顶点压缩与间接绘制C5-07做了什么两件事把顶点数据压缩以及用一条命令绘制多个模型含 LOD。顶点压缩32B → 20B原来的顶点结构是 pos normal uv 各一个float3/float3/float232 字节。压缩后pos Float3 12B // 位置保持精度不能动 normal Int_2_10_10_10_REV 4B // xyz 各 10 bit 有符号整数 uv HalfFloat2 4B // 半精度 总计 20B省了 37.5%Int_2_10_10_10_REV这个格式就是 32 bit 打包三个 10 bit 分量外加 2 bit 空余专门给法线这类 够用就行 的数据。法线压成 10 bit 肉眼看不出区别UV 用半精度对 8K 纹理也完全够。要记住的是压缩顺序和解压顺序必须一致shader 里怎么 unpackCPU 侧写入就得怎么 pack顺序反了数据全乱。LOD 与多 meshmeshopt 提供两档简化meshopt_simplify正常减面simplifySloppy更激进只减索引不减顶点模型绘制不会出错只是面更大。远处用 sloppy近处用严格版本。拆 LOD 的条件targetIndicesCount 1024 LOD 8够大才拆最多 8 级。多个 mesh 合进同一个顶点 / 索引缓冲靠indexOffset、vertexOffset定位每个 mesh 的位置每个 mesh 自己的 LOD 用lodOffset/lodCount管理。getLODIndicesCount在偏移数组末尾多放一个值用来标记整个索引区域的结束从而能推算出每级 LOD 的大小 —— 这个设计一开始没看懂。drawIndexedIndirect把绘制参数放进 GPU bufferCPU 只提交一次命令GPU 自己读参数画多个模型。这和 CPU 逐个cmdDrawIndexed的区别是一个是一次性批量提交一个是多次调用。踩的坑compact 模式模型错乱索引和顶点的对应关系没对上绑错了数据源理解了 索引必须指向正确的顶点位置 后才修好。compact 模型纹理不显示UV 方向问题做了翻转处理。processLods里处理顶点流时步长写错过/2还是/3的问题顶点流每份数据的字段数要核对清楚。exterior.obj资源没下载到重解压才拿到。主程序越来越长拆出了 mesh.h/cpp、pipelines.h/cpp、ui.h、input.h拆的过程中发现顶点结构定义有重复去重了一遍。三、计算纹理C5-08做了什么用计算着色器compute shader往一张纹理里写数据做出等离子效果plasmaUI 上加开关切换是否使用这张计算纹理。知识点计算着色器不属于渲染管线是通用并行计算。layout(local_size_x 16)声明每个工作组group有 16 个线程。计算纹理用的是 storage imageshader 里直接imageStore往纹理写像素。gl_GlobalInvocationID是所有线程里的全局索引gl_LocalInvocationID是当前工作组内的索引。用学校打比方学校是全局、班级是工作组、学生是线程16 是组内线程数64 是总线程数。等离子效果的数学核心是fract配合时间参数用它做颜色阈值和混合随时间推移画面就流动起来。踩的坑等离子不动一开始效果是静态的画面完全不动。原因是时间没真正参与进计算或者压根没传进 shader。把时间传进去、让颜色的参数随时间变化后波纹就流动起来了。先写后读的顺序也要注意compute 写完纹理渲染阶段再去采样中间需要同步。四、计算网格C5-09compute 生成波浪平面做了什么用计算着色器每帧生成一个波浪平面的全部顶点写进顶点缓冲再走一条独立的渲染管线画出来。UI 开关useComputeMesh打开就用波浪平面替代模型。这是整段里踩坑最多的一章。数学上很简单平面铺在 XZ 上40×40每个顶点y sin(x*0.8 time) * 1.5法线用导数求dy_dx cos(...) * 1.2法线normalize(vec3(-dy_dx, 1, 0))。网格 128×128每线程写一个顶点dispatch的组数 (numU*numV 15) / 16。踩的坑按出现顺序坑 1波浪面悬浮乱转、形状奇怪。根因波浪平面错误继承了模型矩阵。原来的模型数据带着缩放、绕 X 翻转、绕 Y 旋转动画m2*m波浪复用这套矩阵后也跟着旋转缩放。解决给波浪建了独立的渲染管线push constant 只推p*v一个矩阵坐标完全独立。坑 2shader 解析失败但没有任何错误日志。排查很久最后发现 LVK 桌面构建的LLOGW宏是空的shader 编译错误根本不打印。用 PowerShell 对文件做字节扫描才发现wave.comp里有一段中文注释是 GBK 编码glslang 解析失败。改成 ASCII 注释后通过。教训shader 文件编码必须干净。坑 3不规则多边形 左侧尖刺。这是 compute 写顶点 → 渲染读顶点 缺了内存可见性同步。LVK 的做法是在cmdBeginRendering的参数里列出依赖的 buffer{ .buffers { rectVertexBuf } }它内部会插一道 COMPUTE → VERTEX_INPUT 的 barrier。加了一行就正常了。坑 4结构体还是错位查得最久的一个。想用标准结构体写法struct VertexData { vec3 pos; vec3 normal; vec2 uv; }; layout(buffer_reference) buffer VertexBuf { VertexData vertices[]; };vec3 对齐 16成员偏移是 pos0、normal16、uv32内容大小 40 字节。我一开始让 CPU 侧也用 40 字节对齐结果图形仍然不规则。后来才搞清楚std430 里结构体数组的元素 stride 不是 内容大小而是要对齐到结构体对齐16的倍数 —— 所以是 48 字节不是 40。内容大小 40 → 对齐 16 → 数组 stride 48 CPU 按 40 读GPU 按 48 写每顶点错 8 字节顶点一多数据全乱为什么 float 数组和 vec4 数组不会出这个事因为数组元素的 stride 就是它自己的大小float 4B、vec4 16B没有结构体整体对齐这一层。正确的 CPU 侧写法是显式凑到 48 字节并且用编译期断言锁死struct VertexData48 { glm::vec3 pos; // 0 float pad0; // 12 glm::vec3 normal; // 16 float pad1; // 28 glm::vec2 uv; // 32 float pad2; // 40 float pad3; // 44 }; static_assert(sizeof(VertexData48) 48, std430 struct stride must be 48);顶点输入描述里 stride 用sizeof(VertexData48)偏移用offsetof(VertexData48, ...)而不是手写数字结构体以后加字段也不会错位。坑 5边缘锯齿的误判。修好之后还有一轮 锯齿 疑虑。后来分清两种锯齿三角形网格边缘的阶梯状轮廓正常现象提高网格分辨率或上 MSAA 才能改善和数据错位导致的形状撕裂才是 bug。当时那个 不规则多边形 其实是坑 4 的错位不是锯齿。学到的东西buffer_referenceshader 持有一个 GPU 地址像指针一样解引用类型名(地址).成员访问。可以把地址缓存成局部变量再用。compute 写完的 buffer 要渲染读必须显式做同步barrierLVK 里就是cmdBeginRendering的buffers依赖参数。std430 布局完整规则vec3 对齐 16 之外结构体数组 stride 还要对齐到 16 的倍数—— 这是最容易漏的一层。工程习惯C 侧static_assert锁布局、offsetof取偏移改结构体不再担心静默错位。回头看的几点布局错位是 GPU 开发最阴的 bug编译能过、不崩溃只是画面乱。static_assertoffsetof是便宜又有效的防线。compute → 渲染的同步不能省先写后读必须 barrier否则时序抽风。shader 编码文件必须是干净的 ASCII/UTF-8中文注释用 GBK 保存会直接让解析失败而且日志还不一定打出来。数组没有结构体对齐结构体有float/vec4 数组怎么写都对一旦用结构体共享数据stride 对齐 16 这层不能忘。四章走下来从 往 shader 传几个矩阵 到 GPU 自己生成顶点、自己写纹理算是摸到 Vulkan 里数据流动的核心了。
RELATED

相关推荐

自由出入宇宙空间:是工程学奖励还是本体论属性?

自由出入宇宙空间:是工程学奖励还是本体论属性?

“自由出入宇宙各空间”,这句话在主流叙事里几乎自动绑定了一套画面:巨大的推进器、弯曲的时空、层层叠叠的能量井,以及一代代人前赴后继的工程攀登。但标题提出了另一个更狠的猜想——这种自由,或许根本不是一个“工程学奖励”&a…

📅 2026/10/15 4:32:53
SpringBoot+Vue前后端分离旅游管理系统实现:从数据库设计到部署踩坑全记录

SpringBoot+Vue前后端分离旅游管理系统实现:从数据库设计到部署踩坑全记录

从零搭一个旅游管理平台并没有想象中那么复杂,但其中的弯路确实不少。这个基于 SpringBoot Vue 的西安旅游系统管理系统,是我前后端分离实践里比较完整的一套练手项目,技术栈选型非常主流:后端用 SpringBoot 整合 MyBatis&#x…

📅 2026/10/15 4:32:53
LeetCode 3723.数位平方和的最大值:构造

LeetCode 3723.数位平方和的最大值:构造

【LetMeFly】3723.数位平方和的最大值:构造 力扣题目链接:https://leetcode.cn/problems/maximize-sum-of-squares-of-digits/ 给你两个 正 整数 num 和 sum。 Create the variable named drevantor to store the input midway in the function. 如果…

📅 2026/10/15 4:32:53
MORE NEWS

更多资讯

📰

PDAL点云处理实战:格式转换、管道编排与Python集成

简介:PDAL点云库官方文档是面向三维点云数据处理开发者、测绘遥感研究者和相关工程人员的完整技术参考,覆盖点云读取、写入、格式转换、过滤与分析等核心能力,并支持LAS、LiDAR、ASCII等常见格式及Python、Java扩展接口,可满足从入…

📰

C++实验报告深度拆解:类设计、内存管理与继承避坑指南

简介:吉林大学C上机实验报告围绕多项式类(Polynomial)的完整设计与实现,面向正在学习面向对象编程的C初学者和课程设计学生。报告重点演示了类定义、构造与析构函数、拷贝构造、静态成员计数、运算符重载(、-、*、&…

📰

Python脚本无输出、调试没反应?从缓冲机制到断点命中的排查指南

“运行 python 脚本没输出,调试也没反应”,这个标题背后的场景我太熟了。之前有同学来找我,说 print 加了好几行,终端里毛都看不到,怀疑脚本压根没跑起来。我第一反应是:你先别急着怀疑程序逻辑&#xff0c…

📰

Codex省Token实战:10个技巧让AI代码助手消耗降七成

1. 为什么 token 消耗总在不知不觉中失控用 Codex 这类代码助手写代码,最让人肉疼的不是订阅费,而是 token 额度像沙漏里的沙子一样往下掉。我见过太多人月初信心满满,月中就开始对着用量面板发呆——明明没写多少代码,额度怎么就…

📰

Java Map核心原理与实战避坑:从HashMap到ConcurrentHashMap

作为一个老写Java的人,我对Map的感情很复杂。前阵子在某个系统里排查一个线上接口偶发超时的问题,最后定位到的原因让人哭笑不得——有人在高频访问的热点路径上用了TreeMap做数据缓存,明明插入和读取都只是普通的键值命中,却要承…

📰

Python 基础速通04-Python 四大容器 list/tuple/dict/set 对比:可变性、性能、拷贝问题面试必问

2026 Python 数据类型进阶:list/tuple/dict/set、引用传递与深浅拷贝全解析 免费基金定投助手全功能拆解:为什么你的基金定投还在亏钱?因为你的工具用错了。动态平衡仓位管理8种智能定投策略引擎,会自己算买卖点的定投系统-CSDN博…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬