개발하는 리프터 꽃게맨입니다.

색칠된 삼각형 그리기 본문

컴퓨터 그래픽스

색칠된 삼각형 그리기

파워꽃게맨 2024. 8. 9. 15:02

아이디어

 

세 점이 주어졌을 경우 삼각형을 그릴 수 있습니다.

그리고 세 점의 좌표를 시용해서 삼각형을 감싸는 가장 작은 직사각형을 정의할 수 있습니다.

이 직사각형이 가지는 좌표는 for문 2개를 순회해서 모두 얻어낼 수 있습니다.

그렇다면, 이렇게 얻어낸 좌표를 해당 삼각형이 포함하는지 아닌지 판단하고

삼각형 내부에 있는 점만 찍어내면, 색칠될 삼각형을 얻어낼 수 있을겁니다.

 

 

삼각형 내부에 있는 점

삼각형을 정의해보았습니다.

 

간단하게 람다 1, 람다 2를 t, s 로 바꿔서 식을 전개해보겠습니다.

 

어떤 점 Px 가 있고 P3->Px 벡터를 w라고 했을 때

벡터 w가 

s [0, 1]

t [0, 1]

s+t [0, 1]

에 대해서 정의가 된다면, 삼각형 내부에 있다고 이해할 수 잇습니다.

 

그렇다면, s와 t의 값을 얻어내야 하는데, 이는 w와 v의 내적, w와 u의 내적을 이용해서 얻어낼 수 있습니다.

 

t도 똑같은 방식으로 구해봅시다.


여기서 분모는 내적의 특성을 이용해서 다움과 같이 바꿀 수 있습니다.


또, 분모는 0이 되면 안되겠죠?

분모가 0이기 위해서는

여야 합니다.

이런 경우엔 삼각형이 아닌, 직선이 만들어집니다.

이런 삼각형을 퇴화삼각형이라고 합니다.

 

만약, 퇴화 삼각형이면 삼각형을 그리지 않도록 합니다.

코드는 다음과 같습니다.

 

void RenderManager::DrawTriangle(const array<Vertex2D, 3> worldVertex2DList) const
{
	// 벡터 u, v
	Vector2D u = worldVertex2DList[1].Position - worldVertex2DList[0].Position;
	Vector2D v = worldVertex2DList[2].Position - worldVertex2DList[0].Position;

	//분모
	float uDotv = u.Dot(v);
	float uDotu = u.Dot(u);
	float vDotv = v.Dot(v);

	float denominator = uDotv * uDotv - uDotu * vDotv;

	// 퇴화 삼각형이면 그리기 생략
	if (MM::Abs(denominator) <= MM::ERROR_BOUND)
	{
		return;
	}

	float invDenominator = 1.f / denominator;

	// 삼각형의 최소 및 최대 좌표 계산
	Vector2D minPos = Vector2D::s_zeroVector;
	Vector2D maxPos = Vector2D::s_zeroVector;

	MM::FindMinMax(minPos.X, maxPos.X, { worldVertex2DList[0].Position.X, worldVertex2DList[1].Position.X, worldVertex2DList[2].Position.X });
	MM::FindMinMax(minPos.Y, maxPos.Y, { worldVertex2DList[0].Position.Y, worldVertex2DList[1].Position.Y, worldVertex2DList[2].Position.Y });

	// 스크린 좌표로 변환 후 클리핑
	ScreenPos leftDown = WorldVector2DToScreenPos(minPos);
	ScreenPos rightUp = WorldVector2DToScreenPos(maxPos);

	leftDown.X = max(0.f, leftDown.X);
	leftDown.Y = min(m_screenSize.height, leftDown.Y);
	rightUp.X = min(m_screenSize.width, rightUp.X);
	rightUp.Y = max(0, rightUp.Y);

	// 삼각형 영역 내 모든 점을 검사하고 색칠
	for (int32 iy = rightUp.Y; iy <= leftDown.Y; ++iy)
	{
		for (int32 ix = leftDown.X; ix <= rightUp.X; ++ix)
		{
			ScreenPos pixel(ix, iy);

			Vector2D toWorldVector = ScreenPosToWorldVector2D(pixel);
			Vector2D w = toWorldVector - worldVertex2DList[0].Position;

			float wDotv = w.Dot(v);
			float wDotu = w.Dot(u);

			float s = (wDotv * uDotv - wDotu * vDotv) * invDenominator;
			float t = (wDotu * uDotv - wDotv * uDotu) * invDenominator;
			float oneMinusST = 1.f - s - t;

			if ((s >= 0 && s <= 1) && (t >= 0 && t <= 1) && (oneMinusST >= 0 && oneMinusST <= 1))
			{
				DrawPixel(pixel, Color32::s_red);
			}
		}
	}
}

 

 

결과

 

색 보간

 

색 보간도 해봅시다.

색보간은 무게중심 좌표를 이용해서 수행합니다.

 

삼각형을 구성하는 방정식의 계수를 나열하면

s, t, 1-s-t 입니다.

 

해당 계수를 이용해서 보간을 수행하면 식은 다음과 같습니다.

결과는 아래와 같습니다.

 

 

 

성능 압도적으로 구림..!!