网格被切断

Mesh getting cut off

我正在使用 DirectX 11。我试图在屏幕上绘制一个立方体网格,但下半部分被切断了。如果我移动相机 up/down,下半部分仍然被切断,这使我认为这不是 viewport/rasterizer 问题,但我不确定。图片是立方体向下看然后向上看的画面。无论相机位置如何,您都可以看到立方体被切断。我认为这可能是我的投影矩阵的问题。

我在此处附上了 RenderDoc 捕获,您可以看到 VS 输入是正确的,但是当查看具有纯色阴影的 VS 输出时,同样的事情发生了。 https://drive.google.com/file/d/1sh7tj0hPYwD936BEQCL0wtH8ZzXMiEno/view?usp=sharing

这就是我计算矩阵的方式:

mat4 LookAtMatrix(float3 Position, float3 Target, float3 Up) {
    float3 Forward = Normalise(Target - Position);
    float3 Right = Cross(Normalise(Up), Forward);
    float3 UpV = Cross(Forward, Right);
    
    mat4 Out;
    Out.v[0] = float4(Right, 0);
    Out.v[1] = float4(UpV, 0);
    Out.v[2] = float4(Forward, 0);
    Out.v[3] = float4(Position, 1);
    return Out;
}

mat4 ProjectionMatrix(f32 FOV, f32 Aspect, f32 Near, f32 Far) {
    mat4 Out;
    f32 YScale = 1.0f / tan((FOV * Deg2Rad) / 2.0f);
    f32 XScale = YScale / Aspect;
    f32 NmF = Near - Far;
    
    Out.v[0] = float4(XScale, 0, 0, 0);
    Out.v[1] = float4(0, YScale, 0, 0);
    Out.v[2] = float4(0, 0, (Far + Near) / NmF, -1.0f);
    Out.v[3] = float4(0, 0, 2 * Far * Near / NmF, 0);
    
    return Out;
}

这就是我调用这些函数的方式(无论我是否使用旋转都会出现问题):

    D3D11_MAPPED_SUBRESOURCE Resource;
    HRESULT Result = DeviceContext->Map(ConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &Resource);
    if(FAILED(Result)) FatalError("DeviceContext->Map failed");
    matrix_buffer *Buffer = (matrix_buffer *)Resource.pData;
    
    static float yR = 0.0f;
    yR += 50.0f * DeltaTime;
    while(yR > 360.0f) yR -= 360.0f;
    while(yR < 0.0f) yR += 360.0f;
    quat R = QuatFromAngles(0.0f, yR, 0.0f);
    
    const float Speed = 100.0f;
    static float3 Position = float3(0, 0, -300);
    if(WDown) Position.z += Speed * DeltaTime;
    if(ADown) Position.x += Speed * DeltaTime;
    if(SDown) Position.z -= Speed * DeltaTime;
    if(DDown) Position.x -= Speed * DeltaTime;
    if(QDown) Position.y -= Speed * DeltaTime;
    if(EDown) Position.y += Speed * DeltaTime;
    
    Buffer->WorldMatrix = RotationMatrix(R, float3(0, 0, 0));
    Buffer->ViewMatrix = LookAtMatrix(Position, Position+float3(0, 0, 1), float3(0, 1, 0));
    Buffer->ProjectionMatrix = ProjectionMatrix(45.0f, 1366/768, 0.1f, 1000.0f);
    
    DeviceContext->Unmap(ConstantBuffer, 0);

这是我的顶点着色器代码:

struct vertex_data {
    float3 Position : POSITION;
    float2 UV : TEXCOORD;
    float4 Colour : COLOR;
    float3 Normal : NORMAL;
};

struct pixel_data {
    float4 Position : SV_POSITION;
    float2 UV : TEXCOORD;
    float4 Colour : COLOR;
    float3 Normal : NORMAL;
};

cbuffer MatrixBuffer {
    float4x4 WorldMatrix;
    float4x4 ViewMatrix;
    float4x4 ProjectionMatrix;
};

pixel_data VertexMain(vertex_data Input) {
    pixel_data Output;

    float4 V = float4(Input.Position, 1);
    Output.Position = mul(V, transpose(WorldMatrix));
    Output.Position = mul(Output.Position, transpose(ViewMatrix));
    Output.Position = mul(Output.Position, transpose(ProjectionMatrix));

    Output.UV = Input.UV;
    Output.Colour = Input.Colour;
    Output.Normal = Input.Normal;

    return Output;
}

这是我设置视口的代码(Width/Height 是 1366/768 - window 的大小):

    D3D11_VIEWPORT Viewport;
    Viewport.Width = (float)Width;
    Viewport.Height = (float)Height;
    Viewport.MinDepth = 0.0f;
    Viewport.MaxDepth = 1.0f;
    Viewport.TopLeftX = 0.0f;
    Viewport.TopLeftY = 0.0f;
    
    DeviceContext->RSSetViewports(1, &Viewport);

我看到过由以下原因引起的类似问题:

  1. 转置矩阵(你使用的是行主矩阵还是列主矩阵?你需要#pragma pack_matrix吗?看起来你已经对转置做了很多事情 - 避免这样做那,因为你会犯难以推理的错误)

  2. 否则矩阵乘法顺序乱了。如果你摆动相机 up/down/left/right 或围绕它旋转模型并旋转它,它真的有效吗?确保将相机旋转与相机平移和对象旋转/平移相结合,否则您可能会错误地认为您的代码有效。如果你拉近或拉远会怎样?

我建议您在调试这些问题时首先尝试 运行 在 CPU 代码中进行着色器转换:

  1. 取一个简单的 model-space 坐标(例如 0,0,0)。
  2. 通过你的世界矩阵,检查它是否正确。
  3. 通过你的视图矩阵,验证它。
  4. 然后是你的项目矩阵。

即使是那个简单的测试也能揭示很多问题。基本上,如果您认为您的顶点着色器是错误的,幸运的是,这通常是最容易在软件中验证的着色器!如果通过,请尝试其他几个顶点,例如您的盒子的顶点。如果这在软件中成功了,那么现在您知道它在某种程度上与您将顶点数据传递给 GPU 的方式有关(例如 row-major 与 column-major)。如果没有,那么您已经构建了一个简单的 CPU-side 重现,太棒了。

(此外,我不确定您的像素着色器是什么,但要排除它并隔离顶点着色器,请考虑将像素着色器设为 return 纯白色)