Advertisement
Old_But_Gold

Untitled

Feb 12th, 2025
14
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.91 KB | None | 0 0
  1. using System;
  2. using System.Numerics;
  3. using System.Threading.Tasks;
  4. using System.Windows;
  5. using System.Windows.Media;
  6. using System.Windows.Media.Imaging;
  7. using AKG.Core.Extensions;
  8. using AKG.Core.Objects;
  9. using AKG.Core.VectorTransformations;
  10.  
  11. namespace AKG.Core.Renderer
  12. {
  13. public static class Rasterizer
  14. {
  15. // Z-буфер: массив [width, height] (первый индекс – x, второй – y)
  16. private static float[,]? _zBuffer;
  17.  
  18. /// <summary>
  19. /// Инициализирует Z-буфер заданного размера, заполняя его значениями, равными camera.ZFar.
  20. /// </summary>
  21. public static void ClearZBuffer(int width, int height, Camera camera)
  22. {
  23. _zBuffer ??= new float[width, height];
  24. float initDepth = camera.ZFar;
  25. for (int x = 0; x < width; x++)
  26. for (int y = 0; y < height; y++)
  27. _zBuffer[x, y] = initDepth;
  28. }
  29.  
  30. /// <summary>
  31. /// Растеризует треугольники для каждой грани модели:
  32. /// - Для граней с 3+ вершинами выполняется фан‑трайангуляция.
  33. /// - Выполняется backface culling (на основе нормали) и рассчитывается интенсивность по модели Ламберта.
  34. /// - Каждый полученный треугольник заполняется с использованием сканирующей линии и Z‑теста.
  35. /// </summary>
  36. public static unsafe void DrawFilledTriangle(ObjModel model, WriteableBitmap wb, Color color, Camera camera)
  37. {
  38. int width = wb.PixelWidth;
  39. int height = wb.PixelHeight;
  40.  
  41. // Вычисляем мировую матрицу модели
  42. Matrix4x4 world = Transformations.CreateWorldTransform(
  43. model.Scale,
  44. Matrix4x4.CreateFromYawPitchRoll(model.Rotation.Y, model.Rotation.X, model.Rotation.Z),
  45. model.Translation);
  46.  
  47. wb.Lock();
  48. unsafe
  49. {
  50. int* buffer = (int*)wb.BackBuffer;
  51. // Обнуляем Z-буфер для данного кадра
  52. ClearZBuffer(width, height, camera);
  53.  
  54. // Обработка граней параллельно
  55. Parallel.ForEach(model.Faces, face =>
  56. {
  57. if (face.Vertices.Count < 3)
  58. return;
  59.  
  60. // Вычисляем мировые координаты первых трёх вершин для нормали
  61. Vector3 worldV0 = Vector4.Transform(model.OriginalVertices[face.Vertices[0].VertexIndex - 1], world).AsVector3();
  62. Vector3 worldV1 = Vector4.Transform(model.OriginalVertices[face.Vertices[1].VertexIndex - 1], world).AsVector3();
  63. Vector3 worldV2 = Vector4.Transform(model.OriginalVertices[face.Vertices[2].VertexIndex - 1], world).AsVector3();
  64.  
  65. // Вычисляем нормаль грани
  66. Vector3 normal = Vector3.Normalize(Vector3.Cross(worldV1 - worldV0, worldV2 - worldV0));
  67.  
  68. // Backface culling: если грань повернута от камеры, пропускаем её
  69. if (Vector3.Dot(normal, camera.Eye - worldV0) <= 0)
  70. return;
  71.  
  72. // Применяем затенение по Ламберту: базовый цвет модифицируется в зависимости от интенсивности освещения
  73. Color shadedColor = color.ApplyLambert(normal, camera.LambertLight);
  74.  
  75. // Фан-трайангуляция: разбиваем грань на треугольники (v0, v_i, v_{i+1})
  76. for (int j = 1; j < face.Vertices.Count - 1; j++)
  77. {
  78. int idx0 = face.Vertices[0].VertexIndex - 1;
  79. int idx1 = face.Vertices[j].VertexIndex - 1;
  80. int idx2 = face.Vertices[j + 1].VertexIndex - 1;
  81. if (idx0 < 0 || idx1 < 0 || idx2 < 0 ||
  82. idx0 >= model.TransformedVertices.Length ||
  83. idx1 >= model.TransformedVertices.Length ||
  84. idx2 >= model.TransformedVertices.Length)
  85. continue;
  86.  
  87. // Получаем экранные координаты из TransformedVertices
  88. Vector3 s0 = model.TransformedVertices[idx0].AsVector3();
  89. Vector3 s1 = model.TransformedVertices[idx1].AsVector3();
  90. Vector3 s2 = model.TransformedVertices[idx2].AsVector3();
  91.  
  92. // Запускаем алгоритм сканирующей линии для заполнения треугольника
  93. DrawFilledTriangleScanline(s0, s1, s2, shadedColor, buffer, width, height);
  94. }
  95. });
  96. }
  97. wb.AddDirtyRect(new Int32Rect(0, 0, wb.PixelWidth, wb.PixelHeight));
  98. wb.Unlock();
  99. }
  100.  
  101. /// <summary>
  102. /// Заполняет один треугольник, заданный тремя экранными вершинами, с использованием алгоритма сканирующей линии.
  103. /// Для каждой scanline вычисляются точки пересечения с двумя сторонами треугольника,
  104. /// после чего по горизонтали выполняется линейная интерполяция глубины (z) и производится Z-тест.
  105. /// </summary>
  106. private static unsafe void DrawFilledTriangleScanline(Vector3 v0, Vector3 v1, Vector3 v2, Color color, int* buffer, int width, int height)
  107. {
  108. // Сортировка вершин по Y (от наименьшей к наибольшей)
  109. Vector3[] verts = new Vector3[] { v0, v1, v2 };
  110. Array.Sort(verts, (a, b) => a.Y.CompareTo(b.Y));
  111. v0 = verts[0];
  112. v1 = verts[1];
  113. v2 = verts[2];
  114.  
  115. // Определяем границы scanline (округленные и ограниченные экраном)
  116. int yStart = Math.Max((int)Math.Ceiling(v0.Y), 0);
  117. int yEnd = Math.Min((int)Math.Floor(v2.Y), height - 1);
  118.  
  119. // Вычисляем параметры для основной стороны (от v0 до v2)
  120. float invDy02 = (v2.Y - v0.Y) != 0 ? 1f / (v2.Y - v0.Y) : 0f;
  121. float x02Slope = (v2.X - v0.X) * invDy02;
  122. float z02Slope = (v2.Z - v0.Z) * invDy02;
  123.  
  124. // Для верхней части (от v0 до v1)
  125. float invDy01 = (v1.Y - v0.Y) != 0 ? 1f / (v1.Y - v0.Y) : 0f;
  126. float x01Slope = (v1.X - v0.X) * invDy01;
  127. float z01Slope = (v1.Z - v0.Z) * invDy01;
  128.  
  129. // Для нижней части (от v1 до v2)
  130. float invDy12 = (v2.Y - v1.Y) != 0 ? 1f / (v2.Y - v1.Y) : 0f;
  131. float x12Slope = (v2.X - v1.X) * invDy12;
  132. float z12Slope = (v2.Z - v1.Z) * invDy12;
  133.  
  134. // Проходим по каждой scanline
  135. for (int y = yStart; y <= yEnd; y++)
  136. {
  137. // t – параметр для основной стороны v0->v2
  138. float t = (y - v0.Y);
  139. float xA_main = v0.X + x02Slope * t;
  140. float zA_main = v0.Z + z02Slope * t;
  141.  
  142. // Определяем, какую сторону использовать для левой части:
  143. float xA_side, zA_side;
  144. if (y < v1.Y)
  145. {
  146. // Верхняя часть: используем сторону v0->v1
  147. float tSide = (y - v0.Y);
  148. xA_side = v0.X + x01Slope * tSide;
  149. zA_side = v0.Z + z01Slope * tSide;
  150. }
  151. else
  152. {
  153. // Нижняя часть: используем сторону v1->v2
  154. float tSide = (y - v1.Y);
  155. xA_side = v1.X + x12Slope * tSide;
  156. zA_side = v1.Z + z12Slope * tSide;
  157. }
  158.  
  159. // Определяем левую и правую точки для данной scanline:
  160. float xLeft = Math.Min(xA_main, xA_side);
  161. float xRight = Math.Max(xA_main, xA_side);
  162. float zLeft = (xA_main <= xA_side) ? zA_main : zA_side;
  163. float zRight = (xA_main <= xA_side) ? zA_side : zA_main;
  164.  
  165. // Ограничиваем по границам экрана
  166. int xStart = Math.Max((int)Math.Ceiling(xLeft), 0);
  167. int xEnd = Math.Min((int)Math.Floor(xRight), width - 1);
  168.  
  169. // Заполняем scanline с интерполяцией глубины
  170. float span = xRight - xLeft;
  171. for (int x = xStart; x <= xEnd; x++)
  172. {
  173. float factor = (span != 0) ? (x - xLeft) / span : 0f;
  174. float depth = zLeft + factor * (zRight - zLeft);
  175. // Z-тест (индексация Z-буфера: [x, y])
  176. if (depth < _zBuffer![x, y])
  177. {
  178. _zBuffer[x, y] = depth;
  179. buffer[y * width + x] = color.ColorToIntBGRA();
  180. }
  181. }
  182. }
  183. }
  184. }
  185. }
  186.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement