Game Develop

[DirectX11] ColorPicking 구현 본문

ComputerGraphics/DirectX

[DirectX11] ColorPicking 구현

MaxLevel 2021. 12. 20. 19:16

컬라이더세팅툴씬에서 컬라이더를 마우스피킹했을 경우 기즈모를 렌더시키고 싶기 때문에 구현해봤다.

사실 기존의 반직선피킹을 하더라도 툴씬을 사용하는데에 지장을 줄정도로 프레임저하가 있을거 같진 않긴하다.

당장은 컬라이더를 뭐 수십 수백개를 세팅(검사)할것도 아니니까..

하지만 여기 말고도 다른 씬에서 사용할 수도 있고, 구현방법은 쉬우면서 효과는 좋기때문에 해봤다.

분명 유용할것같긴하다. 픽셀단위로 정확한거치고는 프레임저하가 거의 없다. 다만 두번 그리는것이기 때문에 고해상도일수록 프레임 저하가 좀 더 있을것이다.

 

 

영상

 

이 방법은 예~전에 김포프님께서 올려주신 글을 보고 따라해봤다.(https://blog.popekim.com/ko/2012/12/03/fast-object-picking.html) 포프님께서도 툴작업에서 유용하게 사용하셨다기도 하고, 구현방법이 쉬워서 따라해봤다. 

메인씬렌더하기전에 렌더타겟에다가 한번 그려주고 얻은 텍스쳐를 이용하는 방법이다. 

 

일단 각 오브젝트마다 HashColor를 만들어주고 컬러버퍼를 셰이더에 셋팅해준다.

컬러피킹용 셰이더는 그냥 픽셀셰이더에서 세팅한 컬러버퍼의 컬러값을 그대로 리턴만 시켜주면된다.

그렇다면 이 상태에서 Draw할 경우, 렌더타겟에 오브젝트가 HashColor의 색상대로 그려질것이다.

그러면 마우스위치에 따라 렌더타겟의 텍스쳐에 해당하는 값을 뽑아와서 비교해주면 된다.

나는 렌더타겟의 텍스쳐의 마우스위치에 따른 샘플링하는것을 그냥 컴퓨트셰이더에 렌더타겟텍스쳐의 SRV랑 마우스 위치값을 보내서 샘플링하고 그 값을 리턴받아서 사용했다.. 사실 그냥 CPU쪽에서 세팅한 렌더타겟과 동일한 텍스쳐를 만들어서 거기로 복사해서 샘플링해도 되긴 하지만, 셰이더가 알아서 샘플링 해주니까 편해서 이렇게 했다..

 

여담으로 멍청한 짓때문에 하루를 날린 이야기를 말하자면, 두 float값(오브젝트의 해쉬컬러값과 렌더타겟텍스쳐에서 샘플링한 float값) 을 비교할 때 두 값의 차이가 엡실론값의 이하이면 같다고 작성해야하는데 그냥 아무생각없이 엡실론값을 안따지고 그냥 if(float f1 == float f2) 이런식으로 먼저 코드를 작성했었다.

그러니 당연히 해당 오브젝트를 피킹해도 피킹이 됐다가 안됐다가 하는 것이다.

이때 생각의 포인트를 엡실론값이 아니라 그저 float값이 셰이더를 거치면서 어떤 값의 변화가 생기는줄알고 아예 셰이더쪽을 계속 건드렸다. 삽질의 시작이였다. float값이 문제니, 그냥 int값을 셰이더에 넘겨서 비교하기로 해봤다. 김포프님의 글에서 해쉬컬러값을 int형으로 하고 셰이더에 셋팅했길래, 사실 원래 int값으로 하는건가..하는 생각이 든것도 크다. 그래서 무작정 텍스쳐포맷부터시작해서 전부 int로 바꿔봤다. 렌더텍스쳐에 그리는 용도의 셰이더에서는 그래도 리턴값으로 float4로 했다. int로 하니까 씬에 렌더할 때 검은색으로만 나와서..

 

뭐 결과적으로 피킹은 잘 됐었다. int값으로 비교한거니까. 근데 지금 만들고있는게 취직을위한 포폴용이긴해서 렌더타겟텍스쳐를 보여주고싶었다. (서로 다른 색상의 오브젝트가 그려진 텍스쳐)

그러면 걍 그 텍스쳐를 렌더하면 되는거아닌가? 라는 생각이 들겠지만,  셰이더에 넘길 때 해쉬컬러값을 0~1이 아닌 0~255으로 넘겼기 때문에 0이 아닌 나머지값 (1~255)이 전부 해당 색의 최고비율로 출력이 되기 때문에 만약 두 오브젝트의 해쉬컬러값의 R값이 1이상이기만 하면 R값의 비율은 동일해진다. 즉 한 오브젝트는 R값이 1이고 다른 오브젝트는 255라면, 원래는 R값의 비율이 많이 차이나는 것이지만 렌더타겟에는 거의 동일한 색으로 출력된다. (둘 다 1이상이라..)

( 2024.05.12 .. 문득 든 생각인데, 렌더타겟의 각 픽셀 컬러값의 범위를 0~1이아닌 0~255로 설정하는거 있을지도;? 

  0~1범위값만 이용했었기 때문에 0~255로 설정한다는 생각자체를 못했었다... 어쨌든 확실하지는 않다.)

 

사실 결과적으로만 보자면 뭐 렌더타겟에 뭔색으로 그려지든, 피킹만 잘되면 되기때문에 아무 의미없다.

이미 말한대로 그냥... 보여주기용이다. 

그래서 렌더타겟을 피킹용1장,보여주기용 1장 해서 총 2장을 셋팅해봤다. 한장은 피킹용 int값 텍스쳐, 나머지 한장은 씬에 렌더용 0~1로 (255로 나눴다) 정규화한 float값 텍스쳐... 이렇게 해서 결국 원하는대로 피킹은 피킹대로 되고 씬에 색상렌더는 색상렌더대로 잘 됐다. 

근데 또 생각해보니 결국 렌더타겟을 한장 더써서 해결했다는게 너무 찝찝했다. 자존심 상했다고해야하나?

분명히 렌더타겟1장으로 해결될것이다. 아직 내가 모르는게 많기때문에 이러한 편법으로 구현한것일뿐일거다.

그래서 다시 본래의 글(김포프님꺼)을 다시 보니까... 셰이더에 세팅할 때 컬러데이터타입에 ToVector4를 해서 셰이더에 셋팅한것을 발견했다. Vector4라서 멤버변수들은 float값일거고, 그렇다는것은 결국 셰이더에 float값으로 넘겨준다는거다.  

 

결국 원점으로 돌아와서 전부 float으로 바꾸고 다시 생각해봤다. 그러다가 float값 비교할때 엡실론값을 반영해야한다는걸 생각한거다. 너무 늦게 생각했다. 거의 스터디카페에서의 하루공부량을 날렸다. 물론 이런 삽질을 하면서 배운것도 이것저것 있긴한데.. 그래도 너무 멍청했다.

아마 고수분들이 봤다면 '왜 이런 멍청한짓을 하는거지?'라고 충분히 생각할수있는 하루였다. 99% 그렇게 생각할거다.