시야각의 각도 θ를 반으로 나눈다.
θ/2 와 target과의 내적을 통해 cosβ값을 얻어낸다음
cosθ/2와 cosβ을 비교해서 cosβ가 더 크다면은 시야각안에 들어와있다고 판별이 가능함.
cos그래프는 -90~90도에서 값이 줄어들기 때문에.
그리고 코드에 시야 distance(300.f)범위 안에 들어와있을 때만 빨간색으로 바뀌게 코드를 좀 추가함.
// 게임 로직을 담당하는 함수
void SoftRenderer::Update2D(float InDeltaSeconds)
{
// 게임 로직에서 사용하는 모듈 내 주요 레퍼런스
auto& g = Get2DGameEngine();
const InputManager& input = g.GetInputManager();
// 게임 로직의 로컬 변수
static float moveSpeed = 100.f;
static std::random_device rd;
static std::mt19937 mt(rd());
static std::uniform_real_distribution<float> randomPosX(-300.f, 300.f);
static std::uniform_real_distribution<float> randomPosY(-200.f, 200.f);
static float duration = 3.f;
static float elapsedTime = 0.f;
static Vector2 TargetStart = TargetPosition;
static Vector2 TargetDestination = Vector2(randomPosX(mt), randomPosY(mt));
// 시야각의 cos 값은 최초 한 회만 계산해 보관한다.
static float halfFovCos = cosf(Math::Deg2Rad(fovAngle * 0.5f));
elapsedTime = Math::Clamp(elapsedTime + InDeltaSeconds, 0.f, duration);
// 지정한 시간이 경과하면 새로운 이동 지점을 랜덤하게 설정
if (elapsedTime == duration)
{
TargetStart = TargetDestination;
TargetPosition = TargetDestination;
TargetDestination = Vector2(randomPosX(mt), randomPosY(mt));
elapsedTime = 0.f;
}
else // 비율에 따라 목표지점까지 선형 보간하면서 이동
{
float ratio = elapsedTime / duration;
TargetPosition = Vector2
(
Math::Lerp(TargetStart.X, TargetDestination.X, ratio),
Math::Lerp(TargetStart.Y, TargetDestination.Y, ratio)
);
}
Vector2 inputVector = Vector2(input.GetAxis(InputAxis::XAxis), input.GetAxis(InputAxis::YAxis)).GetNormalize();
Vector2 deltaPosition = inputVector * moveSpeed * InDeltaSeconds;
Vector2 f = Vector2::UnitY;
Vector2 v = (TargetPosition - PlayerPosition).GetNormalize();
float L = (TargetPosition - PlayerPosition).Size(); // 추가한 코드
// 물체의 최종상태 설정
if (v.Dot(f) >= halfFovCos && L < 300.f)
{
PlayerColor = LinearColor::Red;
TargetColor = LinearColor::Red;
}
else
{
PlayerColor = LinearColor::Gray;
TargetColor = LinearColor::Blue;
}
PlayerPosition += deltaPosition;
}
내적을 활용한 조명 모델 구현
렘베르트 반사 모델을 활용하여
빛이 들어오는데 이 표면벡터와의 사잇각을 통해서
즉, 내적하여 cosθ을 구하여 그 값이 0으로 가까워 질 수록 색을 연하게 칠해서 90도 넘어가면은 아예 검은색(원래색)으로 놔둠.
1일때는 딱 서로 마주보니까 색을 그대로 반사하면됨.
// 게임 로직을 담당하는 함수
void SoftRenderer::Update2D(float InDeltaSeconds)
{
// 게임 로직에서 사용하는 모듈 내 주요 레퍼런스
auto& g = Get2DGameEngine();
const InputManager& input = g.GetInputManager();
// 게임 로직의 로컬 변수
static float duration = 20.f;
static float elapsedTime = 0.f;
static float currentDegree = 0.f;
static float lightDistance = 200.f;
static HSVColor lightHSVColor;
// 경과 시간에 따른 현재 각과 이를 사용한 [0,1]값의 생성
elapsedTime += InDeltaSeconds;
elapsedTime = Math::FMod(elapsedTime, duration);
float currentRad = (elapsedTime / duration) * Math::TwoPI;
float alpha = (sinf(currentRad) + 1) * 0.5f;
// [0,1]을 활용해 주기적으로 크기를 반복하기
currentDegree = Math::Lerp(0.f, 360.f, alpha);
// 광원의 좌표와 색상
float sin = 0.f, cos = 0.f;
Math::GetSinCosRad(sin ,cos, currentDegree);
lightPosition = Vector2(cos, sin) * lightDistance;
lightHSVColor.H = currentRad * Math::InvPI * 0.5f;
lightColor = lightHSVColor.ToLinearColor();
}
// 렌더링 로직을 담당하는 함수
void SoftRenderer::Render2D()
{
// 렌더링 로직에서 사용하는 모듈 내 주요 레퍼런스
auto& r = GetRenderer();
const auto& g = Get2DGameEngine();
// 렌더링 로직의 로컬 변수
static std::vector<Vector2> light;
static float lightRadius = 10.f;
static std::vector<Vector2> circle;
static float circleRadius = 50.f;
// 광원을 표현하는 구체
if (light.empty())
{
float lightRadius = 10.f;
for (float x = -lightRadius; x <= lightRadius; ++x)
{
for (float y = -lightRadius; y <= lightRadius; ++y)
{
Vector2 target(x, y);
float sizeSquared = target.SizeSquared();
float rr = lightRadius * lightRadius;
if (sizeSquared < rr)
{
light.push_back(target);
}
}
}
}
// 빛을 받는 물체
if (circle.empty())
{
for (float x = -circleRadius; x <= circleRadius; ++x)
{
for (float y = -circleRadius; y <= circleRadius; ++y)
{
Vector2 target(x, y);
float sizeSquared = target.SizeSquared();
float rr = circleRadius * circleRadius;
if (sizeSquared < rr)
{
circle.push_back(target);
}
}
}
}
// 광원 그리기
static float lightLineLength = 50.f;
r.DrawLine(lightPosition, lightPosition - lightPosition.GetNormalize() * lightLineLength, lightColor);
for (auto const& v : light)
{
r.DrawPoint(v + lightPosition, lightColor);
}
// 광원을 받는 구체의 모든 픽셀에 NdotL을 계산해 음영을 산출하고 이를 최종 색상에 반영
for (auto const& v : circle)
{
Vector2 n = (v - circlePosition).GetNormalize();
Vector2 l = (lightPosition - v).GetNormalize();
float shading = Math::Clamp(n.Dot(l), 0.f, 1.f);
r.DrawPoint(v, lightColor * shading);
}
// 현재 조명의 위치를 화면에 출력
r.PushStatisticText(std::string("Position : ") + lightPosition.ToString());
}
Render2D의 광원을 받는 구체의 모든 픽셀에 내적을 하여 음영 산출하고 픽셀에 색을 반영한다.
내적을 활용한 투영
Target과의 벡터 내적을 통해 최단거리를 구하는 부분이다.
투영벡터는 나중에 평면의 방적식의 기반이 된다.
// 게임 로직을 담당하는 함수
void SoftRenderer::Update2D(float InDeltaSeconds)
{
// 게임 로직에서 사용하는 모듈 내 주요 레퍼런스
auto& g = Get2DGameEngine();
const InputManager& input = g.GetInputManager();
// 게임 로직의 로컬 변수
static float duration = 6.f;
static float elapsedTime = 0.f;
static float currentDegree = 0.f;
static float rotateSpeed = 180.f;
static float distance = 250.f;
static std::random_device rd;
static std::mt19937 mt(rd());
static std::uniform_real_distribution<float> randomY(-200.f, 200.f);
elapsedTime = Math::Clamp(elapsedTime + InDeltaSeconds, 0.f, duration);
if (elapsedTime == duration)
{
lineStart = Vector2(-400.f, randomY(mt));
lineEnd = Vector2(400.f, randomY(mt));
elapsedTime = 0.f;
}
// 점의 위치설정
currentDegree = Math::FMod(currentDegree + rotateSpeed * InDeltaSeconds, 360.f);
float sin = 0.f, cos = 0.f;
Math::GetSinCos(sin, cos, currentDegree);
point = Vector2(cos, sin) * distance;
}
// 렌더링 로직을 담당하는 함수
void SoftRenderer::Render2D()
{
// 렌더링 로직에서 사용하는 모듈 내 주요 레퍼런스
auto& r = GetRenderer();
const auto& g = Get2DGameEngine();
// 렌더링 로직의 로컬 변수
static float radius = 5.f;
static std::vector<Vector2> circle;
if (circle.empty())
{
for (float x = -radius; x <= radius; ++x)
{
for (float y = -radius; y <= radius; ++y)
{
Vector2 target(x, y);
float sizeSquared = target.SizeSquared();
float rr = radius * radius;
if (sizeSquared < rr)
{
circle.push_back(target);
}
}
}
}
// 붉은 색으로 점 그리기
for (auto const& v : circle)
{
r.DrawPoint(v + point, LinearColor::Red);
}
// 투영할 라인 그리기
r.DrawLine(lineStart, lineEnd, LinearColor::Black);
r.DrawLine(lineStart, point, LinearColor::Red);
// 투영된 위치와 거리 계산
Vector2 unitV = (lineEnd - lineStart).GetNormalize(); // 투영할 선분의 단위 벡터
Vector2 u = point - lineStart; // 점까지의 벡터
Vector2 projV = unitV * (u.Dot(unitV)); // u Dot unitV => 투영된 크기 나옴 -> 이것을 단위벡터에다가 곱함. 그 크기만큼 커짐.
Vector2 projectedPoint = lineStart + projV; // 점 위치를 lineStart에다가 더해서 정확하게 위치시킴
float distance = (projectedPoint - point).Size();
// 투영된 점 그리기
for (auto const& v : circle)
{
r.DrawPoint(v + projectedPoint, LinearColor::Magenta);
}
// 투영 라인 그리기
r.DrawLine(projectedPoint, point, LinearColor::Gray);
// 관련 데이터 화면 출력
r.PushStatisticText(std::string("Point : ") + point.ToString());
r.PushStatisticText(std::string("Projected Point : ") + projectedPoint.ToString());
r.PushStatisticText(std::string("Distance : ") + std::to_string(distance));
}