Currently, I'm calculating the world matrix in C++ and then pass it to the shader to make the sprite always face the camera:
static D3DXVECTOR3 up(0, 0, 1);
D3DXMATRIX world, view, proj;
// get the world, view and projection matrix
g_pd3dDevice->GetTransform(D3DTS_WORLD, &world);
g_pd3dDevice->GetTransform(D3DTS_VIEW, &view);
g_pd3dDevice->GetTransform(D3DTS_PROJECTION, &proj);
D3DXMATRIX translation, invView, cameraPosition, rotation,invRotation;
// get the camera position by inversing the view matrix
D3DXMatrixInverse(&invView, NULL, &view);
cameraPosition = D3DXVECTOR3(invView._41, invView._42, invView._43);
// translate the sprite position to a world matrix
D3DXMatrixTranslation(&translation, spritePosition.x, spritePosition.y, spritePosition.z);
// calculate the world matrix rotation to look from
// the sprite position to the camera position
D3DXMatrixLookAtRH(&invRotation, &spritePosition, &cameraPosition, &up);
D3DXMatrixInverse(&rotation, NULL, &invRotation);
// pass the world * view * projection to the shader
world = rotation * translation;
worldViewProj = matrix.rotation * matrix.view * matrix.proj;
g_pEffect->SetMatrix("WorldViewProj", &worldViewProj);
I've just been learning DirectX and HLSL for the past few days so I don't know if this is the optimal and correct way to do it. I thought it would have been better done in the vertex shader but I don't know how, please guide me.
SimpleMath in the DirectX Tool Kit includes Matrix::CreateBillboard
and Matrix::CreateConstrainedBillboard
which is specifically designed for creating this kind of transformation matrix.
inline Matrix Matrix::CreateBillboard(
const Vector3& object,
const Vector3& cameraPosition,
const Vector3& cameraUp,
const Vector3* cameraForward) noexcept
{
using namespace DirectX;
const XMVECTOR O = XMLoadFloat3(&object);
const XMVECTOR C = XMLoadFloat3(&cameraPosition);
XMVECTOR Z = XMVectorSubtract(O, C);
const XMVECTOR N = XMVector3LengthSq(Z);
if (XMVector3Less(N, g_XMEpsilon))
{
if (cameraForward)
{
const XMVECTOR F = XMLoadFloat3(cameraForward);
Z = XMVectorNegate(F);
}
else
Z = g_XMNegIdentityR2;
}
else
{
Z = XMVector3Normalize(Z);
}
const XMVECTOR up = XMLoadFloat3(&cameraUp);
XMVECTOR X = XMVector3Cross(up, Z);
X = XMVector3Normalize(X);
const XMVECTOR Y = XMVector3Cross(Z, X);
XMMATRIX M;
M.r[0] = X;
M.r[1] = Y;
M.r[2] = Z;
M.r[3] = XMVectorSetW(O, 1.f);
Matrix R;
XMStoreFloat4x4(&R, M);
return R;
}
This is a port of the XNA Game Studio C# math library to C++ using DirectXMath.