세미 커먼 접근 방식은 모듈 호출이라고 생각하는 것과 비슷한 셰이더 구성 요소 호출을 만드는 것 입니다.
아이디어는 후 처리 그래프와 유사합니다. 필요한 입력, 생성 된 출력 및 실제로 작동하는 코드를 모두 포함하는 셰이더 코드 청크를 작성합니다. 어떤 상황에서 적용 할 셰이더를 나타내는 목록이 있습니다 (이 재질에 범프 매핑 구성 요소가 필요한지 여부, 지연 또는 전달 구성 요소의 활성화 여부 등).
이제이 그래프를 가져 와서 그로부터 셰이더 코드를 생성 할 수 있습니다. 이것은 대부분 덩어리의 코드를 제자리에 "붙여 넣기"하는 것을 의미하며, 그래프는 이미 필요한 순서대로되어 있는지 확인한 다음 셰이더 입력 / 출력에 적절하게 붙여 넣습니다 (GLSL에서 "전역"을 , 출력 및 균일 변수).
이것은 ubershader 접근 방식과 동일하지 않습니다. Ubershaders는 컴파일 또는 실행할 때 기능을 켜고 끄는 #ifdefs 및 유니폼 등을 사용하여 모든 것에 필요한 모든 코드를 단일 셰이더 세트에 넣는 곳입니다. 나는 개인적으로 ubershader 접근법을 멸시하지만 다소 인상적인 AAA 엔진이이를 사용합니다 (Crytek이 특히 중요합니다).
여러 가지 방법으로 셰이더 청크를 처리 할 수 있습니다. GLSL, HLSL 및 콘솔을 지원하려는 경우 가장 유용한 방법은 셰이더 언어에 대한 파서를 작성하는 것입니다 (아마도 HLSL / Cg 또는 GLSL에 가까운 경우 개발자가 최대한 "이해할 수있는"정도) 그런 다음 소스 간 변환에 사용할 수 있습니다. 또 다른 방법은 XML 파일 등으로 셰이더 청크를 마무리하는 것입니다.
<shader name="example" type="pixel">
<input name="color" type="float4" source="vertex" />
<output name="color" type="float4" target="output" index="0" />
<glsl><![CDATA[
output.color = vec4(input.color.r, 0, 0, 1);
]]></glsl>
</shader>
이 접근법을 사용하면 다른 API에 대해 여러 개의 코드 섹션을 만들거나 코드 섹션의 버전을 지정할 수 있습니다 (GLSL 1.20 버전 및 GLSL 3.20 버전을 사용할 수 있음). 그래프는 호환 가능한 코드 섹션이없는 셰이더 청크를 자동으로 제외 할 수 있으므로 구형 하드웨어에서 약간의 성능 저하를 얻을 수 있습니다 (따라서 일반 매핑 또는 프로그래머가 필요로하지 않는 구형 하드웨어에서는 제외되는 것). 많은 명시 적 검사를 수행하십시오).
XMl 샘플은 다음과 비슷한 것을 생성 할 수 있습니다 (이것이 유효하지 않은 GLSL 인 경우, API에 영향을받은 지 오래되었습니다).
layout (location=0) in vec4 input_color;
layout (location=0) out vec4 output_color;
struct Input {
vec4 color;
};
struct Output {
vec4 color;
}
void main() {
Input input;
input.color = input_color;
Output output;
// Source: example.shader
#line 5
output.color = vec4(input.color.r, 0, 0, 1);
output_color = output.color;
}
좀 더 똑똑하고 "효율적인"코드를 생성 할 수 있지만, 솔직히 말해서 모든 쓰레기가 아닌 셰이더 컴파일러는 생성 된 코드에서 중복성을 제거 할 것입니다. 최신 GLSL을 사용하면 파일 이름을 #line
명령에 넣을 수도 있지만 이전 버전은 매우 부족하여 지원하지 않습니다.
청크가 여러 개인 경우 해당 입력 (트리의 조상 청크에 의해 출력으로 제공되지 않음)이 출력과 같이 입력 블록에 연결되고 코드가 연결됩니다. 스테이지가 일치하고 (정점 대 조각) 정점 속성 입력 레이아웃이 "정상 작동"하도록 약간의 추가 작업이 수행됩니다. 이 방법의 또 다른 장점은 이전 버전의 GLSL에서 지원되지 않는 명시 적 균일 및 입력 속성 바인딩 인덱스를 작성하고이를 셰이더 생성 / 바인딩 라이브러리에서 처리 할 수 있다는 것입니다. 마찬가지로 메타 데이터를 사용하여 VBO 및 glVertexAttribPointer
호출을 설정하여 호환성을 유지하고 모든 것이 " 제대로 작동"하도록 할 수 있습니다.
불행히도 이와 같은 좋은 크로스 API 라이브러리는 없습니다. Cg는 거의 비슷하지만 AMD 카드에서 OpenGL을 완벽하게 지원하며 가장 기본적인 코드 생성 기능을 사용하면 속도가 매우 느릴 수 있습니다. DirectX 효과 프레임 워크도 작동하지만 HLSL 이외의 언어는 지원하지 않습니다. DirectX 라이브러리를 모방 한 GLSL에 대한 불완전한 / 버기 라이브러리가 있지만 마지막으로 확인한 상태를 지정하면 나만의 것을 작성합니다.
ubershader 방식은 특정 기능에 대해 "잘 알려진"전 처리기 지시문을 정의한 다음 다른 구성으로 다른 재료를 재 컴파일하는 것을 의미합니다. 예를 들어, 노멀 맵이있는 머티리얼에 대해 정의한 USE_NORMAL_MAPPING=1
다음 픽셀 스테이지 우버 쉐이더에서 다음을 가질 수 있습니다.
#if USE_NORMAL_MAPPING
vec4 normal;
// all your normal mapping code
#else
vec4 normal = normalize(in_normal);
#endif
여기서 큰 문제는 사용중인 모든 조합을 사전 컴파일 해야하는 사전 컴파일 된 HLSL에 대해 이것을 처리하는 것입니다. GLSL을 사용하더라도 동일한 쉐이더를 재 컴파일 / 캐싱하지 않도록 사용중인 모든 프리 프로세서 지시문의 키를 올바르게 생성 할 수 있어야합니다. 유니폼을 사용하면 복잡성을 줄일 수 있지만 전 처리기 유니폼과 달리 명령 수를 줄이지 않고 여전히 성능에 약간의 영향을 줄 수 있습니다.
명확하게 말하면, 두 가지 접근 방식 (수 많은 변형을 수동으로 작성하는 것뿐만 아니라)은 모두 AAA 공간에서 사용됩니다. 가장 적합한 것을 사용하십시오.