2013-05-08 2 views
1

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

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

Particles rendered with billboard shader Particles rendered as points

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

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

+1

Похоже, вам нужно добавить исходную позицию вершины на каждый выход вершины по GS. – gareththegeek

+0

Это то, что я думал сначала, однако это должно быть сделано, установив часть перевода «localToWorld» в particle.Position. Поскольку каждая вершинная позиция преобразуется матрицей, она также должна быть сдвинута по положению, не так ли? – Simon

ответ

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; 
+0

Спасибо за совет. К сожалению, это не сработало, и я думаю, что знаю почему; Я забыл добавить, что я работаю с матрицами row_major (#pragma pack_matrix (row_major)), который представляет собой тот же макет памяти, который использует DirectX (поэтому он соответствует статье, которую я связал). Возможно, я решил начальную проблему, но я действительно не понимаю, почему это так. Если я сначала умножаю положение вершины на матрицу «billboard» (localToWorld), а затем * затем * умножаю результирующий вектор на матрицу проекций-прогнозов, тогда ожидается результат - я просто не понимаю, почему. – Simon

+0

Матричное умножение должно быть ассоциативным, например. pos * (World * VP) должен быть таким же, как (pos * World) * VP, если нет? – Simon

+0

Нет, матричное умножение некоммутативно – gareththegeek

Смежные вопросы