프래그먼트 셰이더에 임의의 수의 라이트를 사용하는 방법이 있습니까?


19

프래그먼트 셰이더에 임의의 수의 조명 위치 (및 색상)를 전달하고 셰이더에서 이들을 반복하는 방법이 있습니까?

그렇지 않은 경우 여러 조명을 어떻게 시뮬레이션해야합니까? 예를 들어 확산 방향 조명과 관련하여 셰이더에 대한 총 중량을 전달할 수는 없습니다.


WebGL로 작업하지 않았지만 OpenGL에는 최대 8 개의 광원이 있습니다. 내 의견으로는, 그 이상을 전달하려면 예를 들어 균일 변수를 사용해야합니다.
zacharmarz

오래된 방법은 항상 모든 조명을 통과하는 것이었고 사용하지 않은 조명은 0 광도로 설정되었으므로 장면에 영향을 미치지 않습니다. 아마 더 이상 사용하지 않을 것입니다 ;-)
Patrick Hughes

7
Google이 이와 같은 것을 할 때는 'WebGL'이라는 용어를 사용하지 마십시오. 기술이 너무 어려서 사람들이 이러한 문제에 접근 할 수는 없습니다. 가지고 이 검색 '운 좋은 예감'예를 들어, 일 것이다. WebGL 문제는 정확히 동일한 OpenGL 문제로 잘 변환되어야합니다.
조나단 디킨슨

포워드 렌더링에서 8 개 이상의 조명에 대해 일반적으로 다중 패스 쉐이더를 사용하고 각 패스에 추가 블렌딩을 사용하여 처리 할 서로 다른 8 개의 조명 그룹을 제공합니다.
ChrisC

답변:


29

이를 처리하는 방법에는 일반적으로 두 가지가 있습니다. 오늘날에는이를 포워드 렌더링 및 지연 렌더링이라고합니다. 아래에서 논의 할 두 가지 변형이 있습니다.

포워드 렌더링

영향을주는 모든 조명에 대해 각 객체를 한 번 렌더링합니다. 여기에는 주변 조명이 포함됩니다. 추가 블렌드 모드 ( glBlendFunc(GL_ONE, GL_ONE))를 사용하므로 각 라이트의 컨트 리뷰 션이 서로 추가됩니다. 다른 조명의 기여도는 부가 적이므로 프레임 버퍼는 결국 가치를 얻습니다.

부동 소수점 프레임 버퍼로 렌더링하여 HDR을 얻을 수 있습니다. 그런 다음 장면을 최종적으로 통과하여 HDR 조명 값을 가시 범위로 축소합니다. 또한 블룸 및 기타 사후 효과를 구현할 수도 있습니다.

이 기법에 대한 일반적인 성능 향상 (장면에 많은 오브젝트가있는 경우)은 "프리 패스"를 사용하여 컬러 프레임 버퍼에 아무것도 그리지 않고 모든 오브젝트를 렌더링합니다 ( glColorMask컬러 쓰기를 끄는 데 사용 ). 이것은 단지 깊이 버퍼를 채 웁니다. 이런 식으로 다른 객체 뒤에있는 객체를 렌더링하면 GPU가 해당 조각을 빠르게 건너 뛸 수 있습니다. 여전히 버텍스 쉐이더를 실행해야하지만 일반적으로 더 비싼 프래그먼트 쉐이더 계산을 건너 뛸 수 있습니다.

이것은 코딩하기 쉽고 시각화하기가 더 쉽습니다. 또한 일부 하드웨어 (주로 모바일 및 임베디드 GPU)에서는 다른 하드웨어보다 효율적일 수 있습니다. 그러나 고급 하드웨어에서는 대체적으로 조명이 많은 장면에 적합합니다.

지연 렌더링

지연 렌더링은 조금 더 복잡합니다.

표면의 점에 대한 빛을 계산하는 데 사용하는 조명 방정식은 다음 표면 매개 변수를 사용합니다.

  • 표면 위치
  • 표면 법선
  • 표면 확산 색상
  • 표면 스페 큘러 컬러
  • 표면 경면 광택
  • 다른 표면 매개 변수 (조명 방정식이 얼마나 복잡한 지에 따라 다름)

