현대적인 방법 인 OpenGL로 많은 타일 그리기


35

팀원들과 함께 작은 타일 / 스프라이트 기반 PC 게임을하고 있는데 성능 문제가 있습니다. OpenGL을 마지막으로 사용한 시간은 2004 년경 이었으므로 핵심 프로필을 사용하는 방법을 스스로 가르쳐 왔으며 약간 혼란스러워하고 있습니다.

250-750 48x48 타일 근처에 매 프레임마다 화면에 그리고 약 50 개의 스프라이트를 그려야합니다. 타일은 새 레벨이로드 될 때만 변경되며 스프라이트는 항상 변경됩니다. 타일 ​​중 일부는 4 개의 24x24 조각으로 구성되며 대부분의 스프라이트는 타일과 크기가 같습니다. 많은 타일과 스프라이트는 알파 블렌딩을 사용합니다.

지금은이 모든 작업을 즉각적인 모드로 수행하고 있는데, 이는 나쁜 생각입니다. 마찬가지로 우리 팀원 중 한 명이이를 실행하려고하면 프레임 속도가 매우 약해 (~ 20-30fps), 타일이 많을수록, 특히 타일이 많을수록 더 나빠집니다. 조각으로 잘립니다. 이로 인해 문제가 발생하는 횟수가 문제라고 생각합니다.

나는 이것에 대한 몇 가지 가능한 해결책을 생각했지만, 내가 말하는 것에 대해 알고있는 사람들이 솔루션을 실행하고 싶었 기 때문에 바보 같은 것에 시간을 낭비하지 않았습니다.

타일 ​​:

  1. 레벨이로드되면 모든 타일을 한 번 큰 honking 텍스처에 연결된 프레임 버퍼에 한 번 그리고 프레임마다 그 텍스처로 큰 사각형을 그립니다.
  2. 레벨이로드 될 때 모든 타일을 정적 정점 버퍼에 넣고 그런 식으로 그립니다. glDrawElements를 한 번만 호출하여 다른 질감의 객체를 그리는 방법이 있는지, 심지어 내가하고 싶은 일인지 모르겠습니다. 어쩌면 모든 타일을 큰 거대한 텍스처에 넣고 VBO에서 재미있는 텍스처 좌표를 사용합니까?

SPRITES :

  1. glDrawElements를 별도로 호출하여 각 스프라이트를 그립니다. 이것은 많은 텍스처 전환과 관련이있는 것 같습니다. 여기서 텍스처 배열이 유용할까요?
  2. 어떻게 든 동적 VBO를 사용하십시오. 위의 2 번과 동일한 질감 질문.
  3. 포인트 스프라이트? 이것은 아마도 바보입니다.

이 아이디어 중 어떤 것이 합리적입니까? 내가 볼 수있는 좋은 구현이 있습니까?


타일이 움직이거나 변경되지 않고 전체 레벨과 동일한 방식으로 보이는 경우 첫 번째 아이디어 인 프레임 버퍼를 사용해야합니다. 가장 효율적입니다.
zacharmarz

텍스처 아틀라스를 사용하여 텍스처를 전환 할 필요는 없지만 다른 모든 것을 동일하게 유지하십시오. 이제 프레임 속도는 어떻습니까?
user253751

답변:


25

타일을 렌더링하는 가장 빠른 방법은 정점 데이터를 인덱스가있는 정적 VBO (glDrawElements가 표시)로 압축하는 것입니다. 다른 이미지에 기록하는 것은 완전히 불필요하며 더 많은 메모리가 필요합니다. 텍스처 전환은 매우 비용이 많이 들기 때문에 모든 타일을 소위 텍스처 아틀라스 에 넣고 VBO의 각 삼각형에 올바른 텍스처 좌표를 부여 할 수 있습니다. 이를 바탕으로 하드웨어에 따라 1000 개, 심지어 100000 개의 타일을 렌더링하는 것이 문제가되지 않습니다.

타일 ​​렌더링과 스프라이트 렌더링의 유일한 차이점은 스프라이트가 동적이라는 것입니다. 따라서 최고의 성능을 제공하지만 쉽게 달성 할 수있는 성능을 위해서는 스프라이트 정점에 대한 좌표를 스트림마다 VBO를 그리고 glDrawElements를 사용하여 그릴 수 있습니다. 또한 모든 텍스처를 Texture Atlas에 포장하십시오. 스프라이트가 거의 움직이지 않는다면, 스프라이트가 움직일 때 동적 VBO를 만들어 업데이트 할 수도 있지만 스프라이트 만 렌더링하고 싶기 때문에 여기에 총 오버 킬이 있습니다.

