我正在使用3D引擎,最近添加了一个列表,以便可以对网格的三角形进行排序,并从最远的三角形渲染到最接近的三角形,以免出现重叠的三角形。自从我添加了这种内存使用量后,我不确定该如何解决。
我已经尝试过将列表转换为数组并将其转换为链接列表,但是都没有给我所需的性能。
//#define wireframe
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.IO;
using static System.Diagnostics.Process;
public class Ref<T> // Way around using pointers.
{
private readonly Action<T> setter;
private readonly Func<T> getter;
public Ref(Action<T> setter, Func<T> getter)
{
this.setter = setter;
this.getter = getter;
}
public T Value { get { return getter(); } set { setter(value); } }
}
public class Vector3
{
public float x, y, z;
public Vector3(float xp = 0, float yp = 0, float zp = 0)
{
x = xp;
y = yp;
z = zp;
}
public static Vector3 operator +(Vector3 vec1, Vector3 vec2) => new Vector3(vec1.x + vec2.x, vec1.y + vec2.y, vec1.z + vec2.z);
public static Vector3 operator -(Vector3 vec1, Vector3 vec2) => new Vector3(vec1.x - vec2.x, vec1.y - vec2.y, vec1.z - vec2.z);
public static Vector3 operator *(Vector3 vec1, Vector3 vec2) => new Vector3(vec1.x * vec2.x, vec1.y * vec2.y, vec1.z * vec2.z);
public static Vector3 operator /(Vector3 vec1, Vector3 vec2) => new Vector3(vec1.x / vec2.x, vec1.y / vec2.y, vec1.z / vec2.z);
public static Vector3 operator +(Vector3 vec1, float val) => new Vector3(vec1.x + val, vec1.y + val, vec1.z + val);
public static Vector3 operator -(Vector3 vec1, float val) => new Vector3(vec1.x - val, vec1.y - val, vec1.z - val);
public static Vector3 operator *(Vector3 vec1, float val) => new Vector3(vec1.x * val, vec1.y * val, vec1.z * val);
public static Vector3 operator /(Vector3 vec1, float val) => new Vector3(vec1.x / val, vec1.y / val, vec1.z / val);
};
public class Trig : IComparable<Trig>
{
public List<Vector3> points;
public Color col = Colors.Orange;
public Trig(Vector3 vec1, Vector3 vec2, Vector3 vec3, Color colr)
{
points = new List<Vector3> { vec1, vec2, vec3 };
col = colr;
}
public Trig(Vector3 vec1, Vector3 vec2, Vector3 vec3)
{
points = new List<Vector3> { vec1, vec2, vec3 };
}
public int CompareTo(Trig t2)
{
return (int)((this.points[0].z + this.points[1].z + this.points[2].z) / 3.0f) - (int)((t2.points[0].z + t2.points[1].z + t2.points[2].z) / 3.0f);
}
public Color GetColour(float lum, Color orig) => orig * lum;
};
public class Mesh
{
public List<Trig> tris;
public Mesh(List<Trig> trigs = null)
{
tris = trigs;
}
public void MakeCube(float width = 1, float height = 1, float length = 1)
{
tris = new List<Trig>{
// SOUTH
new Trig(new Vector3(0.0f * width, 0.0f * height, 0.0f * length), new Vector3(0.0f * width, 1.0f * height, 0.0f * length), new Vector3(1.0f * width, 1.0f * height, 0.0f * length)),
new Trig(new Vector3(0.0f * width, 0.0f * height, 0.0f * length), new Vector3(1.0f * width, 1.0f * height, 0.0f * length), new Vector3(1.0f * width, 0.0f * height, 0.0f * length)),
// EAST
new Trig(new Vector3(1.0f * width, 0.0f * height, 0.0f * length), new Vector3(1.0f * width, 1.0f * height, 0.0f * length), new Vector3(1.0f * width, 1.0f * height, 1.0f * length)),
new Trig(new Vector3(1.0f * width, 0.0f * height, 0.0f * length), new Vector3(1.0f * width, 1.0f * height, 1.0f * length), new Vector3(1.0f * width, 0.0f * height, 1.0f * length)),
// NORTH
new Trig(new Vector3(1.0f * width, 0.0f * height, 1.0f * length), new Vector3(1.0f * width, 1.0f * height, 1.0f * length), new Vector3(0.0f * width, 1.0f * height, 1.0f * length)),
new Trig(new Vector3(1.0f * width, 0.0f * height, 1.0f * length), new Vector3(0.0f * width, 1.0f * height, 1.0f * length), new Vector3(0.0f * width, 0.0f * height, 1.0f * length)),
// WEST
new Trig(new Vector3(0.0f * width, 0.0f * height, 1.0f * length), new Vector3(0.0f * width, 1.0f * height, 1.0f * length), new Vector3(0.0f * width, 1.0f * height, 0.0f * length)),
new Trig(new Vector3(0.0f * width, 0.0f * height, 1.0f * length), new Vector3(0.0f * width, 1.0f * height, 0.0f * length), new Vector3(0.0f * width, 0.0f * height, 0.0f * length)),
// TOP
new Trig(new Vector3(0.0f * width, 1.0f * height, 0.0f * length), new Vector3(0.0f * width, 1.0f * height, 1.0f * length), new Vector3(1.0f * width, 1.0f * height, 1.0f * length)),
new Trig(new Vector3(0.0f * width, 1.0f * height, 0.0f * length), new Vector3(1.0f * width, 1.0f * height, 1.0f * length), new Vector3(1.0f * width, 1.0f * height, 0.0f * length)),
// BOTTOM
new Trig(new Vector3(1.0f * width, 0.0f * height, 1.0f * length), new Vector3(0.0f * width, 0.0f * height, 1.0f * length), new Vector3(0.0f * width, 0.0f * height, 0.0f * length)),
new Trig(new Vector3(1.0f * width, 0.0f * height, 1.0f * length), new Vector3(0.0f * width, 0.0f * height, 0.0f * length), new Vector3(1.0f * width, 0.0f * height, 0.0f * length)),
};
}
public bool LoadObjectFromFile(string path)
{
if (!File.Exists(path))
{
return false;
}
else
{
tris = new List<Trig>();
StreamReader file = new StreamReader(path);
List<Vector3> vertCache = new List<Vector3>(); // Cache of verts
string line;
while ((line = file.ReadLine()) != null)
{
if (line != "")
{
if (line[0] == "v"[0])
{
Vector3 v = new Vector3(); // temporary vertex
string[] temp = line.Split(" "[0]); // Split string into array of strings seperated by space
v.x = float.Parse(temp[1]); v.y = float.Parse(temp[2]); v.z = float.Parse(temp[3]); // set temp vertex to the values specified in string.
vertCache.Add(v);
}
if (line[0] == "f"[0])
{
string[] temp = line.Split(" "[0]); // Split string into array of strings seperated by space
tris.Add(new Trig(vertCache[int.Parse(temp[1]) - 1], vertCache[int.Parse(temp[2]) - 1], vertCache[int.Parse(temp[3]) - 1])); // Add a triangle with values from the index specified in string.
}
}
}
Console.WriteLine(tris[0].points[0].x);
file.Close();
return true;
}
}
public void Resize(float x)
{
int counter = 0;
foreach(Trig tri in tris)
{
tris[counter] = new Trig(tri.points[0] * x, tri.points[1] * x, tri.points[2] * x);
counter++;
}
}
public void Resize(float x, float y, float z)
{
int counter = 0;
foreach (Trig tri in tris)
{
Vector3 tempVec = new Vector3(x,y,z);
tris[counter] = new Trig(tri.points[0] * tempVec, tri.points[1] * tempVec, tri.points[2] * tempVec);
counter++;
}
}
};
public class Mat4x
{
public float[,] matrix = new float[4, 4];
public Mat4x()
{
matrix.Initialize();
}
}
public class Camera
{
public Vector3 position = new Vector3();
public float fNear;
public float fFar;
public float fFov;
public float fAspectRatio; // = (float)width / (float)height
public float fFovRad;
public Camera(Vector3 pos, float near = 0.1f, float far = 1000.0f, float fov = 90.0f, float width = 640, float height = 640)
{
position = pos;
fNear = near;
fFar = far;
fFov = fov;
fAspectRatio = width / height;
fFovRad = 1.0f / (float)Math.Tan(fFov * 0.5f / 180.0f * 3.14159f);
}
public float x {get{ return position.x; } set{ position.x = value; }}
public float y { get { return position.y; } set { position.y = value; } }
public float z { get { return position.z; } set { position.z = value; } }
}
public class Light
{
public Vector3 position = new Vector3();
private Vector3 _direction = new Vector3();
public bool isInfinite = false;
public float radius = 1.0f;
public Color col;
public float Normaliser(Vector3 vec)
{
return (float)Math.Sqrt((vec.x * vec.x) + (vec.y * vec.y) + (vec.z * vec.z));
}
public Light(Vector3 dirc, Vector3 pos, Color colr, bool infin = false, float rad = 1.0f)
{
position = pos;
direction = dirc;
isInfinite = infin;
radius = rad;
col = colr;
_direction = new Vector3(dirc.x / Normaliser(dirc), dirc.y / Normaliser(dirc), dirc.z / Normaliser(dirc));
}
public Light(Vector3 dirc, Vector3 pos)
{
position = pos;
direction = dirc;
isInfinite = true;
radius = 0.0f;
col = Colors.WhiteSmoke;
_direction = new Vector3(dirc.x / Normaliser(dirc), dirc.y / Normaliser(dirc), dirc.z / Normaliser(dirc));
}
public Light(Vector3 dirc)
{
direction = dirc;
isInfinite = true;
radius = 0.0f;
col = Colors.WhiteSmoke;
_direction = new Vector3(dirc.x / Normaliser(dirc), dirc.y / Normaliser(dirc), dirc.z / Normaliser(dirc));
}
public float x { get { return position.x; } set { position.x = value; } }
public float y { get { return position.y; } set { position.y = value; } }
public float z { get { return position.z; } set { position.z = value; } }
public Vector3 direction { get => _direction; set => _direction = new Vector3 (value.x / Normaliser(value), value.y / Normaliser(value), value.z / Normaliser(value)); }
}
namespace _2dEngine
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
int height, width;
WriteableBitmap wrBmp;
Mesh cube;
Mat4x matproj, matRotZ, matRotX;
float fTheta, fElapsedTime;
Camera maincam;
Light mainlight;
List<Trig> trigsToDraw = new List<Trig>();
void MultiplyMatrixVector(Vector3 i, Ref<Vector3> o, Mat4x m)
{ // multiply a vector and a 4x matrix to get a vector which we store in o.
Vector3 newVec = new Vector3
{
x = (i.x * m.matrix[0, 0]) + (i.y * m.matrix[1, 0]) + (i.z * m.matrix[2, 0]) + m.matrix[3, 0],
y = (i.x * m.matrix[0, 1]) + (i.y * m.matrix[1, 1]) + (i.z * m.matrix[2, 1]) + m.matrix[3, 1],
z = (i.x * m.matrix[0, 2]) + (i.y * m.matrix[1, 2]) + (i.z * m.matrix[2, 2]) + m.matrix[3, 2]
};
float w = (i.x * m.matrix[0, 3]) + (i.y * m.matrix[1, 3]) + (i.z * m.matrix[2, 3]) + m.matrix[3, 3];
if (w != 0.0f)
{
newVec.x /= w; newVec.y /= w; newVec.z /= w;
}
o.Value = newVec;
//return newVec;
}
void DrawTriangle(int x1, int y1, int x2, int y2, int x3, int y3, Color col)
{
wrBmp.DrawLine(x1, y1, x2, y2, col);
wrBmp.DrawLine(x2, y2, x3, y3, col);
wrBmp.DrawLine(x3, y3, x1, y1, col);
}
void FillTriangle(int x1, int y1, int x2, int y2, int x3, int y3, Color col) => wrBmp.FillTriangle(x1, y1, x2, y2, x3, y3, col);
private void ViewPort_Loaded(object sender, RoutedEventArgs e)
{
matproj = new Mat4x();
matRotZ = new Mat4x();
matRotX = new Mat4x();
width = (int)this.ViewPortContainer.ActualWidth;
height = (int)this.ViewPortContainer.ActualHeight;
wrBmp = BitmapFactory.New(width, height);
ViewPort.Source = wrBmp;
maincam = new Camera(BlankVector3(), width: width, height: height);
mainlight = new Light(new Vector3(0, 0, -1));
cube = new Mesh();
cube.LoadObjectFromFile(@"D:\Users\Electrode\Desktop\Cylinder.obj");
float fNear = 0.1f;
float fFar = 1000.0f;
float fFov = 90.0f;
float fAspectRatio = (float)width / (float)height;
float fFovRad = 1.0f / (float)Math.Tan((double)(fFov * 0.5f / 180.0f * 3.14159f));
fTheta = 1.0f;
matproj.matrix[0, 0] = fAspectRatio * fFovRad;
matproj.matrix[1, 1] = fFovRad;
matproj.matrix[2, 2] = fFar / (fFar - fNear);
matproj.matrix[3, 2] = (-fFar * fNear) / (fFar - fNear);
matproj.matrix[2, 3] = 1.0f;
matproj.matrix[3, 3] = 0.0f;
CompositionTarget.Rendering += CompostitionTarget_Rendering;
}
private void CompostitionTarget_Rendering(object sender, EventArgs e)
{
wrBmp.Clear(Colors.Black);
Trig triRotatedZ, triRotatedZX, triTranslated;
// Rotation Z
matRotZ.matrix[0, 0] = (float)Math.Cos(fTheta);
matRotZ.matrix[0, 1] = (float)Math.Sin(fTheta);
matRotZ.matrix[1, 0] = (float)-Math.Sin(fTheta);
matRotZ.matrix[1, 1] = (float)Math.Cos(fTheta);
matRotZ.matrix[2, 2] = 1;
matRotZ.matrix[3, 3] = 1;
// Rotation X
matRotX.matrix[0, 0] = 1;
matRotX.matrix[1, 1] = (float)Math.Cos(fTheta * 0.5f);
matRotX.matrix[1, 2] = (float)Math.Sin(fTheta * 0.5f);
matRotX.matrix[2, 1] = 0 - (float)Math.Sin(fTheta * 0.5f);
matRotX.matrix[2, 2] = (float)Math.Cos(fTheta * 0.5f);
matRotX.matrix[3, 3] = 1;
fElapsedTime = (float)TimeSpan.FromTicks(DateTime.UtcNow.Ticks - GetCurrentProcess().StartTime.ToUniversalTime().Ticks).TotalSeconds; // Time open in ticks.
// Alternate theta
// fTheta += 1.0f;
fTheta += (1.5f * fElapsedTime / 600) / 4; // Rotate our cube
/// Debug shit
/// Console.WriteLine(fTheta);
foreach (Trig tri in cube.tris) // Iterate through each triangle and render it correctly
{
// setup trigs
triRotatedZ = BlankTrig();
triRotatedZX = BlankTrig();
// Rotate in Z-Axis
MultiplyMatrixVector(tri.points[0], new Ref<Vector3>( // First point rotation
setter: yes => { triRotatedZ.points[0] = yes; }, // our setter for the variable
getter: () => triRotatedZ.points[0]), matRotZ); // our getter
MultiplyMatrixVector(tri.points[1], new Ref<Vector3>( // Second point
setter: yes => { triRotatedZ.points[1] = yes; },
getter: () => triRotatedZ.points[1]), matRotZ);
MultiplyMatrixVector(tri.points[2], new Ref<Vector3>( // Etc
setter: yes => { triRotatedZ.points[2] = yes; },
getter: () => triRotatedZ.points[2]), matRotZ);
// Rotate in X-Axis
MultiplyMatrixVector(triRotatedZ.points[0], new Ref<Vector3>( // See above
setter: yes => { triRotatedZX.points[0] = yes; },
getter: () => triRotatedZX.points[0]), matRotX);
MultiplyMatrixVector(triRotatedZ.points[1], new Ref<Vector3>(
setter: yes => { triRotatedZX.points[1] = yes; },
getter: () => triRotatedZX.points[1]), matRotX);
MultiplyMatrixVector(triRotatedZ.points[2], new Ref<Vector3>(
setter: yes => { triRotatedZX.points[2] = yes; },
getter: () => triRotatedZX.points[2]), matRotX);
// Offset into the screen
triTranslated = triRotatedZX;
triTranslated.points[0].z = triRotatedZX.points[0].z + 120.0f;
triTranslated.points[1].z = triRotatedZX.points[1].z + 120.0f;
triTranslated.points[2].z = triRotatedZX.points[2].z + 120.0f;
// Use Cross-Product to get surface normal
Vector3 normal, line1, line2;
normal = line1 = line2 = BlankVector3();
line1 = triTranslated.points[1] - triTranslated.points[0];
line2 = triTranslated.points[2] - triTranslated.points[0];
normal.x = (line1.y * line2.z) - (line1.z * line2.y);
normal.y = (line1.z * line2.x) - (line1.x * line2.z);
normal.z = (line1.x * line2.y) - (line1.y * line2.x);
// It's normally normal to normalise the normal
float l = (float)Math.Sqrt((normal.x * normal.x) + (normal.y * normal.y) + (normal.z * normal.z));
normal /= l;
if (normal.x * (triTranslated.points[0].x - maincam.x) +
normal.y * (triTranslated.points[0].y - maincam.y) +
normal.z * (triTranslated.points[0].z - maincam.z) < 0.0f)
{
float dp = normal.x * mainlight.direction.x + normal.y * mainlight.direction.y + normal.z * mainlight.direction.z;
triTranslated.col = triTranslated.GetColour(dp, triTranslated.col);
// Project triangle from 3d to 2d
Trig triProj = new Trig(new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 0.0f, 0.0f))
{
col = triTranslated.col
}; // make a blank triangle
MultiplyMatrixVector(triTranslated.points[0], new Ref<Vector3>( // First point projection
setter: yes => { triProj.points[0] = yes; },
getter: () => triProj.points[0]), matproj);
MultiplyMatrixVector(triTranslated.points[1], new Ref<Vector3>( // Second point
setter: yes => { triProj.points[1] = yes; },
getter: () => triProj.points[1]), matproj);
MultiplyMatrixVector(triTranslated.points[2], new Ref<Vector3>( // Etc
setter: yes => { triProj.points[2] = yes; },
getter: () => triProj.points[2]), matproj);
//Scale
triProj.points[0].x += 1.0f; triProj.points[0].y += 1.0f;
triProj.points[1].x += 1.0f; triProj.points[1].y += 1.0f;
triProj.points[2].x += 1.0f; triProj.points[2].y += 1.0f;
triProj.points[0].x *= 0.5f * width;
triProj.points[0].y *= 0.5f * height;
triProj.points[1].x *= 0.5f * width;
triProj.points[1].y *= 0.5f * height;
triProj.points[2].x *= 0.5f * width;
triProj.points[2].y *= 0.5f * height;
trigsToDraw.Add(triProj); // Either this,
}
}
Array.Sort(trigsToDraw.ToArray()); // This,
foreach(Trig triDraw in trigsToDraw) // Or This is causing a performance drop
{
// Draw triangles
FillTriangle(
(int)triDraw.points[0].x,
(int)triDraw.points[0].y,
(int)triDraw.points[1].x,
(int)triDraw.points[1].y,
(int)triDraw.points[2].x,
(int)triDraw.points[2].y,
triDraw.col);
#if (wireframe)
DrawTriangle( // Draw wireframe for debug reasons, if you want.
(int)triDraw.points[0].x,
(int)triDraw.points[0].y,
(int)triDraw.points[1].x,
(int)triDraw.points[1].y,
(int)triDraw.points[2].x,
(int)triDraw.points[2].y,
Color.FromRgb(0, 255, 0));
#endif
}
}
private static Vector3 BlankVector3()
{
return new Vector3();
}
private static Trig BlankTrig()
{
return new Trig(new Vector3(), new Vector3(), new Vector3());
}
}
}
我希望在不浪费过多内存的情况下将3D对象渲染到屏幕上。
答案 0 :(得分:0)
我已经找出问题的根源
trigsToDraw.Add(triProj); // part 1 of the problem
和
foreach(Trig triDraw in trigsToDraw)
{
// Draw triangles
FillTriangle(
(int)triDraw.points[0].x,
(int)triDraw.points[0].y,
(int)triDraw.points[1].x,
(int)triDraw.points[1].y,
(int)triDraw.points[2].x,
(int)triDraw.points[2].y,
triDraw.col);
#if (wireframe)
DrawTriangle( // Draw wireframe for debug reasons, if you want.
(int)triDraw.points[0].x,
(int)triDraw.points[0].y,
(int)triDraw.points[1].x,
(int)triDraw.points[1].y,
(int)triDraw.points[2].x,
(int)triDraw.points[2].y,
Color.FromRgb(0, 255, 0));
#endif
}// Part 2 of the problem
它使用大量内存的原因是因为我每次都忘记清除列表... 有时我会怀疑我的智力。