nvidia 에 올라온 글을 번역한 것입니다. Geforce3 기준으로 되어있습니다.

원문 : http://developer.nvidia.com/view.asp?IO=geforce3_faq

문1) 버텍스 쉐이더 안에서 버텍스를 만들거나 없애는 것이 가능한가요?
답) 아뇨 할 수 없습니다. 버텍스의 구성요소(위치, 색상, 텍스춰 좌표, 등등)를 변경하는 것은 가능합니다만, 버텍스 자체를 더 만들거나 없애는 것은 할 수 없습니다. 그렇지만, 정 원한다면 버텍스를 화면 바깥으로 보내버리거나, 버텍스의 알파값을 0 으로 만들어서 없어진 것과 같은 효과를 내는 쪽을 선택해야 합니다.

문2) 버텍스 쉐이더 안에서 다른 버텍스를 억세스할 수 있나요?
답) 아뇨, 할 수 없습니다. 버텍스 쉐이더는 언제나 단 하나의 버텍스에 대해서만 적용됩니다. 버텍스끼리는 서로 정보를 보거나 상수 메모리에 값을 쓰는 것이 불가능합니다. 그러니까 실질적으로는 이 환경을, 버텍스 버퍼는 볼 수 있지만, 인덱스 버퍼는 볼 수 없는 상태라고 생각하면 됩니다. (역자주 : 나도 이해 안가는 이야기임) 대신, 버텍스당 16 개의 버텍스 속성값을 지정할 수 있으니까 다른 버텍스에 대한 정보를 속성값으로 덧붙여서 보내면 됩니다. (역자주 : 모핑이나 트위닝 같은 것을 할때, 다음 프레임에 있을버텍스의 위치 정보를 속성값으로 붙여서 보내는 방식)

문3) 분기(Branching) 을 하려면 어떻게 해야 합니까?
답) 버텍스 쉐이더에는 분기 명령어가 없습니다 (역자주: 버텍스 쉐이더 2.0 부터는 분기명령이 추가되었다) 하지만 SGE 명령의 결과값을 곱하는 것을 이용해서 if-then-else 를 수행하거나 두개중의 하나의 값을 고르는 일을 할 수 있습니다. 예를 들면 다음과 같습니다.

; r1 이 r2 보다 크면 r0 에 r3 을 넣고, 그렇지 않으면 r0 에 r4 를 넣는다.

SGE r0, r1, r2 ; r1 >= r1 이면 r0 이 1, 아니면 0 이 된다
ADD r1, r3, -r4
MAD r0, r0, r1, r4 ; r0 = r0*(r3-r4) + r4 = r0*r3 + (1-r0)*r4

우리 사이트에 있는 "이 명령은 어디있어요?" 문서를 보시면, 이런 부류의 버텍스 쉐이더 기법들이 더 소개되어있습니다.

문4) 96 개의 상수밖에 없는데 본 갯수가 200 개짜리 모델의 matrix pallette skinning 을 하려면 어떻게 해야 하나요?
답) Bone 데이타를 위해 상수메모리를 얼마나 비워줄 수 있느냐에 따라 다르지만 상수메모리에는 대략 20-30개정도의 본 데이타(행렬)을 담을 수 있는 공간이 있습니다. 만약에 여러분이 그릴 모델이 이것보다 더 많은 갯수의 본을 사용한다면 (인체의 경우라면 거의 대부분 그렇습니다.) 메쉬데이타를 본의 영향값별로 나누어서 쪼개주고, 각각의 메쉬조각별로 나눠서 그려주면 됩니다. 하나의 메쉬조각이 전체 200개의 본중 20-30개 이내의 본에 의해서만 받도록 나눠주는 것입니다. D3DX 에 있는 ConvertToIndexedBlendedMesh 함수를 이용하면 메쉬를 자동으로 나눠주니까 이것때문에 따로 코딩을 하지 않아도 됩니다. 대개의 경우 그렇지만, 본 매트릭스가 Affine 행렬이라면 (스케일이나 프로젝션, 눕히기등의 변환이 포함되어있지 않고, 단지 회전과 이동만으로 구성된 행렬을 Affine 이라고 생각하면 됨) 행렬의 4x3 부분만 사용하면 됩니다. 또한 원래 노말값을 변환시키려면 행렬의 역행렬값도 알아야 하지만, 역시 대개의 경우에는 직교행렬을 쓰니까 본 하나당 4x3 짜리 행렬 하나씩만 쓰면 됩니다.

