2013-05-08 2 views
1

포인트를 입력으로 삼고 삼각형 스트림을 출력하는 지오메트리 셰이더를 사용하여 빌보드를 렌더링하려고합니다 (DirectX11 사용). 그러나 현재 결과는 내가 기대하는 바가 아닙니다.빌보드 기하학 쉐이더 사용

비교를 위해 동일한 방향에서 보았을 때 매우 비슷한 입자 집합을 렌더링하는 두 개의 스크린 샷이 있습니다. (한 번 더 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

가 보이는 읽어야 당신이 행렬의 잘못된 부분에 번역 벡터를 가지고 생각합니다. – gareththegeek

+0

그게 처음에 생각한거야, 그러나 이것은 "localToWorld"의 번역 부분을 particle.Position으로 설정하여 이루어져야합니다. 각 꼭지점의 위치는 행렬에 의해 변형되므로 위치에 따라 이동해야합니다. 그렇지 않아야합니까? – Simon

답변

1

나는 당신이 GS하여 각 정점의 출력에 원래의 정점 위치를 추가해야처럼, 그것은

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