여러 GLSL 셰이더간에 코드 공유


30

여러 셰이더간에 복사 붙여 넣기 코드를 자주 사용합니다. 여기에는 단일 계산에서 모든 셰이더간에 공유되는 특정 계산 또는 데이터와 모든 정점 셰이더에 필요한 공통 계산 (또는 다른 단계)이 포함됩니다.

물론, 그것은 끔찍한 관행입니다. 어디서나 코드를 변경 해야하는 경우 다른 곳에서 코드를 변경해야합니다.

DRY 를 유지하기 위해 허용되는 모범 사례가 있습니까? 사람들이 하나의 공통 파일을 모든 셰이더 앞에 추가합니까? #include지시문 을 구문 분석하는 자체 기초 C 스타일 프리 프로세서를 작성 합니까? 업계에 허용되는 패턴이 있으면 따르고 싶습니다.


4
다른 SE 사이트에서는 모범 사례에 대한 질문을 원하지 않기 때문에이 질문은 다소 논란의 여지가 있습니다. 이것은이 커뮤니티가 그러한 질문에 대해 어떻게 서 있는지 알기위한 것입니다.
Martin Ender

2
흠, 나에게 좋아 보인다. 나는 우리가 질문에 대해 "OverOver"/ "보다 일반적인"정도라고 말하고 싶습니다.
Chris는 Reinstate Monica

2
StackOverflow는 '우리에게 물어 보는 것'에서 ' 제발 부탁 하지 않으면 우리에게 묻지 말 것'으로 바뀌 습니다.
insidesin

주제에 관한 주제를 결정하기 위해 관련된 메타 질문은 어떻습니까?
SL 바스 – 복원 모니카

답변:


18

많은 접근 방식이 있지만 완벽한 것은 없습니다.

glAttachShader셰이더를 결합 하여 코드를 공유 할 수는 있지만 구조체 선언이나 #define-d 상수 와 같은 것을 공유 할 수는 없습니다 . 기능 공유를 위해 작동합니다.

어떤 사람들은 전달 된 문자열 배열을 사용하고 싶어합니다. glShaderSource 코드 앞에 공통 정의를 추가하는 방법으로 하려고하지만 몇 가지 단점이 있습니다.

  1. 셰이더에 포함해야 할 항목을 제어하기가 더 어렵습니다 (별도의 시스템이 필요합니다).
  2. #versionGLSL 사양의 다음 명령문으로 인해 셰이더 작성자가 GLSL을 지정할 수 없음을 의미합니다 .

#Version을 지시어는, 다른 어떤 전에 쉐이더에서 발생하는 의견과 공백을 제외해야합니다.

이 문장으로 인해 선언 glShaderSource앞에 텍스트를 추가하는 데 사용할 수 없습니다 #version. 이것은 #version라인 이 glShaderSource인수에 포함되어야 함을 의미합니다. 이는 GLSL 컴파일러 인터페이스에 어떤 버전의 GLSL이 사용될 것으로 예상되는지 알려줘야합니다. 또한 a #version를 지정하지 않으면 GLSL 컴파일러는 GLSL 버전 1.10을 기본값으로 사용합니다. 셰이더 작성자 #version가 표준 방식으로 스크립트 내에서를 지정 하게하려면 포함을해야 할 때보다 쉽게 ​​제어하기 위해 지시문을 삽입하는 것이 좋습니다. 반면에 GLSL은 줄 앞의 주석을 무시하므로 파일 맨 위에 주석 내에 포함에 대한 메타 데이터를 추가 할 수 있습니다 (yuck).#include 하려면 #version명령문 뒤에 -s를 합니다. GLSL 쉐이더를 명시 적으로 구문 분석하여 #version문자열 (있는 경우) 을 찾아서 문자열 을 찾은 후 포함 시키지만 할 수 있습니다.#include#version

의문의 여지가 있습니다 :에 대한 표준 솔루션이 있습니까 #include, 아니면 자체 전 처리기 확장을 롤링해야합니까?

