%%{init: {
"look": "classic",
"theme": "base",
"fontSize": 13,
"flowchart": {
"curve": "linear",
"nodeSpacing": 16,
"rankSpacing": 26,
"useMaxWidth": true },
"themeVariables": { "primaryColor": "#f0f6ff", "primaryBorderColor": "#3664a8" }
}}%%
flowchart LR
classDef importentNode fill:#e81224,stroke:#999,stroke-width:1px,color:#eff1f5;
IA[ < 输入装配 > <br/>固定功能,可配置,读出顶点数据输出有序的顶点流]
VS[顶点着色器<br/>可编程]
IA --> VS
subgraph CC[曲面细分,可选]
direction TD
HS[< 曲面细分控制器 > <br/>这块面要细分到多密 ,可编程]
TS[< 细分器 > <br/>自动切割机-生成点,固定功能]
DS[< 曲面细分评估着色器 > <br/>算出它真实的 3D 位置,可编程]
end
HS--> TS--> DS
GS[几何着色器<br/>可选,可编程]
VS --> CC --> GS
VS -.可捕获.-> SO[流式输出 / Transform Feedback<br/>可选,写入 Buffer]
GS -.可捕获.-> SO
subgraph R[光栅器功能]
direction TD
A3_1[<图元装配><br>顶点组成:点线,三角形]
A3_2[剪裁]:::importentNode
A3_3[<透视除法><br>也就是:xyz ÷ w 得到 NDC]
A3_4[<视口变换><br>将 NDC 坐标映射到屏幕]
A3_5{正反面判定/剔除}:::importentNode
end
A3_1-->A3_2-->A3_3-->A3_4-->A3_5
GS --> R
VS --> R
R --> Ra[光栅化]
图元装配_剪裁_透视除法与NDC坐标_视口变换_剔除
flowchart LR
classDef importentNode fill:#e81224,stroke:#999,stroke-width:1px,color:#eff1f5;
classDef note fill:#16c60c,stroke:#999,stroke-width:1px,color:#eff1f5;
subgraph A[剪裁与剔除]
direction LR
A1[图元装配]
A2[裁剪]:::importentNode
A3[透视除法]
A4[视口变换]
A5[正反面判定与剔除]:::importentNode
A6[/输出/]
end
A1-->A2-->A3-->A4-->A5-->A6
🔴剪裁
-
剪裁就是沿着视锥的边界,把三角形露在视野外的部分切掉,在边界上插入新顶点,只保留视野里的部分。
-
剪裁在 剪裁空间里干活,判断顶点着色器输出的剪裁空间的顶点坐标,是否在视锥内。
- 一个顶点在视锥内的判断规则:
- -w ≤ x ≤ w
- -w ≤ y ≤ w
- -w ≤ z ≤ w
- 正好对应视锥的 6 个面:左、右、上、下、近、远,共 6 个边界。
- 一个顶点在视锥内的判断规则:
-
剪裁输入输出
- 剪裁接收图元装配阶段的完整图元,每个顶点都带有顶点着色器输出的全部属性:顶点剪裁空间位置(x,y,z,w)、法线、颜色、UV等。
🔴正反面判定与剔除(这里一般说的是背面剔除)
- 封闭物体的背面,从摄像机方向永远看不见,那干脆就不画了。它可以手动切换剔除模式。
- 做三角形的正反面判定
- 对每一个三角形,用 NDC 空间的三个顶点,计算环绕顺序,判断是正面还是背面在 NDC 里看是逆时针(CCW)→ 正面,是顺时针(CW)→ 背面。
我们可以在 图形 api 中配置是逆时针顶点顺序决定三角形正面还是顺时针决定正面。
`IsFrontFaceCCW`:CCW = 逆时针为正面;CW = 顺时针为正面
- 剔除 指定的面
- 是 GPU 几何处理阶段的固定步骤
- 每个三角形都有正反面,由顶点顺序决定正反。
- 不渲染三角形的背面,比如一个封闭的物体,相机正常来讲都看不到里边的面。
- 他可以节省一半的性能。游戏引擎一般会默认开启
cocos 中的背面剔除
- 在cocos 材质属性面板中 可以更改pipeline state 中的cull mode 来决定使用什么 背面剔除模式
| 剔除模式 | 效果 | 使用场景 |
|---|---|---|
| BACK | 默认值,剔除背面,只渲染正面 | 绝大多数封闭的 3D 物体(立方体、角色、建筑等),性能最好 |
| FRONT | 剔除正面,只渲染背面 | 用来做 “从物体内部看” 的特殊效果、比如天空球 |
| NONE | 不剔除,正反面都渲染 | 薄物体(树叶、纸张、布料)、透明面片、需要双面显示的物体 |
[!error] cocos 中,阴影投射 pass2 : Shadow-caster 也默认开启了背面剔除模式。会导致单面不封闭模型投射出来的阴影缺失
🟢透视除法_NDC坐标_视口变换
裁剪空间:四维,边界随 w 改变
↓ xyz / w
NDC:三维,统一落在规范立方体内
↓ 视口变换
屏幕/窗口坐标:以像素计
flowchart LR
classDef importentNode fill:#f7630c,stroke:#999,stroke-width:1px,color:#eff1f5;
classDef note fill:#16c60c,stroke:#999,stroke-width:1px,color:#eff1f5;
subgraph A[透视除法_NDC坐标_视口变换]
direction LR
A1[< 裁剪空间 ><br>四维,边界随 w 改变]
A2[< NDC空间 ><br>三维,落在规范立方体内]
A3[< 屏幕空间 ><br>二维,以像素计算]
end
A1-->|透视除法|A2-->|视口变换|A3
透视除法
- 透视除法 = 把裁剪空间的 x、y、z 全都除以 w
- 除以 w 之后,远处物体坐标被压缩,自动实现近大远小的透视效果。也就是自动实现了透视相机的效果
- 如果想要正交相机的效果,w=1 除以 w 坐标不变就能实现
NDC 坐标空间
NDC 是一个正方形立方体(归一化盒子)。
在NDC空间中的所有可见顶点坐标都会被归一化: x ∈ [-1, 1],y ∈ [-1, 1],z ∈ [-1,1]
视口变换
- 下面是视口变换的公式
- screen_x=(x_{ndc}+1)\times0.5\times viewportWidth+viewportX
- screen_y=(y_{ndc}+1)\times0.5\times viewportHeight+viewportY
screenX = (ndc.x + 1) * 0.5 * viewportWidth + viewportX
screenY = (ndc.y + 1) * 0.5 * viewportHeight + viewportY
+ viewportX 是为了把“视口内部坐标”平移到“整个渲染目标中的实际位置”。
评论区