벡터를 모니터 점으로 표현하기
스크린 좌표계 :
- 데카르트 좌표계를 사용
- y축이 아래를 향하는 방식
- 이산적인 정수를 사용
- 픽셀 Pixel : 스크린 좌표계에서 좌표와 색상에 대응하는 화면 요소
- 픽셀화 Rasterization : 실수로 표현된 벡터좌표를 정수로 변환 후 색상을 부여하는 과정
*픽셀화 과정에서는 경계값에 대한 픽셀화 규칙이 필요하다.해상도가 짝수인 경우 원점을 픽셀로 표현해야할때 , 가장자리에 벡터 좌표가 걸쳐져 있는 경우 규칙을 정해야 한다.
반대로 스크린 좌표에서 데카르트 좌표로 변환할때도 규칙이 필요하다.
브레젠험 알고리즘 Bresenham's algorithm
두점에서 스칼라 a 값의 범위를 늘리면서 벡터를 생성하고 대응하는 픽셀을 찍는다면 하나의 픽셀에 대응되는 벡터가 많기 때문 효과적이지 못하기 때문에 픽셀로 된 선을 그리는 알고리즘이 따로 있다.
브레젠험 알고리즘은 화면을 8등분 영역으로 구분한 후 각 영역별로 그려내는 방식을 사용한다.
1팔분면에 해당하는 선의 예시로 살펴보자.
1팔분면은 [$0^\circ,45^\circ $]의 범위를 가진다.
이는 해당 영역에 존재하는 모든 선의 기울기가 1을 넘을 수 없음을 의미하며 1팔분면에 위치한 선을 구성하는 점의 진행은 평행하거나 한칸만 아래로 내려가는 특징이 있다.
교차점●이 위 두픽셀의 중간지점 ●보다 위에 있다면 $(x+0+1,y_0)$ 픽셀을 칠하고 아래에 있다면 $(x+0+1,y_0+1)$ 픽셀이 칠해진다. ● y값 > ● y값
이를 수식으로 표현하기위해 직선의 방정식 $y= ax+b$에 시작 좌표값을 대입해서 확인해보자
$y=\frac{\Delta y}{\Delta x}x+b$
● $\frac{\Delta y}{\Delta x}x+y_1-\frac{\Delta y}{\Delta x} x_1 <$ ● $ y_0+0.5$
이를 단순화 하면 아래와 같다.
$2\Delta y-\Delta x<0$
각 지점의 값을 대입한 판별값은 아래와 같은 패턴으로 증가한다.
유니티로 브레젠험 알고리즘을 활용해서 선그리기
using System.Collections.Generic;
using UnityEngine;
public class DrawLine : MonoBehaviour
{
public Material mat;
private Texture2D texture;
public int height = 50;
public int width = 50;
private int y0;
private int x0;
private Vector2Int endPos;
private List<Vector2Int> container;
private Color[] texturePixelsColors;
private Color rc;
private Vector2 startPos;
void Start()
{
x0 = (int)(width * .5f);
y0 = (int)(height * .5f);
endPos = new Vector2Int(width, height);
texture = new Texture2D(height, width, TextureFormat.RGBA32, true);
texture.wrapMode = TextureWrapMode.Clamp;
texture.filterMode = FilterMode.Point;
texturePixelsColors = new Color[height * width];
for (int i = 0; i < texturePixelsColors.Length; i++)
{
texturePixelsColors[i] = new Color(1, 1, 1);
}
texture.SetPixels(texturePixelsColors);
texture.Apply();
mat.mainTexture = texture;
}
private Renderer rend;
private MeshCollider meshCollider;
private Texture2D tex;
private RaycastHit hit;
private Vector2 startUV;
private Vector2 endUV;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
rc = Color.HSVToRGB(Random.Range(0f, 1f), 0.6f, 1);
if (!Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit))
return;
rend = hit.transform.GetComponent<Renderer>();
meshCollider = hit.collider as MeshCollider;
if (rend == null || rend.sharedMaterial == null || rend.sharedMaterial.mainTexture == null ||
meshCollider == null)
return;
tex = rend.material.mainTexture as Texture2D;
startUV = hit.textureCoord;
startUV.x *= tex.width;
startUV.y *= tex.height;
}
if (!Input.GetMouseButton(0))
return;
if (!Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit))
return;
endUV = hit.textureCoord;
endUV.x *= tex.width;
endUV.y *= tex.height;
Draw(endUV, startUV);
}
void Draw(Vector2 endPoint, Vector2 startPoint)
{
for (int i = 0; i < texturePixelsColors.Length; i++)
{
texturePixelsColors[i] = new Color(1, 1, 1);
}
x0 = (int)startPoint.x;
y0 = (int)startPoint.y;
int dx = (int)(endPoint.x - x0);
int dy = (int)(endPoint.y - y0);
int w = Mathf.Abs(dx);
int h = Mathf.Abs(dy);
int x1 = x0, y1 = y0;
var xval = dx > 0 ? 1 : -1;
var yval = dy > 0 ? 1 : -1;
bool isGradualSlope = w > h;
if (isGradualSlope)
{
int accumulator = 2 * h - w;
for (int i = 0; i < w - 1; i++)
{
x1 += xval;
if (accumulator < 0)
{
accumulator += 2 * h;
}
else
{
y1 += yval;
accumulator += 2 * (h - w);
}
var textureIndex = GetIndex(new Vector2Int(x1, y1), new Vector2Int(width, height));
texturePixelsColors[textureIndex] = rc;
}
}
else
{
int accumulator = 2 * w - h;
for (int i = 0; i < h - 1; i++)
{
y1 += yval;
if (accumulator < 0)
{
accumulator += 2 * w;
}
else
{
x1 += xval;
accumulator += 2 * (w - h);
}
var textureIndex = GetIndex(new Vector2Int(x1, y1), new Vector2Int(width, height));
texturePixelsColors[textureIndex] = rc;
}
}
texture.SetPixels(texturePixelsColors);
texture.Apply();
mat.mainTexture = texture;
}
int GetIndex(Vector2Int coord, Vector2Int size)
{
return size.x * coord.y + coord.x;
}
}
참고
이득우의 게임 수학 : 네이버 도서
네이버 도서 상세정보를 제공합니다.
search.shopping.naver.com
https://www.markwrobel.dk/post/amiga-machine-code-letter12-linedraw/
https://www.middle-engine.com/blog/posts/2020/07/28/bresenhams-line-algorithm
'study > math' 카테고리의 다른 글
[이득우 게임수학] 아핀공간 (0) | 2024.07.03 |
---|---|
[이득우 게임수학] 역행렬 (0) | 2024.06.30 |
[이득우 게임수학] 행렬의 설계 (0) | 2024.06.26 |
[이득우 게임수학] 행렬 (0) | 2024.06.17 |
[이득우 게임수학] 선형성과 선형변환 (0) | 2024.06.09 |
댓글