7. 내적

CJB_ny·2023년 2월 28일
0

이득우 겜수

목록 보기
5/9
post-thumbnail

정리 글


7-1 예제

시야각의 각도 θ를 반으로 나눈다.

θ/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;
}

7-2

내적을 활용한 조명 모델 구현

렘베르트 반사 모델을 활용하여

빛이 들어오는데 이 표면벡터와의 사잇각을 통해서

즉, 내적하여 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의 광원을 받는 구체의 모든 픽셀에 내적을 하여 음영 산출하고 픽셀에 색을 반영한다.

7-3

내적을 활용한 투영

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));
}
profile
https://cjbworld.tistory.com/ <- 이사중

0개의 댓글