免费建筑图纸下载网站,心理教育网站建设目的,网站地图,网站建设哪里比较好如果把现代渲染管线比喻成一条“图像加工厂”的流水线#xff0c;
那**顶点阶段#xff08;Vertex 阶段#xff09;**就是这家工厂的第一道关键工序#xff1a;把一堆散落在“自己小世界”#xff08;模型空间#xff09;里的三维点#xff0c;
一路变换、搬运到“摄像机…如果把现代渲染管线比喻成一条“图像加工厂”的流水线那**顶点阶段Vertex 阶段**就是这家工厂的第一道关键工序把一堆散落在“自己小世界”模型空间里的三维点一路变换、搬运到“摄像机能理解的世界”为后面画三角形、填颜色、打光、上屏幕打下基础。这篇我们就用特别大白话从零把顶点阶段讲透重点围绕顶点阶段在整个 GPU 流水线里处在什么位置、负责什么活一个顶点从“模型空间的小点点”到“准备上屏幕的点”到底经历了哪些变换那些常听到的“Model / View / Projection 矩阵”“MVP”“gl_Position”到底啥意思顶点着色器Vertex Shader里通常干哪些事怎么写、改什么实战里Unity / Unreal / OpenGL / DirectX你是怎么和这一阶段打交道的常见坑和容易混淆的概念有哪些你可以把这篇文章当成“3D 点是怎么被 GPU 一步一步塞到屏幕上的上篇顶点篇”。一、先搭个大框架现代渲染管线到底长啥样现代 GPU 渲染管线可以粗暴理解为一个“做饭流水线”CPU 准备好食材顶点数据位置、法线、UV、颜色、索引……GPU 接过食材开始流水线加工顶点阶段“把每个顶点从模型坐标一路变换到裁剪坐标/屏幕可用坐标。”图元装配 剪裁“用顶点拼三角形裁掉看不见的那部分。”光栅化“把三角形拆成一个个像素片元。”片元阶段“给每个像素算颜色、光照、阴影、纹理。”测试 混合“深度测试、透明混合、写入 Framebuffer。”其中顶点阶段是整个 GPU 真正“接触几何数据”的第一站。它只关心一件事从每个顶点的“原始三维位置”出发把它变换成后续能投影、裁剪、光栅化用的坐标。后面那些“填像素、打光、反射、后期”等都是在顶点阶段准备好的位置基础上干活的。二、顶点是什么先把“顶点数据”这件事讲清楚2.1 顶点 模型的最小“骨节”在 3D 模型里一个“面”不是连续的而是由无数三角形拼起来的三角形由 3 个点组成这些点就是顶点Vertex。每个顶点不是只有一个(x, y, z)它通常带很多“附加信息”比如位置position(x, y, z)法线normal(nx, ny, nz)用来打光切线tangent做法线贴图用纹理坐标UV(u, v)用来在表面贴图颜色color顶点颜色骨骼权重skin weights做骨骼动画时每个顶点被哪些骨骼、以多大权重影响等等这些东西组成一个“顶点结构体”在 CPU 端排好队丢给 GPU。2.2 顶点阶段主要管哪些数据在顶点阶段最关键的是其中一个字段位置position。所有变换的主角就是它“这个点在模型空间是(x,y,z)那它最终在屏幕上应该在哪儿”其他数据法线、UV、颜色……通常会跟着一起被搬运或简单变换然后被传给下一阶段插值给每个像素。三、顶点阶段的硬核任务位置变换的“221 步法”如果把一个顶点的“旅程”拆开看大致是这么几步模型空间Model / Local → 世界空间World → 摄像机空间View / Camera → 裁剪空间Clip → 归一化设备坐标NDC透视除法 → 屏幕空间Screen真正由顶点阶段负责的通常是前面这一大截模型空间 → 世界空间 → 摄像机空间 → 裁剪空间Clip最后那一步“除以 w 得到 NDC”是 GPU 自动做的不在 Shader 里写。你肯定经常在 Shader 里看到这样一串东西gl_Position Projection * View * Model * vec4(position, 1.0);这就是典型的顶点阶段核心一句话用 MVP 矩阵把模型空间的坐标变成裁剪空间的坐标。我们把这些步骤用大白话讲透。四、第一步模型空间 → 世界空间Model → World4.1 模型空间每个模型“自己家里的坐标”模型空间Local Space是美术做模型时用的坐标系顶点数据里的 position 通常就是这个空间里的坐标它只关心“模型自己长啥样”不管模型放到世界哪儿。比如一个 2×2×2 的立方体以中心为原点它的 8 个顶点在模型空间是(-1,-1,-1) ~ (1,1,1)4.2 世界空间所有东西共享的“大地图”世界空间World Space是整个场景统一的一套坐标角色、地形、灯光、摄像机全都在这一个坐标系下有位置。要把一个模型从自己的小世界搬到整个大世界里需要一个Model 矩阵也叫 ObjectToWorld。它包含缩放Scale放大多少倍旋转Rotation转向平移Translation放到世界的什么位置。4.3 公式Local → World对于每个顶点worldPos Model * localPos写成代码伪 GLSL / HLSLfloat4 localPos float4(v.position, 1.0); float4 worldPos mul(u_ModelMatrix, localPos);localPos 是模型空间的坐标worldPos 就是世界空间的坐标。五、第二步世界空间 → 摄像机空间World → View5.1 摄像机空间站在相机的眼睛里看世界摄像机空间View / Camera Space是以摄像机为原点的坐标系相机永远在 (0,0,0)前方轴、上方轴固定如前Z 或 -Z视引擎约定。从世界空间变到摄像机空间就是把世界“搬到相机身上”。5.2 View 矩阵把“世界”变成“相机视角”View 矩阵一般由相机的位置eye看向目标target上方向up组成一个 LookAt 矩阵。对每个顶点viewPos View * worldPos代码float4 viewPos mul(u_ViewMatrix, worldPos);viewPos 就是从相机的角度看到的这个点“在我前方多少、左/右多少、上/下多少”。六、第三步摄像机空间 → 裁剪空间View → Clip6.1 裁剪空间Clip Space投影前的“标准盒子”Projection投影矩阵把摄像机视野中的“视锥体”压成一个标准的“盒子”Clip Space以方便后续裁剪和归一化。投影有两种常见方式透视投影Perspective近大远小模拟人眼正交投影Orthographic不随距离缩放适合 UI 或某些策略视角。6.2 Projection 矩阵把 3D 视锥压成标准盒子对于每个点clipPos Projection * viewPos代码float4 clipPos mul(u_ProjectionMatrix, viewPos);得到的 clipPos 是一个四维向量(x, y, z, w)。注意这个 w 很关键。在透视投影下w 和原来的深度 z 有强相关后续 GPU 会自动做透视除法ndc clipPos / clipPos.w变成 [-1,1] 范围的 NDC。在顶点阶段你通常只需要把clipPos赋给 GPU 规定的输出变量OpenGLgl_Position clipPos;HLSL返回值的SV_Position。透视除法和后面的裁剪由 GPU 来接手。七、一句 MVP 总结局部 → 世界 → 相机 → 投影一次乘完上面三步可以合并成一行clipPos Projection * View * Model * localPos MVP * localPos这就是常说的MVP 矩阵MModel 矩阵Local → WorldVView 矩阵World → ViewPProjection 矩阵View → Clip典型顶点着色器伪代码uniform mat4 u_Model; uniform mat4 u_View; uniform mat4 u_Projection; in vec3 a_Position; void main() { vec4 localPos vec4(a_Position, 1.0); gl_Position u_Projection * u_View * u_Model * localPos; }或者直接传一个 MVP 矩阵uniform mat4 u_MVP; gl_Position u_MVP * vec4(a_Position, 1.0);到这里顶点的“位置变换”任务就完成了。八、顶点阶段除了变位置还会顺带干这些活顶点着色器不是只能动位置它还可以对其他顶点属性做处理常见有8.1 法线变换打光用法线在模型空间是(nx, ny, nz)要做光照计算时通常需要世界空间的法线或视空间法线。典型操作vec3 normalLocal a_Normal; // 一般用 3x3 的 (Model 的逆转置矩阵) 把法线转到世界空间 vec3 normalWorld normalize((u_NormalMatrix * vec4(normalLocal, 0.0)).xyz); // 或者用 ObjectToWorld 的 3x3 部分视具体需求和缩放情况而定然后把normalWorld通过out变量传给片元着色器。8.2 UV / 颜色 / 自定义数据的“原样转发”UV 通常不变换直接传下去即可out vec2 v_UV; v_UV a_UV;颜色也一样out vec4 v_Color; v_Color a_Color;片元阶段会根据光栅化插值给每个像素算一个“中间 UV / 颜色”。8.3 顶点动画在顶点阶段做形变有些特效会在顶点阶段直接对顶点坐标动手脚比如飘动的旗子 / 草叶根据时间和顶点位置做波动呼吸起伏的模型按某个轴做周期性缩放顶点 Morph 动画在两个模型形状之间插值。示例顶点在本地空间做简单 Y 方向波浪vec4 localPos vec4(a_Position, 1.0); localPos.y sin(u_Time localPos.x * 2.0) * 0.1; gl_Position u_MVP * localPos;这里的位置变换就不再是“死数据”而是动态动画的一部分。九、具体到引擎里Unity / Unreal / OpenGL / DirectX 怎么用这一套9.1 Unity内置矩阵 自己写顶点函数在 Unity 中unity_ObjectToWorldModel 矩阵unity_WorldToObject逆矩阵UNITY_MATRIX_VView 矩阵UNITY_MATRIX_PProjection 矩阵UNITY_MATRIX_MVPMVP 矩阵。顶点 Shader 大概长这样HLSL 语法struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float2 uv : TEXCOORD0; }; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; }; v2f vert (appdata v) { v2f o; o.pos mul(UNITY_MATRIX_MVP, v.vertex); o.uv v.uv; return o; }Unity 帮你自动把这些矩阵传给 Shader你主要决定如何从输入顶点算出SV_POSITION和各种插值数据。9.2 Unreal引擎帮你做好 90%你改剩下那一点Unreal 的材质系统大部分情况下你看不到“低层 MVP”引擎内部会自动用LocalToWorld;ViewProjection等矩阵。不过你写 HLSL / 自定义 Shader 时逻辑类似float4 worldPos mul(LocalToWorld, float4(localPos, 1)); float4 clipPos mul(ViewProjection, worldPos); return clipPos; // SV_Position9.3 OpenGL / DirectX你自己传矩阵 自己写 VSOpenGL 的标准顶点 Shader#version 330 core layout(location 0) in vec3 aPos; uniform mat4 u_Model; uniform mat4 u_View; uniform mat4 u_Projection; void main() { gl_Position u_Projection * u_View * u_Model * vec4(aPos, 1.0); }DirectX / HLSL 类似只是布局和语义不同。十、顶点阶段常见坑 容易搞晕的点10.1 矩阵乘法顺序搞反P * V * M * v和M * V * P * v不是一回事。绝大多数图形 API 约定的是右乘向量所以顺序是clipPos P * V * M * localPos也就是先 M再 V再 P。如果搞成M * V * P * v画面立刻飞天。10.2 忘记给顶点坐标第四分量用 1.0vec4(a_Position, 1.0)的1.0代表“这是个点而不是方向”。点w1受平移影响方向w0不受平移影响比如法线、方向向量。如果你不小心对一个 w0 的向量乘 Model 矩阵平移就对它无效变换结果会错。10.3 混淆“世界空间”、“视空间”、“裁剪空间”中的坐标在顶点 / 片元 Shader 里你可能同时存在worldPosviewPosclipPosndcPos隐式screenPos搞清楚自己每个变量是哪个空间的才能用对它比如做光照时用 worldPos 或 viewPos做屏幕效果时用 ndc / screen。十一、把整个故事压缩成一条“顶点阶段流水线”最后我们再用一条“顶点的一生”输出来帮你彻底记住顶点阶段在干嘛。一个模型顶点最初是这样的localPos (x, y, z) // 美术建模时的坐标 normal (nx, ny, nz) uv (u, v) ...在顶点阶段它经历Local → WorldworldPos Model * vec4(localPos, 1)World → ViewviewPos View * worldPosView → ClipclipPos Projection * viewPos输出给 GPUgl_Position clipPos; // or SV_Position clipPos // 此时 clipPos (x,y,z,w)GPU 自动处理透视除法ndc clipPos / clipPos.w得到 [-1,1] 的标准坐标裁剪丢弃视野外的光栅化用这些顶点去拼三角形算每个像素的位置 / 插值属性。与此同时法线、UV、颜色等在顶点着色器中根据需要被简单变换或原样转发交给片元阶段用来打光、采样纹理。如果用一句话形容顶点阶段它就是把“几何点”顶点从“模型自己的小世界”搬运、旋转、缩放到整个“摄像机要看的大世界”产出的就是 GPU 能理解的“标准位置坐标”后面所有画面效果都站在它算好的位置上继续往下做。你以后在 Shader 里写的那句gl_Position u_Projection * u_View * u_Model * vec4(a_Position, 1.0);就可以在脑子里翻译成“好现在我把这个顶点从模型小窝里拎出来放到世界舞台上再搬到相机眼前最后塞进 GPU 的标准盒子里交给后面的流水线去画画。”