Камера. Сетка из линий. Использование алгоритма Брезенхема

Загрузить архив с примерами ЗДЕСЬ.

Ранее в примере для рисования сетки из линий мы использовали функции GDI MoveToEx() и LineTo(). Эти функции сами производят отсечение в экранных координатах. Например следующий код корректно обрежет и отобразит линию:


#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <stdio.h>

int main(void)
{
	HWND hWnd = GetConsoleWindow();

	HDC hDC = GetDC(hWnd);

	HPEN hPen = CreatePen(PS_SOLID, 4, RGB(255, 255, 127));
	HPEN hOldPen = (HPEN) SelectObject(hDC, hPen);

	int x1 = 50;
	int y1 = 50;

	int x2 = -50;
	int y2 = 150;

	MoveToEx(hDC, x1, y1, NULL);
	LineTo(hDC, x2, y2);

	getchar();

	SelectObject(hDC, hOldPen);
	DeleteObject(hPen);
	ReleaseDC(hWnd, hDC);

	return 0;
}

Теперь в этом примере мы будем сами рисовать линии при помощи алгоритма Брезенхема и нам самим нужно отсекать линии в экраннх координатах. В примере использовалась функция DrawDibDraw. Для клиппинга линий в экранных координатах использвался алгоритм Коэна-Сазерленда.

Код примера можно загрузить /src/03.003-draw_field_brezenhame/Draw_Field_Brezenhame.

После запуска примера перемещатся по сцене можно с помощью клавиш W,S,A,D и поворачивать камеру мышей.

Камера. Сетка из линий. Использование алгоритма Брезенхема

Реализация алгоритма Коэна-Сазерленда: Каждой вершине присваивается код региона. Биты кода указывают на положение точки относительно экранных границ. Внутренний регион (0000) указывает, что точка полностью видима.


	1001 | 1000 | 1010
	-------------------
	0001 | 0000 | 0010
	-------------------
	0101 | 0100 | 0110


#include <iostream>

const int LEFT = 1;   // 0001
const int RIGHT = 2;  // 0010
const int TOP = 4;    // 0100
const int BOTTOM = 8; // 1000

int ComputeCode(int x, int y, int left, int right, int top, int bottom)
{
    int code = 0;
    if (x < left) code |= LEFT;
    if (x > right) code |= RIGHT;
    if (y < top) code |= TOP;
    if (y > bottom) code |= BOTTOM;
    return code;
}

void CohenSutherlandClip(int x1, int y1, int x2, int y2,
                         int left, int right, int top, int bottom)
{
    int code1 = ComputeCode(x1, y1, left, right, top, bottom);
    int code2 = ComputeCode(x2, y2, left, right, top, bottom);
    bool accept = false;

    while (true)
    {
        if (!(code1 | code2)) // Оба конца внутри окна
        {
            accept = true;
            break;
        }
        else if (code1 & code2) // Оба конца вне окна в одном направлении
        {
            break;
        }
        else
        {
            int codeOut;
            int x, y;

            if (code1 != 0)
                codeOut = code1;
            else
                codeOut = code2;

            if (codeOut & TOP)
            {
                x = x1 + (x2 - x1) * (top - y1) / (y2 - y1);
                y = top;
            }
            else if (codeOut & BOTTOM)
            {
                x = x1 + (x2 - x1) * (bottom - y1) / (y2 - y1);
                y = bottom;
            }
            else if (codeOut & RIGHT)
            {
                y = y1 + (y2 - y1) * (right - x1) / (x2 - x1);
                x = right;
            }
            else if (codeOut & LEFT)
            {
                y = y1 + (y2 - y1) * (left - x1) / (x2 - x1);
                x = left;
            }

            if (codeOut == code1)
            {
                x1 = x;
                y1 = y;
                code1 = ComputeCode(x1, y1, left, right, top, bottom);
            }
            else
            {
                x2 = x;
                y2 = y;
                code2 = ComputeCode(x2, y2, left, right, top, bottom);
            }
        }
    }

    if (accept)
    {
        std::cout << "Clipped Line: (" << x1 << ", " << y1 << ") to (" << x2 << ", " << y2 << ")" << std::endl;
    }
    else
    {
        std::cout << "Line is outside the clipping area" << std::endl;
    }
}