문5) W-buffer 가 제대로 안 되는 것 같습니다.
답) 버텍스 쉐이더를 쓰면서 w-buffer 를 사용할때에는 반드시 SetTransform 을 이용해서 projection matrix 를 별도로 세트해줘야 합니다. 안 그러면 w-buffer 가 안 먹습니다.

문6) 버텍스 쉐이더랑 FFP (Fixed Function Pipeline = 버텍스 쉐이더 안 쓰는 방식)를 섞어서 써도 되나요?
답) 가능합니다. 하지만 DrawPrimitive 호출할때 그려지는 내용 자체는 VS 나 FFP 둘중 하나로만 써야 합니다. 예를 들면 VS 로 라이팅을 하면서 FFP 로 변환을 한다던가 하는 것은 할 수 없습니다. 만약 VS 를 쓰기로 했다면 VS 는 변환/라이팅에 필요한 모든 작업을 다 끝내서 동차 클립 좌표계(Homogeneous Clip Space)로 되어있는 위치정보, 라이팅이 끝난 색상 정보, 텍스춰 좌표정보, 포그 정보등을 결정해서 보내줘야 합니다. DrawPrimitive 를 여러번 하게 된다면, 그중 어떤건 ffp 로 찍고 어떤건 vs 로 찍는다던가 하는 것은 자유롭게 할 수 있습니다. 하지만 같은 입력에 대해 ffp 의 결과값과 vs 의 결과값이 이론상 똑같이 될거라고 생각하고 찍었다고 해서 완전히 비트 단위로 같은 결과값이 나올거라고 보장하지는 않는 다는 점을 유념해야 합니다. 가끔가다 이런 보장이 필요할 때가 있는데, 한 오브젝트를 멀티패스로 찍을때 z 값이 서로 달라지면 곤란한 경우가 그렇습니다. 예를 들면 칼라 텍스춰는 vs 로 찍고, 그 위에 라이트맵은 ffp 로 찍는다던가 하면 z 값이 미세하게 달라질 수도 있기 때문에 이런 부분은 가급적 ffp 만 쓰던지 vs 만 쓰던지 하나로 통일해야 합니다.

문7) GeForce3 는 FFP 상에서의 matrix palette skinning 을 지원하나요?
답) 아뇨. 지원하지 않습니다. 왜냐하면 DX8 에서 FFMPS 를 처리하는 소프트웨어 부분은 버텍스 쉐이더의 소프트웨어 에뮬레이션을 처리하는 부분보다 더 비효율적으로 되어있기 때문입니다. 매트릭스 팔레트 스키닝을 쓰려면 꼭 버텍스 쉐이더를 쓰시길 권합니다.

문8) 라이팅/스키닝/텍스춰 좌표생성등의 각각 처리의 조합별로 버텍스 쉐이더를 일일이 만들어야 하나요? 예를 들면 라이트를 한개 쓰면서 스키닝을 하는 쉐이더, 라이트를 두개쓰면서 스키닝을 하는 쉐이더, 라이트를 한개 쓰면서 스키닝을 안하는 쉐이더... 그런 식의 조합 말입니다.
답) 그렇습니다. 그렇게 하고 싶지 않을 때에는 예를 들어서 하나의 쉐이더가 최고 4 개까지의 라이트를 처리할 수 있게 만들어놓고 나머지 라이트의 데이타 값을 0 으로 해서 라이트를 안 쓰게 하는 식으로 할 수 있을 것입니다. 하지만 효과를 쓰지 않는다고 해도 버텍스 쉐이더상의 처리 시간은 여전히 소모되기 때문에 그만큼 시간은 낭비될 것이고, 이 부분이 병목지점에 속한다면 결국 전체적인 속도에 부담을 주게 됩니다. 대신 NVLink 라는 툴로 버텍스 쉐이더의 각 부품(Fragment) 들을 프로그램 수행시, 또는 수행전에 임의로 조합할 수 있도록 준비했습니다. 이 툴은 저희 개발자 웹사이트에서 구하실 수 있습니다.

imcgames 의 김학규입니다