Я пытаюсь отображать рекламные щиты с использованием геометрического шейдера, который принимает точки в качестве входных данных и выводит поток треугольника (используя DirectX11). Однако текущий результат не тот, который я ожидаю.Billboard с использованием Geometry shader
Для сравнения, вот два скриншота, изображающие один и тот же набор частиц, просматриваемых с одного и того же направления (более или менее, мне пришлось поворачивать камеру на ~ 90 °) и расстояние, как только отображались как точки и один раз оказываемый с моими щитами шейдера:
Как вы можете видеть на втором снимке, при визуализации в виде точек, можно ясно видеть, что частицы двигаются от центра, охватывающие почти все экран, тогда как при визуализации с помощью щитового щита они слегка изменяются в масштабе, но всегда остаются неподвижными.
К сожалению, у меня нет подсказки относительно того, что вызывает это. Я слежу за учебниками, такими как this, в которых объясняется, как должна быть настроена матрица, однако результат показывает, что мои ожидания или реализация неверны.
Ниже приведен код для рекламного щита шейдера, который строит матрицу для описания ориентации частицы, а затем излучает два треугольника для рекламного щита:
#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);
}
Другая странность, которую я заметил, что мои рекламные щиты не смотрит в камеру, по крайней мере, не всегда. Однако, если я настрою матрицу, я бы ожидал их. Вместо этого они смотрят на камеру только при просмотре с двух противоположных направлений, а затем уменьшают ширину, как только я начинаю поворачивать камеру.
Это привело меня к мысли, что я ошибся в построении матрицы, однако я не могу ее распознать.
Надеюсь, вы, ребята, можете помочь мне найти проблему. Заранее спасибо за вашу помощь!
Редактировать
Я, кажется, нашли решение этой проблемы, но я не понимаю, почему это одна. По какой-то причудливой причине я не могу умножить матрицу localToWorld на матрицу ViewProjection. Вместо этого, я должен разделить его на два шага, как так:
v1.Position = mul(float4(-size, size, 0, 1), localToWorld);
v1.Position = mul(v1.Position, ViewProjection);
Я не понимаю, почему это так, может быть, это связано с использованием row_major матриц вместо значения по умолчанию, column_major. Но, как и сейчас, это поведение вообще не имеет для меня никакого смысла: умножение матрицы должно быть ассоциативным, поэтому приведенный выше фрагмент должен приводить к тому же результату, что и исходный код, но это явно не так. Возможно, некоторые из вас могут пролить свет на то, что здесь происходит.
Похоже, вам нужно добавить исходную позицию вершины на каждый выход вершины по GS. – gareththegeek
Это то, что я думал сначала, однако это должно быть сделано, установив часть перевода «localToWorld» в particle.Position. Поскольку каждая вершинная позиция преобразуется матрицей, она также должна быть сдвинута по положению, не так ли? – Simon