당신은 OpenGL을로 C ++로 만든 작은 프로토 타입 I 볼 수 있습니다 : 미립자를

나는 일반적인 컴퓨터 (Quad Core @ 2.66GHz)에서 평균 fps 400으로 약 10000 포인트 스프라이트를 렌더링합니다. CPU가 제한되어있어 그래픽 카드가 훨씬 더 렌더링 할 수 있습니다. 파티클에 단일 텍스처 만 있기 때문에 여기서는 텍스처 아틀라스를 사용하지 않습니다. 파티클은 GL_POINTS로 렌더링되고 쉐이더는 실제 쿼드 크기를 계산하지만 쿼드 렌더러도 있다고 생각합니다.

네, 정사각형이없고 텍스처 매핑에 쉐이더를 사용하지 않는 한 GL_POINTS는 매우 바보입니다. ;)


스프라이트는 위치와 사용중인 텍스처를 변경하며 대부분 프레임마다이 작업을 수행합니다. 또한 스프라이트는 매우 자주 생성되고 파괴됩니다. 스트림 드로우 VBO가 처리 할 수있는 것들이 있습니까?
Nic

2
스트림 그리기는 기본적으로 "이 데이터를 그래픽 카드로 보내고 그리기 후에 버립니다"를 의미합니다. 따라서 매 프레임마다 데이터를 다시 보내야하므로 렌더링하는 스프라이트 수, 위치, 텍스처 좌표 또는 색상이 중요하지 않습니다. 그러나 모든 데이터를 한 번에 보내고 GPU가 즉시 모드보다 훨씬 빠릅니다.
Marco

이 모든 것이 의미가 있습니다. 이것에 인덱스 버퍼를 사용하는 것이 가치가 있습니까? 반복 될 정점은 모든 사각형의 두 모서리입니다. (내 이해는 인덱스가 glDrawElements와 glDrawArrays의 차이라는 것입니다. 맞습니까?)
Nic

1
인덱스가 없으면 GL_TRIANGLES를 사용할 수 없습니다. GL_TRIANGLES는이 그리기 방법이 최상의 성능을 보장하는 방법이므로 일반적으로 나쁩니다. 또한 GL_QUADS 구현은 OpenGL 3.0에서 더 이상 사용되지 않습니다 (source : stackoverflow.com/questions/6644099/… ). 삼각형은 모든 그래픽 카드의 기본 메쉬입니다. 따라서 2 개의 정점 셰이더 실행과 vertex_size * 2 바이트를 저장하기 위해 2 * 6 바이트 더 "사용"합니다. 따라서 일반적으로 항상 더 좋다고 말할 수 있습니다.
Marco

2
Particulate에 대한 링크가 죽었습니다 ... 새로운 것을 제공해 주시겠습니까?
SWdV

4

직접 모드가 느릴 수 있지만 그렇지 않다 - 무승부의 수를 호출해도 당신은 성능 저하의 종류를보고하지 않아야 한다는 느린 (참조, 심지어 사랑하는짜리 지진이 떨어지지 않고 프레임 당 수천 즉시 모드 통화를 관리 할 수 있습니다 너무 심하게).

나는 여기에 더 흥미로운 일이 있다고 생각합니다. 가장 먼저해야 할 일은 프로그램을 프로파일 링하는 데 시간을 투자하는 것입니다. 그렇지 않으면 성능 향상이 전혀 없을 수 있다는 가정에 따라 재구성 할 위험이 큽니다. 따라서 GLIntercept와 같은 기본적인 것을 통해 실행하고 시간이 어디로 가는지 확인하십시오. 그 결과를 바탕으로 주요 병목 현상에 대한 실제 정보를 통해 문제를 해결할 수 있습니다.


성능 문제가 개발과 동일한 시스템에서 발생하지 않기 때문에 약간의 프로파일 링을 수행했지만 어색합니다. 나는 타일 수에 따라 문제가 확실히 증가하기 때문에 문제가 다른 곳에 있다는 것에 회의적입니다.
Nic

그러면 상태 변경은 어떻습니까? 상태별로 불투명 타일을 그룹화하고 있습니까?
Maximus Minimus