포워드 렌더링에서이 매개 변수는 정점 셰이더에서 직접 전달되거나 텍스처에서 가져 오거나 (일반적으로 정점 셰이더에서 전달 된 텍스처 좌표를 통해) 프래그먼트 셰이더의 조명 함수에 도달합니다. 다른 매개 변수. 확산 색은 버텍스 당 색을 텍스처와 결합하거나 여러 텍스처를 결합하여 계산할 수 있습니다.

지연 렌더링에서는이를 모두 명시 적으로 만듭니다. 첫 번째 단계에서는 모든 객체를 렌더링합니다. 그러나 우리는 색상을 렌더링하지 않습니다 . 대신 표면 매개 변수를 렌더링 합니다. 따라서 화면의 각 픽셀에는 표면 매개 변수 세트가 있습니다. 이것은 오프 스크린 텍스처로의 렌더링을 통해 이루어집니다. 하나의 텍스처는 확산 색상을 RGB로 저장하고 스페 큘러 광택을 알파로 저장합니다. 다른 텍스처는 반사 색상을 저장합니다. 세 번째는 법선을 저장합니다. 등등.

위치는 일반적으로 저장되지 않습니다. 대신 두 번째 단계에서 수학으로 재구성되어 여기에 들어가기가 너무 복잡합니다. 말하자면, 깊이 버퍼와 화면 공간 조각 위치를 입력으로 사용하여 표면에있는 점의 카메라 공간 위치를 알아냅니다.

이제이 텍스처는 장면에서 보이는 모든 픽셀에 대한 모든 표면 정보를 기본적으로 보유하므로 전체 화면 쿼드 렌더링을 시작합니다. 각 라이트는 전체 화면 쿼드 렌더를 얻습니다. 표면 매개 변수 텍스처에서 샘플링 한 다음 위치를 재구성 한 다음 해당 광원의 기여도를 계산하는 데 사용합니다. glBlendFunc(GL_ONE, GL_ONE)이미지 에 (다시 ) 추가 됩니다. 우리는 빛이 다 떨어질 때까지 이것을 계속합니다.

HDR은 다시 프로세스 후 단계입니다.

지연 렌더링의 가장 큰 단점은 앤티 앨리어싱입니다. 앤티 앨리어싱을 제대로 수행하려면 약간 더 많은 작업이 필요합니다.

GPU에 많은 메모리 대역폭이있는 경우 가장 큰 장점은 성능입니다. 실제 지오메트리를 한 번만 렌더링합니다 (또는 그림자 매핑을 수행하는 경우 그림자가있는 조명 당 1 + 1). 우리는 결코 이 후 보이지 않는 숨겨진 픽셀 구조에 모든 시간을 할애하지 않는다. 모든 조명 통과 시간은 실제로 보이는 것들에 소비됩니다.

GPU에 많은 메모리 대역폭이 없으면 광선 통과가 실제로 다칠 수 있습니다. 화면 픽셀 당 3-5 개의 텍스처를 가져 오는 것은 재미 있지 않습니다.

가벼운 프리 패스

이것은 흥미로운 트레이드 오프가있는 지연 렌더링의 변형입니다.

지연 렌더링과 마찬가지로 표면 매개 변수를 일련의 버퍼로 렌더링합니다. 그러나 표면 데이터는 약식입니다. 이 시간에 관심이있는 유일한 표면 데이터는 깊이 버퍼 값 (위치를 재구성하기위한), 법선 및 반사광입니다.

그런 다음 각 조명에 대해 조명 결과 만 계산합니다. 표면 색상과의 곱셈은 없습니다. 표면 색상이 전혀없는 점 (N, L)과 정반사 만 사용합니다. 정반사와 확산 항은 별도의 버퍼에 보관해야합니다. 각 라이트에 대한 반사 및 확산 항은 두 버퍼 내에서 요약됩니다.

그런 다음 전체 스페 큘러 및 확산 조명 계산을 사용하여 지오메트리를 다시 렌더링하여 표면 색상과 최종 조합하여 전체 반사율을 생성합니다.

