[GPU프로그래밍] 14. Particle Systems and Animation

jungizz_·2024년 6월 13일
0

GPU Programming

목록 보기
14/15
post-thumbnail
  • 애니메이션 연산을 GPU(shader)에서해서 빠르게 하기
    • vertex position의 계산 및 transform
  • 다음 프레임에 업데이트 되는 버텍스 데이터를 전달하기 위해 정보를 저장해야함
    • shader의 output을 바로 buffer에 저장할 수 있는 Transform feedback 사용

Animating a surface

  • vs에서 시간에 따른 함수로 vertex position을 계산하여 transform
    • xz평면의 쿼드의 y좌표를 시간에 따른 sine wave로 계산
    • 이에 따라 바뀌는 노멀벡터도 편미분으로 계산

OpenGL Application

  • xz평면에 여러개로 쪼개진(tesselated) 평면 데이터 세팅
    • 삼각형이 많을수록 (많이 쪼갤수록) 애니메이션이 자연스럽다
  • 시간 및 sine함수의 매개변수를 uniform 변수로 설정

vertex shader

  • 해당 vertex에 대한 시간에 따른 y값 계산
  • GPU의 데이터가 CPU에 필요한 경우
    • 예를 들어 collision detection의 경우, 모든 버텍스의 updated position을 사용해 매 프레임마다 추가적인 계산이 필요 -> CPU에서 계산해야함
    • GPU의 데이터를 버퍼 등에 저장해서 CPU 전달해줘야함

Particle

  • 형태가 고정되지 않은 것들을 시뮬레이션할 때 사용 (fuzzy things - smoke, luquid spray, fire, ...)
  • 각 파티클은 포인트GL-POINTS를 사용
    • 시뮬레이션 시 크기는 고려하지 않음
    • 보통 transparency를 가지는 single,textured,camera-facing quad로 렌더링해서 형태를 만듦
  • lifetime을 가짐 (born->animates->dies)
    • 시간/위치에 도달함에 따라 dead 결정
    • dead 파티클은 recycle될 수 있음
  • lifetime동안 보여지는 방식(shape, transparency)을 바꿀 수 있음
  • 파티클끼리의 인터렉션과 빛 반사는 가정안함 (적어도 이번 챕터에서는..)
  • 파티클은 basic kinematic equations(속도, 가속도 등)에 따라 애니메이션되고, 외부 요인(바람, 마찰 등)도 고려

1. Particle fountain

  • 한 지점으로 부터 분수형태로 파티클 뿜어내기
    • 파티클 recycle은 안하고 시간 지나면 사라짐
    • 각 파티클의 시작 위치는 같고, 약간의 시간차/속도차를 두고 생성된다

Instancing ✨

  • gs에서 파티클을 생성하는게 아니라서 Instancing 사용
  • 한 파티클은 6개의 버텍스로 이루어진 쿼드로 나타나는데, 모든 파티클마다의 버텍스를 버퍼에 저장하는게 아닌, 6개의 버텍스만 저장하고 glDrawArrayInstanced로 그 버퍼가 파티클 개수만큼 반복해서 그려지도록 한다
  • 한 파티클마다 6번의 vs가 실행되고, vs에서 파티클 위치를 기준으로 쿼드의 버텍스 위치를 계산 (offset)
    • 근데 강노에서는 버텍스 버퍼가 필요업다하고, 저 함수는 버퍼를 반복해서 그린다하고... 모르겠음!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  • particle의 position이 저장된 버퍼의 attribute index는 한 Instance가 그려지면 증가
    • attribute index 증가 = index advance
    • 한 Instancing = 6번의 vertex shader 실행
  • 이를 세팅하기 위해 glVertexAttribDivisor로 divisor값을 설정
    • divisor=n: n번의 instance 후 index advance
    • divisor=0: 한 vertex shader 실행될 때마다 index advance (default)
  • glDrawArrayInstancedglVertexAttribDivisor의 예시

📃 Code