거기이다 GL_ARB_shading_language_include확장,하지만 몇 가지 단점이 있습니다 :

  1. NVIDIA에서만 지원됩니다 ( http://delphigl.de/glcapsviewer/listreports2.php?listreportsbyextension=GL_ARB_shading_language_include )
  2. 포함 문자열을 미리 지정하여 작동합니다. 따라서 컴파일하기 전에 문자열 "/buffers.glsl"( #include "/buffers.glsl")이 파일의 내용과 일치하도록 지정해야 합니다.buffer.glsl (이전에로드 .
  3. 포인트 (2)에서 알 수 있듯이 경로는 "/"Linux 스타일 절대 경로와 같이 로 시작해야 합니다. 이 표기법은 일반적으로 C 프로그래머에게는 익숙하지 않으므로 상대 경로를 지정할 수 없습니다.

일반적인 설계는 고유 한 #include메커니즘 을 구현하는 것이지만 #if조건부 컴파일 (예 : 헤더 가드)을 올바르게 처리하려면 다른 전 처리기 명령어를 구문 분석하고 평가해야하기 때문에 까다로울 수 있습니다 .

자신 #include만을 구현하면 구현하려는 방법에 대한 자유도 있습니다.

  • 미리 문자열을 전달할 수 있습니다 (예 GL_ARB_shading_language_include:).
  • 포함 콜백을 지정할 수 있습니다 (DirectX의 D3DCompiler 라이브러리에서 수행).
  • 일반적인 C 응용 프로그램에서와 같이 항상 파일 시스템에서 직접 읽는 시스템을 구현할 수 있습니다.

단순화로 전처리 계층에 포함 할 때마다 헤더 가드를 자동으로 삽입 할 수 있으므로 프로세서 계층은 다음과 같습니다.

if (#include and not_included_yet) include_file();

(위의 기술을 보여준 Trent Reed에게 감사의 뜻을 전합니다.)

결론적 으로 자동, 표준 및 간단한 솔루션은 없습니다. 향후 솔루션에서는 일부 SPIR-V OpenGL 인터페이스를 사용할 수 있습니다.이 경우 GLSL to SPIR-V 컴파일러는 GL API 외부에있을 수 있습니다. 컴파일러를 OpenGL 런타임 외부에두면 #include파일 시스템과 인터페이스하기에 더 적합한 장소이므로 구현을 크게 단순화 합니다. 현재 널리 보급 된 방법은 C 프로그래머가 익숙 해야하는 방식으로 작동하는 사용자 정의 전처리기를 구현하는 것입니다.


glslify를 사용하여 셰이더를 모듈로 분리 할 수도 있지만 node.js 에서만 작동합니다.
앤더슨 그린

9

필자는 일반적으로 glShaderSource (...)가 입력으로 문자열 배열을 허용한다는 사실을 사용합니다.

쉐이더 (또는 더 정확한 프로그램)가 구성되는 방법을 지정하는 json 기반 쉐이더 정의 파일을 사용하고 필요한 프리 프로세서 정의, 사용하는 유니폼, 정점 / 조각 쉐이더 파일, 모든 추가 "종속성"파일. 실제 셰이더 소스 이전에 소스에 추가되는 함수 모음입니다.

AFAIK를 추가하기 위해 언리얼 엔진 4는 #include 지시문을 사용하여 컴파일하기 전에 구문 분석되고 모든 관련 파일을 추가합니다.


4

나는 일반적인 관습이 있다고 생각하지 않지만 추측을하면 거의 모든 사람들이 전처리 단계 ( #include확장자) 로 간단한 형태의 텍스트 포함을 구현한다고 말합니다. 매우 쉬운 일이기 때문입니다. 그래서. (예를 들어 JavaScript / WebGL에서는 간단한 정규식으로이를 수행 할 수 있습니다). 이것의 단점은 셰이더 코드를 더 이상 변경할 필요가없는 경우 "릴리스"빌드를 위해 오프라인 단계에서 사전 처리를 수행 할 수 있다는 것입니다.

실제로이 접근 방식이 일반적이라는 표시는 ARB 확장이 다음과 같이 도입되었다는 사실입니다 GL_ARB_shading_language_include. 이것이 지금까지 핵심 기능이되었는지 확실하지 않지만 확장은 OpenGL 3.2에 대해 작성되었습니다.


2
GL_ARB_shading_language_include는 핵심 기능이 아닙니다. 실제로 NVIDIA만이 지원합니다. ( delphigl.de/glcapsviewer/… )
Nicolas Louis Guillemot 1

4

어떤 사람들은 이미 glShaderSource문자열 배열을 취할 수 있다고 지적했습니다 .

또한 GLSL에서 컴파일 (glShaderSource , glCompileShader)과 연결 ( glAttachShader, glLinkProgram)은 별개입니다.

일부 프로젝트에서는 특정 파트와 대부분의 셰이더에 공통적 인 파트 사이에 셰이더를 분할하여 모든 셰이더 프로그램과 컴파일 및 공유하는 데 사용했습니다. 이것은 작동하며 구현하기 어렵지 않습니다. 종속성 목록 만 유지하면됩니다.

그러나 유지 관리 측면에서이기는 것이 확실하지 않습니다. 관찰 결과는 동일합니다. 실제로 반복을 피하는 동안 기술의 오버 헤드는 상당히 중요합니다. 또한 최종 셰이더는 추출하기가 더 어렵습니다. 선언이 일부 컴파일러가 거부하거나 복제되는 순서로 종료되므로 셰이더 소스를 연결할 수 없습니다. 따라서 별도의 도구에서 빠른 쉐이더 테스트를 수행하기가 더 어려워집니다.

결국이 기술은 일부 DRY 문제를 해결하지만 이상적이지는 않습니다.

부수적 인 주제로,이 접근 방식이 컴파일 시간 측면에서 어떤 영향을 미치는지 확실하지 않습니다. 일부 드라이버는 링크시 쉐이더 프로그램 만 실제로 컴파일한다는 것을 읽었지만 측정하지는 않았습니다.


내 이해로는 구조체 정의 공유 문제를 해결하지 못한다고 생각합니다.
Nicolas Louis Guillemot

@NicolasLouisGuillemot : 그렇습니다. 지시 코드만이 선언이 아닌이 방법으로 공유됩니다.
Julien Guertault
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.