여기서 단점은 멀티 샘플링을 다시 얻는 것입니다 (적어도 지연 된 것보다 쉽습니다). 순방향 렌더링보다 객체 별 렌더링이 적습니다. 그러나 이것이 제공한다고 지연되는 가장 중요한 것은 다른 표면에 대해 다른 조명 방정식을 갖는 더 쉬운 시간입니다.

지연 렌더링을 사용하면 일반적으로 조명마다 동일한 쉐이더로 전체 장면을 그립니다. 따라서 모든 객체는 동일한 재질 매개 변수를 사용해야합니다. 라이트 프리 패스를 사용하면 각 오브젝트에 서로 다른 셰이더를 제공하여 자체적으로 최종 조명 단계를 수행 할 수 있습니다.

이것은 포워드 렌더링 케이스만큼 많은 자유를 제공하지 않습니다. 그러나 여분의 텍스처 대역폭이 있다면 여전히 더 빠릅니다.


-1 : LPP / PPL을 언급하지 못했습니다. -1 지연 : 렌더링은 모든 DX9.0 하드웨어에서 즉시 승리합니다 ( '비즈니스'랩탑에서도 가능)-2009 년경의 기본 요구 사항입니다. DX8.0을 대상으로하지 않는 한 (Deferred / LPP를 수행 할 수 없음) 지연 / LPP가 기본값 입니다. 마지막으로 '많은 메모리 대역폭'은 미쳤습니다. 우리는 일반적으로 PCI-X x4를 포화 시키지도 않고 LPP는 메모리 대역폭을 크게 떨어 뜨립니다. 마지막으로 귀하의 의견에 -1; 이런 식으로 루프? 이 루프가 프레임 당 2073600 번 발생한다는 것을 알고 있습니까? 그래픽 카드의 상호 관계에도 불구하고 나쁘다.
조나단 디킨슨

1
@JonathanDickinson 그의 요점은 지연 / 라이트 프리 패스의 메모리 대역폭이 일반적으로 순방향 렌더링보다 몇 배 더 크다는 것입니다. 지연된 접근 방식은 무효화되지 않습니다. 선택할 때 고려해야 할 사항입니다. BTW : 지연된 버퍼는 비디오 메모리에 있어야하므로 PCI-X 대역폭은 관련이 없습니다. 중요한 것은 GPU의 내부 대역폭입니다. 롤링되지 않은 루프와 같은 긴 픽셀 셰이더는 유용한 작업을하고 있는지에 대해 놀라지 않아도됩니다. z- 버퍼 프리 패스 트릭에는 아무런 문제가 없습니다. 잘 작동합니다.
Nathan Reed

3
@JonathanDickinson : WebGL에 대한 이야기이므로 "셰이더 모델"에 대한 모든 논의는 관련이 없습니다. 그리고 어떤 종류의 렌더링을 사용하는지는 "종교적인 주제"가 아닙니다. 그것은 단순히 어떤 하드웨어를 사용하고 있는지의 문제입니다. "비디오 메모리"가 일반 CPU RAM 인 내장 GPU는 지연 렌더링에서 매우 나쁘게 작동합니다. 모바일 타일 기반 렌더러에서는 훨씬 더 나쁩니다 . 지연 렌더링은 하드웨어와 상관없이 "즉시 승리"가 아닙니다. 하드웨어와 마찬가지로 장단점이 있습니다.
Nicol Bolas

2
@JonathanDickinson : "또한 z- 버퍼 프리 패스 트릭을 사용하면 그려야 할 객체와 z- 파이팅을 제거하기 위해 고군분투 할 것입니다." 그건 말도 안돼 동일한 변환 매트릭스와 동일한 정점 셰이더를 사용하여 동일한 객체를 렌더링합니다. 부두에서 1 일 동안 멀티 패스 렌더링을 수행했습니다 . 이것은 해결 된 문제입니다. 조명을 축적해도 아무런 변화가 없습니다.
Nicol Bolas

8
@JonathanDickinson :하지만 우리는 와이어 프레임 렌더링에 대해 이야기하고 있지 않습니다. 우리는 이전 과 같은 삼각형 을 렌더링하는 것에 대해 이야기하고 있습니다. OpenGL 은 동일한 버텍스 셰이더를 사용하는 한 동일한 객체에 대해 불일치를 보장 합니다 (물론 invariant다른 경우에도이를 보장 하는 키워드가 있습니다).
Nicol Bolas

