1. 스카이박스 (랜더컴포넌트 상속)
랜더컴포넌트 상속 받았기 때문에 랜더링 관련 기능이다.
그리고, 매쉬랜더와 같이 기본적인 함수들은 다 있다 (랜더컴포넌트 부모쪽에 있는 함수!)
랜더컴포넌트에 구현되어있는것
1. 매쉬 지정
2. 재질 지정
3. 랜더링 호출
4. 버텍스버퍼, 인덱스버퍼 셋팅
물체 랜더링하는게 순수한 목적이면 skybox를 만들 필요가 없다..
why 만들.. ?
스카이박스의 역할
1. 게임 월드를 크게 영역을 잡아서 커다란 돔 형태로 주변을 감싼다. (엄청 크게 매쉬를 키워서 월드를 두른다.)
2. 직사각형 텍스쳐가 좌우끝이 만난다.
3. 도달한다고 가정해서 만든게 아니고, 엄청나게 멀리 도달할 수 없는 하늘, 우주 별 같은 것
4. 얼마나 크게해야 멀리 있는 느낌이 날까 ?
=> 메인카메라가 누구인지 랜더매니저가 받아와서 메인카메라오브젝트에 월드트랜스폼정보를 받아와서 스카이박스에 월드트랜스폼을 본인의 위치값으로 쓰면 된다 (회전 필요x, 위치만 따라다니게)
=> 또는, 쉐이더상에서 처리하면 된다. (그럼 위에처럼 할 필요 x, 아래에 다시 설명.)
스카이박스쉐이더는 lighting처리를 하지 않는다
why? 이미 텍스쳐가 lighting처리를 감안?해서 그려진 것이기 때문에, 맵핑된 그대로 출력할 것이라서
스카이박스는 안에서 밖을 봐야하기때문에 cull_back이 아닌 cull_front로 바꾸어주어야한다.
(cull_back은 안에서 밖을 보려고 하면 안보이지만, cull_front는 밖에서 안을 보려고 해도 보인다.
why? 내가 보는 방향에서 시계방향이 그려지고, 반시계가 안그려지는 것인데 cull_front는 안으로 움푹 들어간 부분이 보이게 되기 때문에 보인다. 수박 반으로 잘라서 보이는 느낌..?)
4. 얼마나 크게해야 멀리 있는 느낌이 날까 ? (쉐이더상에서 처리하는 방법)
스카이박스 오브젝트가 실제로 움직일 필요가 없다. (카메라를 쫓아다닐 필요가 없다, 쉐이더 상에서 강제로 위치를 잡기 때문)
쉐이더 상에서 월드행렬이 빠져야한다.
월드상태가 어떤지 스카이박스에 영향을 주면 안되기 때문이다.
// =======================
// SkyBox Shader
// mesh : Sphere
// g_tex_0 : Output Texture 출력할 텍스쳐
// g_int_0 : SKY Box Type ( 0 : Sphere, 1 : Cube )
// =======================
struct VS_IN
{
float3 vPos : POSITION;
float2 vUV : TEXCOORD;
};
struct VS_OUT
{
float4 vPosition : SV_Position;
float2 vUV : TEXCOORD;
float3 vLocalPos : POSITION;
};
VS_OUT VS_SkyBox(VS_IN _in)
{
VS_OUT output = (VS_OUT) 0.f;
// ViewSpace 로 취급
로컬스페이스를 뷰스페이스라고 가정을 하면,
무조건 원점기준으로 스피어가 감싼다.
why? 자기 로컬 좌표를 가져다 썼을 뿐인데, 뷰스페이스라고 가정해서 사용하기 때문에 무조건 카메라를 중심으로 구 가 감싸지는 느낌이 나는 것이다.
(로컬스페이스 설계할 때도 원점을 중심으로 매쉬 정점을 찍는다)
로컬스페이스에 있는 좌표가 너무 작아서 이 좌표를 그대로 사용할 수 없다.
(뷰스페이스라면, 니어1 파10000으로 이 안에 있는 정점들을 투영해서 NDC 좌표계(-1 ~ 1)로 보낸다)
그래서 쉐이더상에서 로컬스페이스 좌표를 바로 뷰pos로 보고 투영행렬만 곱하면 안된다.
(로컬스페이스의 좌표가 너무 작기 때문)
파가 10000이기 때문에 반지름이 10000인게 볼 수 있는 제일 최대시야이다. 그 이상을 가면 안그려짐..
최대시야로 보려면 투영행렬을 곱하기 전에 적어도 skybox의 파 거리만큼은 늘어나야 투영하면 최대시야로 스카이박 스 에 잡히게 된다. (깊이가 1인 지점)
깊이판정을 LESS가 아닌 LESS_EQUAL로 해야한다
why? 매 프레임마다 랜더링하기전에 제일 먼저 하는 작업이 랜더타겟이랑 깊이타겟을 클리어한다.
깊이 통과 조건이 LESS일 경우, 기존에 기록 되어있는 깊이보다 더 작아야 통과한다.
랜더링 순서에 상관없이 깊이를 기록을 해두기 때문에 그것보다 더 작아야 통과한다. (픽셀 단위로 판단)
이런 이유로 깊이버퍼는 전부 1로 초기화가 되어있다. 그래야 첫번째 물체가 그려질 때 그려질 수가 있다.
초기화를 만약 0으로 하면 투영하게 되면, 0이 가장 가까운 것이기 때문에 그보다 더 작을 수가 없다..
그래서 LESS 조건으로는 다 실패하게 된다.
Skybox는 가장 먼 파 위치에 배치를 하니까 깊이가 투영좌표계 기준으로 1이 된다..
초기화를 하면 1로 초기화가 되기때문에 LESS면 skybox가 안 그려지게 되기 때문에 less_equal을 사용한다.
투영행렬 특징
1. 뷰스페이스에 있는 정점들을 투영시켜 NDC좌표에 가져올 때, 뷰스페이스상에 본인의 z값이 있으니까 그걸 이용해서 삼각형 비율로 1:ViewSpace Z값 = x : ViewSpace X값 인데, viewspace상이니까 이미 뷰스페이스상의 좌표는 알고 있기 때문에 투영시킨 NDC 좌표의 x값을 알 수 있게 된다. (near의 길이)
월드 행렬처럼 고정이 될 수 없고 뷰스페이스상의 좌표를 꼭 알아야한다!
월드행렬은 로컬스페이스에 있는 매쉬를 월드에 어느위치에 보내겠다라고 이미 다 정해져 있는 행렬이다.
투영행렬은 곱해지는 대상마다 좌표가 다 달라진다
월드행렬과 뷰행렬처럼 절대적으로 하나만 있지가 않다.
카메라가 기준이고 카메라가 어디에 있는지 쳐다보고 있는지에 따라 결정이 된다.
투영행렬은 투영을 당하는 애들이 기준이 된다.
투영좌표계에서의 좌표를 알아내려면 입력되는 뷰스페이스에서의 정점의 좌표가 그 비율로 쓰이니까 투영을 하려는 정점마다 다 다르게 나오게 된다.
float4 vViewPos = mul(float4(_in.vPos, 0.f), g_matView);
(1.f가 아닌 이유 => 이미 원점에 와있으니까 이동이 의미가 없고 회전만 적용한다. 카메라가 실제 바라보고 있는 월드가 z축앞에 올 수 있게끔 회전을 적용시켜준다.)
output.vPosition = mul(float4(vViewPos.xyz, 1.f), g_matProj);
(원래는 vProjPos = vViewPos.xyz / vViewPos.w 을 해야되는데, 레스터라이저에서 w를 자동으로 나눠주기 때문에 투영 행렬만 곱하면 된다)
output.vPosition.z = output.vPosition.w;
(z자리에 w를 넣으면, 위에서 아래가 된다.)
아무리 압축을 해도 NDC좌표에서 x축, y축은 같다.
그런데 투영좌표는 w를 자동으로 나눠서 사용하기 때문에 깊이판정에서 z가 1이 된다.
그래서 skybox에 넣으면 최대시야에 있는 것처럼 느껴지게 된다.
//output.vPosition = mul(float4(_in.vPos, 1.f), g_matWVP);
//output.vUV = _in.vUV;
output.vLocalPos = _in.vPos;
return output;
}
// Rasterizer
float4 PS_SkyBox(VS_OUT _in) : SV_Target
{
float4 vOutColor = float4(1.f, 0.f, 1.f, 1.f);
// SkyBox 가 Sphere 타입인 경우
if (g_int_0 == 0)
{
vOutColor = g_tex_0.Sample(g_sam_0, _in.vUV);
}
// SkyBox 가 Cube 타입인 경우
else if (g_int_0 == 1)
{
vOutColor = g_cube_0.Sample(g_sam_0, _in.vLocalPos);
}
return vOutColor;
}
'DirectX 11 3d' 카테고리의 다른 글
230510 노말맵핑 (0) | 2023.05.11 |
---|---|
230509 skybox 개선 (스피어->큐브) (0) | 2023.05.11 |
220504 (0) | 2023.05.10 |
220503 (0) | 2023.05.09 |
220502 (0) | 2023.05.09 |