顶点着色器中的坐标
顶点默认使用Normalized Device Coordinates。 当你把一个顶点传进顶点着色器后,最终输出一个 SV_POSITION,这个位置被投影到了 NDC 空间(归一化设备坐标),它具有如下特性:
轴向 | 范围 | 含义 |
---|---|---|
X | -1 到 +1 | 左到右 |
Y | +1 到 -1 | 上到下(注意是倒的) |
Z | 0 到 +1 | 前到后(视锥内的深度) |
🤓 D3D11 的特殊点:
- 左手坐标系:D3D 默认使用左手坐标系(x右,y上,z向屏幕内)
- NDC 的 Z 范围是 0 到 1(OpenGL 是 -1 到 +1)
Primitives(图元)
- Point Lists
- Line Lists
- Line Strips
- Triangle Lists (最常用)
- Triangle Strips
渲染管线
输入装配(IA) → 顶点着色器(VS) → 曲面细分阶段(HS/DS) → 几何着色器(GS)
↓
光栅化(Rasterizer)
↓
像素着色器(PS)
↓
输出合并(OM)
阶段 | 可编程? | Shader 名称 | 是否必须 |
---|---|---|---|
IA | ❌ | 无 | 是 |
VS | ✅ | Vertex Shader | ✅ 必须 |
HS/DS | ✅ | Hull/Domain Shader | 可选 |
GS | ✅ | Geometry Shader | 可选 |
Rasterizer | ❌ | 无 | 是 |
PS | ✅ | Pixel Shader | ✅ 必须 |
OM | ❌ | 无 | 是 |
纹理
DXGI FORMAT对应HLSL中的类型
| DXGI | HLSL | | —————————— | ———— | | DXGI_FORMAT_R32_FLOAT | float | | DXGI_FORMAT_R32G32_FLOAT | float2 | | DXGI_FORMAT_R32G32B32A32_FLOAT | float4 | | DXGI_FORMAT_R32_UINT | uint | | DXGI_FORMAT_R32G32_UINT | uint2 | | DXGI_FORMAT_R32G32B32A32_UINT | uint4 | | DXGI_FORMAT_R32_SINT | int | | DXGI_FORMAT_R32G32_SINT | int2 | | DXGI_FORMAT_R32G32B32A32_SINT | int4 | | DXGI_FORMAT_R16G16B16A16_FLOAT | float4 | | DXGI_FORMAT_R8G8B8A8_UNORM | unorm float4 | | DXGI_FORMAT_R8G8B8A8_SNORM | snorm float4 |
其中unorm float表示的是一个32位无符号的,规格化的浮点数,可以表示范围0到1 而与之对应的snorm float表示的是32位有符号的,规格化的浮点数,可以表示范围-1到1
USAGE
| D3D11_USAGE | CPU读 | CPU写 | GPU读 | GPU写 | | ——————— | —– | —– | —– | —– | | D3D11_USAGE_DEFAULT | | | √ | √ | | D3D11_USAGE_IMMUTABLE | √ | | | | | D3D11_USAGE_DYNAMIC | | √ | √ | | | D3D11_USAGE_STAGING | √ | √ | √ | √ |
BindFlag
| D3D11_BIND_FLAG | 描述 | | ————————— | ———————————————————– | | D3D11_BIND_SHADER_RESOURCE | 纹理可以作为着色器资源绑定到渲染管线 | | D3D11_BIND_STREAM_OUTPUT | 纹理可以作为流输出阶段的输出点 | | D3D11_BIND_RENDER_TARGET | 纹理可以作为渲染目标的输出点,并且指定它可以用于生成mipmaps | | D3D11_BIND_DEPTH_STENCIL | 纹理可以作为深度/模板缓冲区 | | D3D11_BIND_UNORDERED_ACCESS | 纹理可以绑定到无序访问视图作为输出 |
CPU ACESS
D3D11_CPU_ACCESS_FLAG | 描述 |
---|---|
D3D11_CPU_ACCESS_WRITE | 允许通过映射方式从CPU写入,它不能作为管线的输出,且只能用于D3D11_USAGE_DYNAMIC和D3D11_USAGE_STAGING绑定的资源 |
D3D11_CPU_ACCESS_READ | 允许通过映射方式给CPU读取,它不能作为管线的输入或输出,且只能用于D3D11_USAGE_STAGING绑定的资源 |
Vertex Shadler
D3D11_INPUT_ELEMENT_DESC input_desc[] =
{
// 我们知道DXGI_FORMAT_R32G32B32_FLOAT是12字节的
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0},
};
CreateInputLayout(VERTEX::input_desc, std::size(VERTEX::input_desc), ...);
当我们在C++里面写入上述代码的时候, 是告诉D3D11已”Position”和”Color”解析输入的Vertex数据。
于是, 我们将下面的数据传给D3D11:
VERTEX OurVertices[] = {
// 每组数据3 * 4 + 3 * 4 = 24字节
{DirectX::XMFLOAT3{0.0f, 0.5f, 0.0f}, DirectX::XMFLOAT3(1.0f, 0.0f, 0.0f)},
{DirectX::XMFLOAT3{0.45f, -0.5, 0.0f}, DirectX::XMFLOAT3(0.0f, 1.0f, 0.0f)},
{DirectX::XMFLOAT3{-0.45f, -0.5f, 0.0f}, DirectX::XMFLOAT3(0.0f, 0.0f, 1.0f)},
};
CreateBuffer(&vertex_buf_);
IASetVertexBuffers(vertex_buf_);
关键字语义
Semantic | Description |
---|---|
POSITION | A float4 value that stores position. It is used to denote the position of vertices, usually (but not necessarily) in 3D space. |
COLOR | A float4 value that stored color |
SV_POSITION | A float4 value that stores position. It is used to denote the position in normalized screen coordinates, not 3D coordinates. |
SV_TARGET | A float4 value telling the output-merger to draw the given color on the render target. |
// 为什么是float4, 而不是float3?搞不懂
// 实测float3也可以work
struct VOut {
float4 position : SV_POSITION;
float4 color : COLOR;
};
VOut VShader(float4 position : POSITION, float4 color : COLOR)
{
VOut output;
output.position = position;
output.color = color;
return output;
}
// 使用的是normailized坐标, 返回值是给输出合并阶段的用的, 输出到render Target上
float4 PShader(float4 position : SV_POSITION,
float4 color: COLOR): SV_TARGET
{
return color;
}
为什么CPU中的float3也能匹配上VertexShader中的POSITION(float4)语义
🧠 真相是:
✅ 如果你 VS 中写 float4,但 InputLayout 提供的是 float3,编译器自动补 w=1.0f
这是 HLSL 里的一个智能规则:
如果你的 InputLayout 是 float3,而你的 shader 要求 float4,系统会自动补成 float4(x, y, z, 1.0f)
这个行为完全合法,且广泛使用 —— 因为通常你需要 w=1.0f 来做变换矩阵乘法。
✅ 反过来就不行!
如果你上传的是 float4,但 shader 输入写的是 float3,会报错或行为不确定。因为你传的数据比 shader 想要的还多,HLSL 不知道要不要忽略、剪掉,容易出错。
为什么VertexShader的输出必须 float4?
- GPU 使用的是“齐次坐标(Homogeneous Coordinates)”
- 裁剪、透视除法、屏幕映射都依赖它
- 这就是你要执行 output.pos = mul(MVP, float4(input.pos, 1.0f)); 的原因
缓冲区寄存器标记
HLSL register | 类型 | 意义 |
---|---|---|
b# |
常量缓冲区 (CB) | Constant Buffer,最多绑定 14 个 |
t# |
纹理 (Texture) | Texture2D , Texture3D , 等 |
s# |
采样器 (Sampler) | SamplerState 等 |
u# |
无序访问 (UAV) | 用于计算着色器、RW结构等 |
HLSL 结构对齐的问题
下面是常量缓冲区的对齐规则, 但是顶点缓冲区的对齐规则又是咋样的, 为什么大家都是写float4?
see: https://www.cnblogs.com/X-Jun/p/9376474.html
类型 | 是否需要对齐 | 常见用途 |
---|---|---|
ConstantBuffer (常量缓冲区) |
✅ 必须对齐 | 向 Shader 传递参数(register(b#)) |
StructuredBuffer |
✅ 建议对齐 | Shader 中访问结构化数据 |
RawBuffer (字节地址缓冲) |
❌ 不强制 | 用 ByteAddressBuffer 类型按字节访问 |
VertexBuffer |
✅ 通常对齐 | 顶点输入结构体 |
IndexBuffer |
❌ 不要求 | 用于绘制顶点顺序(16或32bit) |
RWStructuredBuffer (可写) |
✅ 建议对齐 | Shader 中写入结构体数据 |
常见对齐策略:
变量类型 | 占用空间(HLSL) | 补齐建议(C++) |
---|---|---|
float , int , uint |
4 字节 | 补 12 字节 → 成 16 字节对齐 |
float2 |
8 字节 | 补 8 字节 |
float3 |
12 字节 | 补 4 字节 |
float4 |
16 字节 | 不用补 |
matrix (4x4) |
64 字节 | 已自动对齐 |
示例:
cbuffer Example : register(b0)
{
float a; // offset 0
float3 b; // offset 16(float + float3 共享 vec4)
float4 c; // offset 32
};
参考文档
- https://www.cnblogs.com/X-Jun/p/10262524.html