Я новичок в C ++, но язык мне кажется нормальным. В качестве учебного проекта я решил создать небольшой 2D графический движок. Это может показаться сложным проектом, но у меня есть хорошая идея, как двигаться дальше.
На самом деле я еще не начал, но у меня складываются мысли в тот момент, когда я столкнулся с этой проблемой:
В какой-то момент мне придется сделать функцию рисования кругов на экране. Мой подход к этому сейчас был бы примерно таким:
in a square with sides from (x-r) to (x+r) loop through x and y,
if at each point, the current distance sqr(x^2+y^2) is less than or equal to r
, then draw a pixel at that point.
Это будет работать, если нет, не говорите мне, я пойму это. Я бы только нарисовал этот круг, если х + г & y + r на экране.
Проблема в том, что мне иногда нужно рисовать действительно большие круги. Если, например, мне нужно нарисовать круг с радиусом 5000, то (если в вычислениях с циклом пикселей потребуется цикл в общей сложности 10000 ^ 2 раза). Таким образом, с процессором с частотой 2 ГГц этот единственный круг будет способен отображать только 2 ГГц / (10000 ^ 2), что составляет ~ 22 раза / с, занимая все ядро (полагая, что требуется только один расчет на пиксель, что нигде правда).
Какой подход является правильным сейчас? Я думаю, это как-то связано с использованием GFX для этих простых вычислений. Если да, могу ли я использовать OpenGL для C ++ для этого? Я бы тоже хотел узнать это 🙂
Моими первыми проектами на C / C ++ были графические библиотеки. У меня не было OpenGL или DirectX, и я использовал DOS в то время. Я многому научился из этого, так как постоянно находил новые и лучшие (и более быстрые) способы рисования на экране.
Проблема с современными операционными системами состоит в том, что они действительно не позволяют вам делать то, что я делал тогда. Вы не можете просто начать использовать оборудование напрямую. И, честно говоря, в эти дни вы не хотите больше.
Вы все еще можете нарисовать все сами. Посмотрите на SDL, если вы хотите поставить свои собственные пиксели. Это библиотека C, которую вы сможете обернуть в свои собственные объекты C ++. Он работает на разных платформах (включая Linux, Windows, Mac, …) и внутренне будет использовать такие вещи, как DirectX или OpenGL.
Для графики реального мира нужно не просто рисовать собственные пиксели. Это не эффективно. Или, по крайней мере, не на устройствах, где вы не можете использовать оборудование напрямую …
Но для ваших целей, я думаю, SDL — это определенно правильный путь! Удачи с этим.
Вы не делаете графику, рисуя пиксели вручную на экране, таким образом, ложь безумие.
То, что вы хотите использовать, это либо DirectX, либо OpenGL. Я предлагаю вам взломать Google и пойти читать, есть много, чтобы прочитать там.
После того, как вы загрузите библиотеки, вы увидите множество примеров проектов, которые помогут вам начать.
На данный момент существует два подхода: есть математический способ вычисления векторов, которые описывают форму с очень большим числом сторон (то есть она будет выглядеть как круг). Или есть «читерский» метод — просто нарисовать текстуру (то есть картинку) круга на экране с альфа-каналом, чтобы сделать оставшуюся часть текстуры прозрачной. (Метод мошенничества легче кодировать, быстрее выполнять и дает лучший результат, хотя он и менее гибкий).
Если вы хотите сделать это математически, то обе эти библиотеки позволят вам рисовать линии на экране, поэтому вам нужно начинать подход с точки зрения начальной и конечной точек каждой линии, а не отдельных пикселей. я, ты хочешь векторную графику.
Я не могу сейчас заниматься тяжелой математикой, но векторный подход может выглядеть примерно так (sudo-code):
in-param: num_of_sides, length_of_side;
float angle = 360 / num_of_sides;
float start_x = 0;
float start_y = 0;
x = startX;
y = startX;
for(int i(0); i < num_of_sides; ++i)
{
float endX, endY;
rotateOffsetByAngle(x, y, x + lenth_of_side, y, angle * i, endX, endY);
drawline(x, y, endX, endY);
x = endX;
y = endY;
}drawline(float startX, startY, endX, endY)
{
//does code that draws line between the start and end coordinates;
}
rotateOffsetByAngle(float startX, startY, endX, endY, angle, &outX, &outY)
{
//the in-parameters startX, startY and endX, endY describe a line
//we treat this line as the offset from the starting point
//do code that rotates this line around the point startX, startY, by the angle.
//after this rotation is done endX and endY are now at the same
//distance from startX and startY that they were, but rotated.
outX = endX;
outY = endY; //pass these new coordinates back out by reference;
}
В приведенном выше коде мы перемещаемся по внешней стороне круга, рисуя каждую отдельную линию вокруг внешней 1 на одну. Для каждой линии у нас есть начальная точка и смещение, затем мы поворачиваем смещение на угол (этот угол увеличивается при движении по кругу). Затем мы рисуем линию от начальной точки до точки смещения. Прежде чем мы начнем следующую итерацию, мы перемещаем начальную точку в точку смещения, поэтому следующая строка начинается с конца последней.
Надеюсь, это понятно.
Это один из способов нарисовать заполненный круг. Как вы видите, он будет работать ужасно медленно.
Современная графика основана на абстрагировании вещей более низкого уровня, чтобы их можно было оптимизировать; разработчик пишет drawCircle (x, y, r), и графические драйверы libary + могут передать это до самого чипа, который может заполнить соответствующие пиксели.
Хотя вы пишете на C ++, вы не манипулируете данными, ближайшими к ядру, если не используете графические драйверы. Существуют слои вызовов подпрограмм даже между вашими методами уровня setPixelColour и фактическим двоичным значением, передаваемым по проводам; почти на каждом уровне выполняются проверки, выполняются дополнительные вычисления и процедуры. Таким образом, секрет более быстрой графики заключается в том, чтобы уменьшить количество таких вызовов. Если вы можете получить команду drawCircle вплоть до графического чипа, сделайте это. Не тратьте вызов на один пиксель, когда он такой же обыденный, как и рисование правильной формы.
В современной ОС существуют слои обработки графики, принимающие запросы отдельных приложений, подобных вашему, и объединяющие их с обработкой окон, композитингом и любыми другими эффектами. Таким образом, ваша команда «рисовать на экране» уже опосредована несколькими слоями. То, что вы хотите предоставить процессору, — это минимальная информация, необходимая для выгрузки вычислений в графическую подсистему.
Я бы сказал, если вы хотите научиться рисовать что-то на экране, поиграть с canvas и js, так как цикл разработки прост и сравнительно безболезнен. Если вы хотите изучать C ++, попробуйте проект Euler или нарисуйте материал, используя существующие графические библиотеки. Если вы хотите написать библиотеку 2d-графики, изучите базовые графические технологии, такие как DirectX и OpenGL, потому что именно так графика реализуется в реальности. Но они кажутся такими сложными, говорите? Тогда вам нужно сначала изучить C ++. Они такие, какие есть, по очень веским причинам, какими бы сложными они ни были.
Как говорится в первом ответе, вы не должны делать это самостоятельно для серьезной работы. Но если вы просто хотите сделать это в качестве примера, то можете сделать что-то вроде этого: Сначала определите функцию для рисования отрезков на экране:
void draw_line(int x1, int y1, int x2, int y2);
Это должно быть относительно просто реализовать: просто найдите направление, которое меняется быстрее всего, и итерируйте это направление, используя целочисленную логику, чтобы выяснить, насколько должно измениться другое измерение. То есть, если х меняется быстрее, то y = (x-x1)*(y2-y1)/(x2-x1)
,
Затем используйте эту функцию для реализации круга в качестве кусочно-линейных элементов:
void draw_circle(int x, int y, int r)
{
double dtheta = 2*M_PI/8/r;
int x1 = x+r, x2, y1 = y, y2;
int n = 2*M_PI/dtheta;
for(int i = 1; i < n; i++)
{
double theta = i*dtheta;
x2 = int(x+r*cos(theta)); y2 = int(y+r*sin(theta));
draw_line(x1, y1, x2, y2);
x1 = x2; y1 = y2;
}
}
При этом используются логика и тригонометрические функции с плавающей запятой, чтобы определить, какие линейные элементы наилучшим образом приближают окружность. Это довольно грубая реализация, но я думаю, что любая реализация, которая хочет быть эффективной для очень больших кругов, должна делать что-то вроде этого.
Если вам разрешено использовать только целочисленную логику, одним из подходов может быть сначала нарисовать целочисленный круг с низким разрешением, а затем разделить каждый выбранный пиксель на меньшие пиксели и выбрать нужные субпиксели и т. Д. Это будет масштабироваться как N log N, так что все еще медленнее, чем подход выше. Но вы сможете избежать греха и соз.