2017-01-06 1 views
0

내 게임에서 쉐이더 구성에 도움이 필요합니다.HLSL - 쉐이더 및 기법 구성 및 손실되지 않음

게임은 질감과 조명을 위해 정점 및 픽셀 쉐이더를 사용합니다. 일부 객체는 질감이 있고, 다른 객체는 착색되어 있습니다. 그리고 나서 조명 알고리즘, 확산 및 반사 조명, 일부 객체 만 얻는 그림자 생성 등이 있습니다.

내 첫 번째 아이디어는 크고 멋진 쉐이더 기법 모든 매개 변수를 기반으로 처리 할 수 ​​있습니다. 유지 관리하는 것이 좋습니다 (아이디어를 제공하는 의사 코드).

float4 PixelShaderFunction(input) 
{ 
    if (UseTexture) 
     objectColor = tex2D(...) 
    else 
     objectColor = ObjectColor; 

    if (UseAmbient) 
     ... 

    if (UseDiffuseLight) 
     ... 

    // etc... 
} 

그러나 성능이 떨어졌습니다.

그런 다음 여러 기술을 사용하여 if 분기를 피할 수 있으므로 모든 기술은 특정 개체에 필요한 것을 사용합니다. 따라서 객체가 그림자를 허용하지 않으면 거기에 코드가 없습니다. 반사가 없으면 코드가 없습니다. 또한 공통 기능을 기능으로 그룹화했습니다. 이와 같이 (실제 코드는 현재) :

float4 Textured_PixelShader(Textured_VsOut input) : COLOR0 
{ 
    float4 lightLevel = 0; 
    float4 objectColor = GetObjColorTex(input.TexUV); 

    // calculate main table lighting 
    PsAddTableLight(input.WorldPosition, input.Normal, lightLevel); 

    // calculate cloth lighting 
    PsAddClothLight(input.WorldPosition, input.Normal, lightLevel); 

    // calculate fill light - do we need it? 
    PsAddFillLight(input.Normal, lightLevel); 

    // add ambient light 
    PsAddAmbientLight(lightLevel); 

    // apply light 
    PsApplyDiffuseLight(lightLevel, objectColor); 

    // add speculars 
    PsAddSpecularBlurred(input.WorldPosition, input.Normal, objectColor); 

    // final work 
    return PsFinalize(objectColor); 
} 

성능이 크게 향상되었습니다.

그러나이 쉐이더를 유지하는 데 길을 잃어 가고 있습니다. 매 초마다 texture + lightmap + this_kind_of_shadow + specular 또는 내가 필요한 모든 것을 수행 할 수있는 조합이 아직 없기 때문에 새로운 기술을 추가해야합니다. 그들 중 일부는 이런 이름을 가지며, 다른 하나는 그와 같은 조합을 가진 하나의 객체 만 있기 때문에 사용 된 객체에 의해 이름이 붙여집니다. 그리고 그것은 엉망이되고 있습니다.

  • 가 왜 그 if 문을 가질 수 없습니다 :

    나는 두 가지 질문 후이? 조건부 실행이 GPU를 손상시키는 방법에 대해 많이 읽었지만, if는 모든 렌더링 된 픽셀 (또는 verts)에 대해 동일한 값을 갖는 셰이더 매개 변수에만 의존합니다. 왜 그들은 빠를 수 없습니까? 나는 정말로 그들을 그리워한다.

  • 이 코드를 다른 쉐이더/기법/파일로 나누는 가장 좋은 방법은 무엇입니까? 좋은 표준이나 규칙이 있습니까?

답변

0

if 문을 사용할 수없는 이유는 무엇입니까? 조건부 실행이 GPU를 손상시키는 방법에 대해 많이 읽었지만, if는 모든 렌더링 된 픽셀 (또는 verts)에 대해 동일한 값을 갖는 셰이더 매개 변수에만 의존합니다. 왜 그들은 빠를 수 없습니까? 나는 정말로 그들을 그리워한다.

생각 :

가장 큰 문제는 컴파일러는 부울 표현과 아닌 변수가 쉐이더 상수라는 의미 론적 정보를 볼 수 있다는, 여기에있다. 특수한 경우 일 뿐이며 복잡한 셰이더 상수를 사용하는 함수로 더 복잡한 부울 식을 사용하는 경우 복잡해 질 수 있습니다. 나는 셰이더 컴파일러 개발자들의 두통을 막으려 고 생각한다.

현황 다음 HLSL-Documentation for if에 명시된 바와 같이

의 두 가지 모드가있는 flatten 또는 branch 중 하나, 경우. flatten을 사용하면 컴파일러에서 if의 양쪽을 롤아웃하기 때문에 먼저 계산되고 결과는 오른쪽에서 가져옵니다.branch을 사용하면 부울이 먼저 평가되므로 오른쪽 만 실행되지만이 모드는 tex2D와 같은 그라디언트 함수를 사용하지 않는 경우에만 사용할 수 있습니다. 이는 이웃 조각에 종속적이므로 실행해야합니다. 각 단편을 건너 뛰지 않아야합니다. 귀하의 경우에는 컴파일러가 ifs에 대해 flatten을 선택하므로 완전히 실행되고 느린 쉐이더가 생성됩니다.

이 코드를 다른 쉐이더/기법/파일로 나누는 가장 좋은 방법은 무엇입니까? 좋은 표준이나 규칙이 있습니까?

필자가 아는 한 좋은 표준은 없지만, Unity 또는 Unreal과 같은 엔진은 쉐이더를 관리하는 방법에 대해 자세히 살펴 보는 것이 좋습니다. 마지막으로 언리얼을 살펴 보았을 때 각 셰이더가 필요할 때마다 동적으로 생성되었으므로 셰이더 코드가 셰이더 작성기에서 생성되어 컴파일됩니다.

제 자신의 작은 엔진에서는 비슷한 접근 방식을 사용했지만 동적 분기 대신 전 처리기 지시문 #if, #elif, #else, and #endif을 사용하고 있습니다. 엔진이 필요한 셰이더를 만나면 오른쪽 정의를 설정 한 다음 D3DCompile으로 즉시 컴파일합니다. 말더듬을 방지하기 위해 컴파일 된 쉐이더를 디스크에 저장하고 컴파일하기 전에 쉐이더를 컴파일하기 전에 찾고 있습니다.

+0

불행히도 monogame이 지원하지 않기 때문에 자신의 방식 (동적 컴파일)을 수행 할 수없는 것처럼 보입니다. 그리고 일부 플랫폼에서만 제한 될 수 있습니다. 그러나 이것은 내가 받아 들인 것으로 표시하는 위대하고 가치있는 대답입니다 - 감사합니다. – Arek

+0

왜 'if'가 느린지 - 아직 확실하지는 않지만 성능 저하를 확인하기 위해 곧 다시 테스트 할 것입니다. 모든 소스에 따르면, 'if'조건은 렌더링 된 모든 것에 대해 동일하기 때문에 빠르다. – Arek

관련 문제