Билборд с использованием шейдера Geometry

Я пытаюсь визуализировать рекламные щиты, используя геометрический шейдер, который принимает точки в качестве входных данных и выводит поток треугольника (используя DirectX11). Однако текущий результат не тот, который я ожидаю.

Для сравнения, вот два снимка экрана, на которых изображен один и тот же набор частиц, видимый с одного и того же направления (более или менее мне пришлось повернуть камеру на ~90°) и расстояния, один раз отрисованный в виде точек и один раз с помощью моего рекламного щита шейдер:

Частицы отображаются с рекламным шейдеромЧастицы отображаются в виде точек

Как вы можете видеть на втором изображении, при рендеринге в виде точек ясно видно, что частицы удаляются от центра, покрывая почти весь экран, в то время как при рендеринге с помощью шейдерного щита они немного изменяются в масштабе, но всегда оставаться на месте

К сожалению, я понятия не имею, что является причиной этого. Я следовал подобным учебным пособиям, в которых объясняется, как должна устанавливаться матрица, однако результат показывает, что мои ожидания или реализация неверны.

Ниже приведен код для шейдера билборда, который строит матрицу для описания ориентации частицы, а затем испускает два треугольника для билборда:

#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. Но, как и сейчас, такое поведение вообще не имеет для меня никакого смысла: умножение матриц должно быть ассоциативным, и поэтому приведенный выше фрагмент должен приводить к тому же результату, что и исходный код, но это явно не так. Может быть, некоторые из вас смогут пролить свет на то, что здесь происходит.

1 ответ

Я думаю, что у вас есть вектор перевода в неправильной части матрицы, он должен читать

localToWorld._14 = particle.Position.x;
localToWorld._24 = particle.Position.y;
localToWorld._34 = particle.Position.z;
localToWorld._41 = 0;
localToWorld._42 = 0;
localToWorld._43 = 0;
Другие вопросы по тегам