OpenGL Application

  • 2개의 버퍼 (초기 속도, 시작 시간)
    • 속도와 시간으로 파티클의 위치 계산할 것
  • 초기 속도 버퍼는 특정 범위(cone) 내에서의 랜덤 방향과 랜덤 크기로 결정
  • 시작 버퍼는 각 파티클이 생성될 시간의 간격을 구한 뒤, 몇번째 파티클인지에 따라 i를 곱해서 시간 결정
  • Instancing 설정으로 파티클 개수만큼 해당 Vertex Array가 그려지도록 한다
  • 두 버퍼의 divisor는 1로 설정해서 한 instance가 그려져야 index가 증가되도록 설정
  • transparancy를 사용하기 위해 blend enable

vertex shader

  • 해당 vertex가 한 particle에서 몇 번째인지 gl-VertexID를 통해 알아낸 뒤, 해당하는 index의 offset과 texcoord 정보를 적용
  • particle이 살아있을 때, 위치와 투명도 정보를 계산

fragment shader

  • texture 및 투명도 설정
  • 시간에 따라 파티클에 주어지는 힘이 달라진다면 운동 방정식에서의 a에 변화가 생기는 것으로, 직전 프레임의 데이터가 필요하다
    -> Transform feedback

Transform feedback

  • vs, gs의 output을 버퍼에 저장해서 다음 pass에서 사용하는 방법
    • 파티클과 같은 discrete simulaton에 적합 (연속적인 과정을 시간 단계로 나눠 업데이트하는 방식)
  • vs에서 계산한 파티클의 위치를 버퍼에 저장하고, 이 값을 다음 pass의 input으로 사용해 위치를 update (이 과정을 반복)

Euler method

  • 현재 프레임의 파티클 정보로 다음 프레임의 파티클을 계산하기 위해 euler method 사용

Buffer ping-ponging

  • euler method를 사용하여 다음 프레임 데이터를 계산하기 위해 2개의 버퍼가 필요하고, 2pass를 거친다
  • 1pass (Update pass)
    • no rasterization (그림을 그리는게 아닌, position 계산이 목적이므로)
    • A버퍼로 계산한 다음 프레임 데이터를 output으로 B버퍼에 저장
  • 2pass (Render pass)
    • 1pass에서 계산 완료된 B버퍼를 사용해 렌더링
  • 다음 프레임에서는.. B버퍼가 1pass의 input이 되고, A버퍼에 output이 저장

Store Shader output to Vertex Buffer

  • shader의 output을 vertex buffer에 담아야 다음 프레임에서의 shader input으로 사용할 수 있음
    • 이 과정을 transform feedback이 해줌
  • glTransformFeedbackVarying: (numbering된) 각 attribute에 해당하는 buffer와 shader의 output을 연결
    • 각 attribute에 해당하는 버퍼에 numbering은 glBindBase로 설정
  • 렌더링 과정에서는, glBeginTransformFeedbackglEndTransformFeedback 사이에 shader input으로 사용할 vertex array 바인딩해서 point primitive를 그림
❗위 내용은 아래 코드 보고 오는게 이해 더 잘 될 듯!

📃 Code

OpenGL Application

  • attribute가 3개이므로, 3개 쌍의 버퍼 생성
    • attribute: 파티클 위치, 속도, 시간(나이)
    • 나중에 계산되므로 처음에 만들 때는 넣어줄 값이 없어서 크기만 지정(age만 지정해줌)
  • 한 쌍의 vertex array도 만들고, 버퍼 한 세트씩 묶기
  • shader의 output이 알맞는 attribute의 버퍼로 연결될 수 있도록 numbering
    • ageBuf의 초기값은 전부 음수, 초기 속도는 랜덤 설정 (전체 코드 참고)
  • numbering된 각 attribute에 해당하는 buffer와 shader의 output을 연결
  • Render함수에서 pass에 따른 세팅
    • pass1 -> rasterizer를 disable, TransformFeedback으로 shader output을 버퍼에 연결, divisor=0으로 설정(particle별 계산이니까), 다 끝나면 rasterizer enable
    • pass2 -> divisor=1로 설정, buffer swap

