2006(PascalABC)

Около десяти лет назад, когда была актуальна PascalABC(Не .Net!) в поставку с неё входили примеры с трёхмерной графикой, автором был Ткачук А.В.И я решил один из примеров переписать под .Net при этом сохранив оригинальность программы(но не до фанатизма). Конечно же модуль отвечавший за 3D отрисовку был тесно связан со внутренностями PascalAbc и большинство моментов пришлось реализовывать самому. Также я не использовал другие API(OpenGL),потому-что одной из главных моей целей было изучение трёхмерное математики. Оптимизация пока хромает. Вот результаты:

2006.rar (14,2 КБ)

3 лайка

Красиво. Меня тоже эта тема интересует, правда времени особо не было вникать в то как это устроено, написал как-то псевдо-3D-графический проектик, потом отдельно накидал заготовку математического движка для работы с 3D, какие-то примитивы. А тут прям на первый взгяд такое сложное так просто сделано…

Вот минимизированный код(56 строк), который вращает тор,что бы еще проще было разобраться. {$reference PresentationCore.dll} uses System.Windows.Media.Media3D,timers,graphabc; const r1 = 105;r2 = r1 / 3;r3 = r1 / 2;n = 20;m = 20;rstep = 2; function Project(cw, ch, factor: real; pt: Point3D): Point; begin Result := new System.Drawing.Point(Round(cw + pt.x * factor / (pt.z + factor)), Round(ch - pt.y * factor / (pt.z + factor))); end; function TorPoint(t1, t2: real): Point3D; begin var x := (r1 + r2 * cos(DegToRad(t1))) * cos(DegToRad(t2)); var y := (r1 + r2 * cos(DegToRad(t1))) * sin(DegToRad(t2)); var z := r3 * sin(DegToRad(t1)); Result := new Point3D(x, y, z); end; var tim: Timer; rndrot: Point3D; mat := new Matrix3D(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); procedure Draw(); begin ClearWindow(clBlack); mat.RotatePrepend(new Quaternion(new vector3d(1, 0, 0), rndrot.X)); mat.RotatePrepend(new Quaternion(new vector3d(0, 1, 0), rndrot.Y)); mat.RotatePrepend(new Quaternion(new vector3d(1, 0, 1), rndrot.Z)); var pts := new Point[n, m]; for var i := 0 to n - 1 do for var j := 0 to m - 1 do begin var pt := mat.Transform(TorPoint(i * 360 / (n - 1), j * 360 / (m - 1))); pts[i, j] := Project(WindowWidth / 2, WindowHeight / 2, 500, pt); end; for var i := 0 to n - 2 do for var j := 0 to m - 2 do begin var b := new point[4]; b[0] := pts[i, j]; b[1] := pts[i + 1, j]; b[2] := pts[i + 1, j + 1]; b[3] := pts[i, j + 1]; DrawPolygon(b); end; Redraw(); end; procedure md(mb,x,y:integer); begin rndrot := new Point3D(Random() * rstep * 4 - rstep * 2, Random() * rstep * 4 - rstep * 2,Random() * rstep * 4 - rstep * 2); end; begin md(0,0,0); OnMouseDown+=md; LockDrawing(); SetPenColor(clred); tim := new Timer(30,draw); tim.start(); end.

Ну то есть тора на самом деле нет, есть массив точек. и превратить его сходу из каркаса в поверхность не получится. Хотя… по каркасу сложно сказать, но в коде я никакой сортировки не вижу по глубине. то есть пока это каркас - это работает. А если DrawPolygon заменить на просто Polygon - то получается психодел)

Я как-то вот с этим разбирался, http://blog.rogach.org/2015/08/how-to-create-your-own-simple-3d-render.html и мне даже для этой пирамидосферы удалось задать правильный порядок обхода ребер, чтобы лицевая сторона всегда наружу была, с сохранением освещенности и цветности всей приблуды. но это java, портировать все свое дерево на нее я пока не хочу. Там у меня склонятор (на основе padeg.jar, перепиленной почти до основания) и загадыватель фраз для игры в “крокодила” зреет.

Что-то я столкнулся с проблемой, давно на паскале не писал Thor.pas (4,0 КБ)

попробовал добавить z-index спроецированным точкам и полигонам, но все перестало работать.

пока не понял почему, вроде если рисовать сразу, то все нормально, а если класть в лист - то все съезжает в один полигон. вроде везде новые объекты создаются, ссылки на значения везде должны быть свои…

Попробую разобраться но … Вот кстати интересный цикл статей про 3D https://habrahabr.ru/post/248153/

Нашёл,надо создавать копию массива и тогда работает.
constructor create(params points: array of ZIndexedPoint); begin self.points := points.ToArray(); end; и соответственно Polygon(lstSorted[i].getDrawingPoints()); И если дальше играться то…Thor.pas (3,7 КБ)

Красиво. Но почему-то одна часть тора тоньше другой

Для исправления этого, надо использовать матрицу проекции и перспективу.Сейчас изучаю соответствующие.

Да, подтверждаю, когда в Компасе 3D модели делаешь - если перспективу выключить все именно так и происходит - дальняя часть получается шире ближней.

Дальше надо будет придумать, как те полигоны, которые раньше всех в z-index лежат и полностью перекрываются более близкими - не отрисовывать вообще. по идее отрисовка занимает больше времени, чем расчеты, поэтому производительность должна улучшиться. (на одном таком торе может и незаметно будет, там полигонов то всего ничего, сейчас еси разбивка на точки не менялась - то что-то около 720 должно быть. А если за что-то более серьезное браться - то оптимизация будет полезна)

Можно менять константы n m произведение которых равно числу полигнов. Вот за вечер реализовал про-то что Вы пишите, но оптимизация пока не очень. А Вы @Admin можете тоже посмотреть.Thor2.pas (4,4 КБ)