그럴 가능성이 있습니다. 이것은 내 부분에 더 많은 관심을 기울일 가치가 있습니다.
Nic

2

좋아, 마지막 대답이 여기에서 나왔기 때문에 새로운 것이 더 유용 할 것입니다.


2D 성능 정보

먼저 일반적인 조언 : 2D는 현재 하드웨어를 요구하지 않으며, 최적화되지 않은 코드조차도 작동합니다. 그렇다고해서 중간 모드를 선택해야한다는 의미는 아닙니다. 최소한 동일한 텍스처가 이미 바인딩되어있을 때 glBindTexture로 새 텍스처를 바인딩하지 마십시오 (예 : CPU의 체크가 톤인 경우). glBindTexture-call보다 빠르며 glVertex와 같이 완전히 잘못되고 어리석은 것을 사용하지 마십시오 (glDrawArrays도 훨씬 빠르며 사용하기가 어렵지 않지만 "현대"는 아닙니다). 이 두 가지 매우 간단한 규칙을 사용하면 프레임 시간이 10ms (100fps) 이상이어야합니다. 이제 더 빠른 속도를 얻으려면 다음 논리적 단계는 일괄 처리입니다. 예를 들어 많은 드로우 콜을 하나로 묶습니다. 텍스처 아틀라스 구현을 고려해야합니다. 텍스처 바인딩의 양을 최소화하여 한 번의 호출로 많은 양의 사각형을 그릴 수 있습니다. 지금 약 2ms (500fps)까지 떨어지지 않으면 잘못된 일이 있습니다. :)


타일 ​​맵

타일 ​​맵에 대한 드로잉 코드를 구현하면 유연성과 속도의 균형을 찾을 수 있습니다. 정적 VBO를 사용할 수 있지만 애니메이션 타일에서는 작동하지 않거나 각 프레임마다 정점 데이터를 생성하고 위에서 설명한 규칙을 적용 할 수 있습니다. 이는 매우 유연하지만 빠르지는 않습니다.

이전 답변에서 프래그먼트 셰이더가 전체 텍스처링을 처리하는 다른 모델을 소개했지만 의존적 인 텍스처 조회가 필요하므로 다른 방법만큼 빠르지 않을 수 있음을 지적했습니다. (아이디어는 기본적으로 타일 지표 만 업로드하고 프래그먼트 셰이더에서 텍스처 좌표를 계산하여 하나의 직사각형으로 전체 맵을 그릴 수 있음을 의미합니다)


스프라이트

스프라이트는 "2D 성능 정보"섹션에서 논의 된 것을 제외하고는 많은 유연성이 필요하므로 최적화하기가 매우 어렵습니다. 그리고 화면에 수만 개의 스프라이트를 동시에 원하지 않으면 노력할 가치가 없을 것입니다.


1
그리고 수만 개의 스프라이트가 있더라도 현대 하드웨어는 적절한 속도로 실행해야합니다. :
Marco

@ API-Beast 무엇을 기다려? 프래그먼트 셰이더에서 Texture UV를 어떻게 계산합니까? Arent UV 조각을 조각 쉐이더로 보내야합니까?
HgMerk

0

모두 실패하면 ...

플립 플롭 드로잉 방법을 설정하십시오. 한 번에 다른 모든 스프라이트 만 업데이트하십시오. VisualBasic6 및 간단한 비트 블리트 메서드를 사용해도 프레임 당 수천 개의 스프라이트를 능동적으로 그릴 수 있습니다. 스프라이트를 그리는 직접적인 방법이 실패한 것처럼 보이기 때문에 이러한 방법을 살펴 봐야 할 것입니다. "렌더링 방법"을 사용하는 것처럼 보이지만 "게임 방법"처럼 사용하려고합니다. 렌더링은 속도가 아니라 선명도에 관한 것입니다.

기회는 계속해서 전체 화면을 계속해서 다시 그리는 것입니다. 변경된 영역 만 다시 그리는 대신. 그것은 많은 오버 헤드입니다. 개념은 간단하지만 이해하기 쉽지 않습니다.