Vertex shader

  • pass1에서는 이전 프레임 데이터를 사용해서 파티클의 새로운 위치, 속도, 나이 값 계산
  • pass2에서는 위치와 투명도 계산
  • shader output을 저장하기 위해 image load/store 또는 shader storage buffer objects 등을 사용할 수도 있다
  • compute shader를 사용하면 1pass로도 가능하다

Query

  • 사용자가 요청한 특정 데이터를 보여달라는 요청
  • ex) TransformFeedback의 결과를 쿼리하고 싶을 때
    • 쿼리 오브젝트 생성
    • TransformFeedback 수행 전 glBeginQuery 로 어떤걸 쿼리할지 지정
      • TransformFeddback으로 생성된 primitive의 개수
    • TransformFeedback이 완료된 후 glEndQuery
    • 쿼리 오브젝트에 답변이 저장되고, 그 데이터를 받아와 출력해서 확인

Instanced meshes

  • 점 대신 mesh data를 Instance
    • glDrawArraysInstance 대신 glDrawElementsInstanced
  • 메시를 인스턴싱 하는거므로 더 많은 정보 필요 -> 6개의 attribute
    • 파티클 버퍼 4개쌍
    • 메시를 그리기 위한 메시의 버텍스 버퍼 2개
AttributesdataDivisor
0Mesh vertex position0
1Mesh vertex normal0
2texture coordinate (필요한 경우)
3particle positionrendering때는 1, update때는 0
4particle velocityrendering때는 1, update때는 0
5particle agerendering때는 1, update때는 0
6particle rotationrendering때는 1, update때는 0
  • 4개의 버퍼쌍: 파티클 위치, 속도, 나이, 회전
  • 메시의 각 버텍스마다의 위치와 노멀 attribute도 필요 -> divisor=0

vertex shader

  • Rotation의 x는 회전되어있는 정도(회전각), y는 매 프레임마다 회전할 정도(속도?)
  • ParticleRotation도 정확하진 않지만 x가 회전각, y는 회전 속도
  • 회전각에 회전할 정도 더해서 새로운 회전 각 계산하고, 그 값으로 cos, sine값을 계산하여 얻은 rotation matrix를 mesh 위치/노멀에 곱한다

OpenGL Application

  • 1pass에선 렌더링하지 않고 계산만 하므로 vertex attribute array disable 및 divisor=0으로 설정
  • 2pass에선 enable 및 divisor=1로 설정
  • gl_InstanceID라는 빌트인 변수를 사용하여 각 인스턴스마다 다른 값을 부여할 수 있다
    • ex) 각 인스턴스마다 랜덤값

2. Fire

  • 불의 파티클 시스템
    • 약간의 중력 적용
    • 위로 향하는 가속도
    • 불꽃 텍스처
    • 일직선 상의 랜덤한 초기 위치 설정

OpenGL Application

  • Transform feedback 사용
  • 가속도, lifetime, texture, particle size 세팅
  • 랜덤 값을 설정하기 위한 Random texture

vertex shader

  • 초기 속도 랜덤 설정
    • x, z는 0, y값만 랜덤 설정
  • 초기 위치 랜덤 설정
    • 랜덤 x값으로 emitter position(파티클 생성 위치)의 offset 설정

fragment shader

  • 검은 연기를 표현하기 위해, 태어난 시간이 지남에 따라 검은색 blend
  • 바람이 불어 불꽃이 다른 방향으로 움직이게 하려면
    • 가속도 값을 다른 방향으로 조절하면 됨

3. Smoke

  • 연기의 파티클 시스템
    • 위로 갈수록 퍼지는 효과를 위해 파티클 크기를 키우기
    • 위로 향하는 가속도
    • 연기 텍스처

OpenGL Application

  • Transform feedback 사용
  • 가속도, lifetime, texture, particle size 세팅
  • particle size는 변화하므로 mix/max값 지정

vertex shader

  • 버텍스 생존 시간에 따라 파티클 사이즈 조절
profile
( •̀ .̫ •́ )✧

0개의 댓글