ポイントを入力として受け取り、三角形のストリームを出力するジオメトリ シェーダーを使用してビルボードをレンダリングしようとしています (DirectX11 を使用)。しかし、現在の結果は私が期待するものではありません。
比較のために、ここに 2 つのスクリーンショットを示します。まったく同じセットのパーティクルをレンダリングし、同じ方向 (多かれ少なかれ、カメラを ~90° 回転させる必要がありました) と距離から見たものです。ポイントとしてレンダリングしたものと、ビルボードでレンダリングしたものですシェーダー:
2 番目の画像でわかるように、ポイントとしてレンダリングすると、パーティクルが中心から離れて移動し、ほぼ画面全体をカバーしていることがはっきりとわかりますが、ビルボード シェーダーでレンダリングすると、スケールがわずかに変化しますが、常に静止している。
残念ながら、何がこれを引き起こしているのかについての手がかりはありません。私はthisなどのチュートリアルに従ってきました.これは、マトリックスがどのように設定されることになっているのかを説明していますが、結果は私の期待または実装が間違っていることを示しています.
以下は、ビルボード シェーダーのコードです。このシェーダーは、マトリックスを構築してパーティクルの方向を記述し、ビルボードの 2 つの三角形を放出します。
#include <Materials/SceneConstants.hlsl>
#include <Materials/ModelConstants.hlsl>
#include <Particles/Particle.hlsl>
Texture2D diffuseTexture : register(ps, t[0]);
SamplerState diffuseSampler : register(ps, s[0]);
struct PS_IN
{
float4 Position : SV_POSITION;
float4 Color : COLOR;
float2 TexCoord : TEXCOORD;
};
Particle vs(Particle input)
{
return input;
}
[maxvertexcount(4)]
void gs(point Particle particles[1], inout TriangleStream<PS_IN> triStream)
{
// We need to create a matrix for the local coordinate system for the billboard of the given particle.
// One axis points from the particle to the camera, one axis is the camera's side axis (for example to
// the left) and the third one is perpendicular to both.
Particle particle = particles[0];
float3 zAxis = normalize(CameraPosition - particle.Position);
float3 xAxis = normalize(cross(float3(0, 1, 0), zAxis));
float3 yAxis = cross(zAxis, xAxis);
// The matrix to describe the local coordinate system is easily constructed:
float4x4 localToWorld;
localToWorld._11 = xAxis.x;
localToWorld._21 = xAxis.y;
localToWorld._31 = xAxis.z;
localToWorld._12 = yAxis.x;
localToWorld._22 = yAxis.y;
localToWorld._32 = yAxis.z;
localToWorld._13 = zAxis.x;
localToWorld._23 = zAxis.y;
localToWorld._33 = zAxis.z;
localToWorld._41 = particle.Position.x;
localToWorld._42 = particle.Position.y;
localToWorld._43 = particle.Position.z;
localToWorld._14 = 0;
localToWorld._24 = 0;
localToWorld._34 = 0;
localToWorld._44 = 1;
// And the matrix to transform from local to screen space...
float4x4 transform = localToWorld * World * ViewProjection;
// The positions of that quad is easily described in the local coordinate system:
// -z points towards the camera, y points upwards and x towards the right.
// The position marks the center of the quad, hence (0, 0, 0) is the center of the quad in
// local coordinates and the quad has an edge-length of particle.Size to either side.
PS_IN v1, v2, v3, v4;
//float size = particle.Size / 2;
float size = 0.5f;
v1.Position = mul(float4(-size, size, 0, 1), transform);
v1.TexCoord = float2(0, 0);
v1.Color = particle.Color;
v2.Position = mul(float4(size, size, 0, 1), transform);
v2.TexCoord = float2(1, 0);
v2.Color = particle.Color;
v3.Position = mul(float4(-size,-size, 0, 1), transform);
v3.TexCoord = float2(0, 1);
v3.Color = particle.Color;
v4.Position = mul(float4(size, -size, 0, 1), transform);
v4.TexCoord = float2(1, 1);
v4.Color = particle.Color;
triStream.Append(v1);
triStream.Append(v2);
triStream.Append(v3);
triStream.Append(v4);
}
float4 ps(PS_IN input) : SV_TARGET0
{
/*float4 texel = diffuseTexture.Sample(diffuseSampler, input.TexCoord);
return input.Color * texel;*/
return float4(1, 1, 1, 1);
}
参考までに、パーティクルを単純なポイントとしてレンダリングするためのシェーダー コードを次に示します。
#include <Materials/SceneConstants.hlsl>
#include <Materials/ModelConstants.hlsl>
#include <Particles/Particle.hlsl>
struct PS_IN
{
float4 Position : SV_POSITION;
float4 Color : COLOR;
};
PS_IN vs(Particle input)
{
PS_IN output;
float4 posWorld = mul(float4(input.Position, 1), World);
output.Position = mul(posWorld, ViewProjection);
output.Color = input.Color;
return output;
}
float4 ps(PS_IN input) : SV_TARGET0
{
//return input.Color;
return float4(1, 1, 1, 1);
}
私が気付いたもう 1 つの奇妙な点は、少なくとも常にではなく、私のビルボードがカメラの方を向いていないことです。ただし、マトリックスをセットアップする方法からすると、そうなると思います。代わりに、2 つの反対方向から見た場合にのみカメラの方を向き、カメラを回転させ始めるとすぐに幅が狭くなります。
これにより、マトリックスの作成を間違えたと思いましたが、それを見つけることができません。
うまくいけば、あなたたちは私が問題を見つけるのを手伝ってくれるでしょう. よろしくお願いします。
編集
この問題の解決策を見つけたようですが、なぜこれが解決策なのかわかりません。奇妙な理由で、localToWorld マトリックスに ViewProjection マトリックスを掛けることができません。代わりに、次のように 2 つのステップに分ける必要があります。
v1.Position = mul(float4(-size, size, 0, 1), localToWorld);
v1.Position = mul(v1.Position, ViewProjection);
これが事実である理由がわかりません。おそらく、デフォルトの column_major の代わりに row_major マトリックスを使用することに関連しています。しかし、現状では、この動作は私にはまったく意味がありません。行列の乗算は連想的でなければならないため、上記のスニペットは元のコードと同じ結果になるはずですが、明らかにそうではありません。ここで何が起こっているのか、いくつかの光を当てることができる人もいるかもしれません。