You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于DirectX 9顶点着色器实现2D精灵始终朝向相机

关于精灵朝向相机的矩阵计算优化与顶点着色器实现

首先看你当前代码里的几个问题:

  • cameraPosition被错误定义为D3DXMATRIX,实际应该是D3DXVECTOR3,类型不匹配会导致逻辑错误。
  • worldViewProj的计算存在笔误,正确公式应为world * view * proj,你写的matrix.rotation * matrix.view * matrix.proj里的matrix变量未定义。
  • 通过D3DXMatrixLookAtRH再求逆的方式计算旋转矩阵,存在额外的矩阵求逆开销,其实可以通过向量运算直接构建朝向矩阵。

修正后的CPU端计算代码

如果继续在CPU端处理,优化后的代码如下:

static D3DXVECTOR3 up(0, 0, 1);
D3DXMATRIX world, view, proj;

g_pd3dDevice->GetTransform(D3DTS_VIEW, &view);
g_pd3dDevice->GetTransform(D3DTS_PROJECTION, &proj);

// 从视图矩阵提取相机位置
D3DXMATRIX invView;
D3DXMatrixInverse(&invView, NULL, &view);
D3DXVECTOR3 cameraPos(invView._41, invView._42, invView._43);

// 计算精灵到相机的归一化方向向量
D3DXVECTOR3 lookDir = cameraPos - spritePosition;
D3DXVec3Normalize(&lookDir, &lookDir);

// 构建朝向矩阵:先计算右向量,再通过叉乘修正上方向(避免原始上方向与视线平行)
D3DXVECTOR3 right;
D3DXVec3Cross(&right, &up, &lookDir);
D3DXVec3Normalize(&right, &right);
D3DXVECTOR3 newUp;
D3DXVec3Cross(&newUp, &lookDir, &right);

// 构建行优先的旋转矩阵
D3DXMATRIX rotation;
rotation._11 = right.x; rotation._12 = right.y; rotation._13 = right.z; rotation._14 = 0;
rotation._21 = newUp.x; rotation._22 = newUp.y; rotation._23 = newUp.z; rotation._24 = 0;
rotation._31 = lookDir.x; rotation._32 = lookDir.y; rotation._33 = lookDir.z; rotation._34 = 0;
rotation._41 = 0; rotation._42 = 0; rotation._43 = 0; rotation._44 = 1;

// 平移矩阵
D3DXMATRIX translation;
D3DXMatrixTranslation(&translation, spritePosition.x, spritePosition.y, spritePosition.z);

// 世界矩阵 = 旋转 * 平移(DirectX矩阵乘法顺序:变换从右到左)
world = rotation * translation;

// 计算世界-视图-投影矩阵
D3DXMATRIX worldViewProj = world * view * proj;
g_pEffect->SetMatrix("WorldViewProj", &worldViewProj);

顶点着色器端实现方案

将逻辑移到GPU(顶点着色器)可以减少CPU到GPU的数据传输开销,尤其适合大量精灵的场景。

1. C++端传递必要参数

不需要计算完整世界矩阵,仅传递基础参数到着色器:

D3DXMATRIX view, proj;
g_pd3dDevice->GetTransform(D3DTS_VIEW, &view);
g_pd3dDevice->GetTransform(D3DTS_PROJECTION, &proj);

D3DXMATRIX invView;
D3DXMatrixInverse(&invView, NULL, &view);
D3DXVECTOR3 cameraPos(invView._41, invView._42, invView._43);

g_pEffect->SetVector("SpritePos", (D3DXVECTOR4*)&spritePosition);
g_pEffect->SetVector("CameraPos", (D3DXVECTOR4*)&cameraPos);
g_pEffect->SetVector("UpDir", (D3DXVECTOR4*)&up);
g_pEffect->SetMatrix("View", &view);
g_pEffect->SetMatrix("Proj", &proj);

2. HLSL顶点着色器实现

在着色器中完成朝向矩阵计算与顶点变换:

struct VS_INPUT
{
    float3 pos : POSITION; // 精灵本地顶点坐标(比如中心为(0,0,0),四角为(±0.5,±0.5,0))
    float2 uv : TEXCOORD0;
};

struct PS_INPUT
{
    float4 pos : SV_POSITION;
    float2 uv : TEXCOORD0;
};

cbuffer ConstantBuffer : register(b0)
{
    float4 SpritePos;
    float4 CameraPos;
    float4 UpDir;
    float4x4 View;
    float4x4 Proj;
};

PS_INPUT VS(VS_INPUT input)
{
    PS_INPUT output;

    // 计算精灵到相机的归一化方向
    float3 lookDir = normalize(CameraPos.xyz - SpritePos.xyz);
    // 计算右向量
    float3 right = normalize(cross(UpDir.xyz, lookDir));
    // 修正上方向,避免与视线平行
    float3 newUp = cross(lookDir, right);

    // 构建世界矩阵:旋转 + 平移
    float4x4 world = float4x4(
        right.x, newUp.x, lookDir.x, 0,
        right.y, newUp.y, lookDir.y, 0,
        right.z, newUp.z, lookDir.z, 0,
        SpritePos.x, SpritePos.y, SpritePos.z, 1
    );

    // 顶点坐标变换:本地 -> 世界 -> 视图 -> 裁剪
    float4 worldPos = mul(float4(input.pos, 1.0f), world);
    float4 viewPos = mul(worldPos, View);
    output.pos = mul(viewPos, Proj);

    output.uv = input.uv;
    return output;
}

方案对比

  • CPU端计算:适合精灵数量少的场景,代码直观易调试,但大量精灵会占用较多CPU资源。
  • 顶点着色器计算:将矩阵计算转移到GPU,CPU仅传递基础参数,适合大规模精灵场景,效率更高。

内容的提问来源于stack exchange,提问作者thedemons

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 08:00:30