처녀 정적 배경에 버퍼를 사용하십시오. 화면에 스프라이트가없는 한 자체 렌더링되지 않습니다. 이것은 스프라이트가 그려진 곳을 "복귀"하고 다음 호출에서 스프라이트를 뽑는 데 지속적으로 사용됩니다. 또한 화면이 아닌 "그리기"버퍼가 필요합니다. 거기에 그림을 그리고 나서 일단 그리면 화면에 한 번 뒤집습니다. 모든 스프라이트 당 한 번의 스크린 콜이어야합니다. (화면에 각 스프라이트를 한 번에 하나씩 그리거나 한 번에 모두 시도하면 알파 블렌딩이 실패합니다.) 메모리에 쓰기가 빠르며 "그리기 위해 화면 시간이 필요하지 않습니다. ". 각 드로우 콜은 리턴 신호를 기다렸다가 다시 그립니다. (실제 하드웨어 틱인 v-sync가 아니며 RAM의 대기 시간보다 훨씬 느립니다.)

나는 이것이 한 컴퓨터에서만이 문제를 보는 이유의 일부라고 생각합니다. 또는 모든 카드가 지원하지 않는 ALPHA-BLEND의 소프트웨어 렌더링으로 돌아가고 있습니다. 해당 기능을 사용하기 전에 해당 기능이 하드웨어를 지원하는지 확인합니까? 알파 블렌드 모드가 아닌 경우 폴백 (비 알파 블렌드 모드)이 있습니까? 분명히 게임 콘텐츠를 저하시킬 것이라고 가정하기 때문에 (혼합물 수)를 제한하는 코드가 없습니다. (이들이 모두 알파 블렌딩 된 파티클 효과와 달리, 프로그래머가 하드웨어 지원이 있더라도 대부분의 시스템에서 많은 부담을지고 있기 때문에 프로그래머가 제한하는 이유는 아닙니다.)

마지막으로, 알파 블렌딩하는 것을 필요한 것만으로 제한하는 것이 좋습니다. 모든 것이 필요한 경우 ... 사용자는 더 나은 하드웨어 요구 사항을 요구하거나 원하는 성능을 위해 게임 성능을 저하시킬 수밖에 없습니다.


-1

다른 2D 게임에서와 같이 오브젝트 용 스프라이트 시트와 터 레인 용 타일 세트를 만들면 텍스처를 전환 할 필요가 없습니다.

각 삼각형 쌍마다 고유 한 텍스처 좌표가 필요하기 때문에 렌더링 타일은 고통 스러울 수 있습니다. 그러나이 문제에 대한 해결책이 있는데이를 인스턴스 렌더링 이라고 합니다 .

예를 들어 잔디 타일 및 해당 위치의 목록을 가질 수있는 방식으로 데이터를 정렬 할 수있는 한 단일 드로우 콜로 모든 잔디 타일을 렌더링 할 수 있습니다. 배열을 제공하기 만하면됩니다. 각 타일의 모델을 월드 매트릭스로 이 방법으로 데이터를 정렬하는 것은 가장 간단한 장면 그래프에서도 문제가되지 않습니다.


-1 : 인스 턴싱은 비스트 씨의 순수한 쉐이더 솔루션보다 더 나쁜 아이디어입니다. 인스턴스화는 중간 정도의 복잡성 (~ 100 삼각형 정도)의 객체를 렌더링 할 때 성능에 가장 적합합니다. 텍스처 좌표가 필요한 각 삼각형 타일은 문제가되지 않습니다. 타일 ​​맵을 형성하는 느슨한 쿼드로 메쉬를 만듭니다.
Nicol Bolas 2016 년

1
@NicolBolas 좋아, 나는 학습을 위해 답을 떠날거야
dreta

1
명확하게하기 위해, Nicol Bolas,이 모든 것을 다루는 방법에 대한 당신의 제안은 무엇입니까? 마르코의 개울은 일을? 이 구현을 볼 수있는 곳이 있습니까?
Nic

@Nic : 버퍼 객체 로의 스트리밍 은 특별히 복잡한 코드는 아닙니다. 그러나 실제로, 50 개의 스피트 만 이야기한다면, 그것은 아무것도 아닙니다 . 성능 문제를 일으킨 지형 도면이므로 승산이 좋으므로 정적 버퍼로 전환하면 충분할 것입니다.
Nicol Bolas

실제로 인스 턴싱이 예상대로 작동한다면 최상의 솔루션이 될 것입니다. 그러나 그렇지 않기 때문에 모든 인스턴스를 하나의 정적 vbo로 굽는 것이 좋습니다.
Jari Komppa
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.