4

디퍼 드 렌더링 또는 프리 패스 조명 을 사용해야 합니다. 이전의 고정 기능 파이프 라인 (읽기 : 쉐이더 없음) 중 일부는 최대 16 개 또는 24 개의 라이트를 지원 했지만 그게 전부 입니다. 지연 렌더링은 조명 제한을 제거합니다. 그러나 훨씬 더 복잡한 렌더링 시스템이 필요합니다.

분명히 WebGL은 모든 형태의 지연 렌더링에 절대적으로 필요한 MRT를 지원 하므로 가능할 수 있습니다. 나는 그것이 얼마나 그럴듯한 지 잘 모르겠다.

또는 즉시 렌더링을 연기 한 Unity 5를 조사 할 수 있습니다.

이 문제를 해결하는 또 다른 간단한 방법은 조명의 우선 순위를 정하는 것입니다 (플레이어와의 거리 및 카메라 절두체에 있는지 여부에 따라 다름). 상단 8 만 사용하도록 설정하는 것입니다. 출력 품질 (예 : Far Cry 1)

미리 계산 된 라이트 맵 을 볼 수도 있습니다 . Quake 1과 같은 게임은 이것들로부터 많은 마일리지를 얻었습니다. 그리고 그것들은 아주 작을 수 있습니다 (쌍 선형 필터링은 늘어난 라이트 맵을 아주 부드럽게 부드럽게합니다). 불행하게도 제외 100 %의 동적 조명의 개념을 미리 계산하지만 정말 표정 않는 위대한 . 이를 8 개의 라이트 제한과 결합 할 수 있습니다. 예를 들어 로켓이나 그와 같은 조명은 실제 조명이되지만 벽면의 조명은 라이트 맵이됩니다.

참고 : 셰이더에서 루프를 반복하지 않겠습니까? 공연에 작별 인사를하십시오. GPU로는 되지 는 CPU 예를 들어, 자바 스크립트가하는, 같은 방식으로 작동하도록 설계되지 않았습니다. 렌더링하는 각 픽셀 (덮어 쓰기 된 경우에도)은 루프를 수행해야합니다. 따라서 1920x1080에서 실행하고 16 회 실행하는 간단한 루프를 실행하면 해당 루프 내부의 모든 항목을 33177600 번 효과적으로 실행합니다. 그래픽 카드는 많은 조각을 병렬로 실행하지만 해당 루프는 여전히 오래된 하드웨어를 사용합니다.


-1 : "지연 렌더링을 사용해야합니다"이것은 사실이 아닙니다. 지연 렌더링은 확실히 방법이지만, 유일한 방법 은 아닙니다 . 또한 루프는 특히 균일 한 값을 기반으로하는 경우 (예 : 각 조각의 루프 길이가 다름) 성능면에서 나쁘지 않습니다.
Nicol Bolas

1
네 번째 단락을 읽으십시오.
조나단 디킨슨

2

n 조명을 지원하는 픽셀 셰이더 (여기서 n은 4 또는 8과 같은 작은 숫자)를 사용하고 장면을 여러 번 다시 그려 매번 새 조명 배치를 전달하고 추가 블렌딩을 사용하여 조명을 모두 결합 할 수 있습니다.

이것이 기본 아이디어입니다. 물론 적당한 크기의 장면을 위해 이것을 충분히 빠르게하기 위해 많은 최적화가 필요합니다. 모든 라이트를 그리지 말고 가시 광선 (절두체와 폐색 컬링) 만 그립니다. 실제로 각 패스마다 전체 장면을 다시 그리지 말고 해당 패스의 조명 범위 내에있는 오브젝트 만 다시 그리십시오 . 다양한 수의 조명 (1, 2, 3, ...)을 지원하는 여러 버전의 셰이더가 있으므로 필요한 것보다 많은 조명을 평가하는 데 시간을 낭비하지 않아도됩니다.

다른 답변에 언급 된 지연 렌더링은 작은 조명이 많을 때 좋은 선택이지만 유일한 방법은 아닙니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.