我正在使用.net框架windowsforms gdi+在表单上绘制我的模拟并渲染它。到目前为止,我希望它渲染相同,但我想在 directX(SharpDX 2d) 中渲染它。我知道我的模拟有错误和其他类似的事情,但对我来说,它看起来很棒,作为简单的模拟(我仍在学习如何进行模拟)。如果有人知道我如何用我的代码做到这一点,我很感激,而且我也希望对此进行优化!
代码:
public class Body
{
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public double Mass { get; set; }
public double X { get; set; }
public double Y { get; set; }
public double VX { get; set; }
public double VY { get; set; }
public double AX { get; set; }
public double AY { get; set; }
public int Radius { get; set; }
public bool IsSelected { get; set; }
public bool IsStatic { get; set; }
public bool Visible { get; set; }
public Brush BodyColor { get; set; } = Brushes.Red;
public List<Point> OrbitTrail { get; set; } = new List<Point>();
public double Elasticity { get; set; }
public double FrictionCoefficient { get; set; }
public double Rotation { get; set; }
public double AngularVelocity { get; set; }
public double AngularAcceleration { get; set; }
public BigInteger Temperature { get; set; }
public List<Body> AttachedObjects { get; set; } = new List<Body>();
public string Type { get; set; } = "Ellipse";
public Body() { }
}
public partial class GravitySim : Form
{
#region variables
private double G = 6.67430e-11;
private QuadTree quadTree;
private double timeStep = 1.0;
private int numSimulationSteps = 5000;
private const double scaleFactor = 1e5;
private const double MassScaleFactor = 1e22;
private bool drawQuadTree = false;
private bool selectionMode = true;
private bool velocityVectors = true;
private List<Body> currentPreset = null;
// Declare a variable for real-time acceleration factor
private double realTimeAcceleration = 1.0;
private int maxOrbitPoints = 50000;
private const double TargetFPS = 120.0;
private const double TargetFrameTime = 1000.0 / TargetFPS;
private List<Body> bodies;
private Timer timer;
private Stopwatch frameStopwatch = new Stopwatch();
private Stopwatch renderStopwatch = new Stopwatch();
private Stopwatch SimStopwatch = new Stopwatch();
private int framesCount = 0;
private double frameRate = 0.0;
private double frames;
#endregion
public GravitySim()
{
InitializeComponent();
InitializeSimulation();
this.SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
// Inicjalizacja KONTROLEK
this.MouseDoubleClick += Form1_MouseDoubleClick;
}
#region basicFNC
private void StartRenderTimer()
{
renderStopwatch.Restart();
}
private void StopRenderTimer()
{
renderStopwatch.Stop();
double renderTime = renderStopwatch.Elapsed.TotalMilliseconds;
label28.Text = $"Render Time: {renderTime:F2} ms";
}
private void InitializeSimulation()
{
InitializeColorComboBox();
InitializePresets();
List<Body> bodies = new List<Body>()
{
new Body { Mass = 6e5, X = 200 + 350, Y = 500, VX = 0, VY = 0, Radius = 20, IsStatic = true, BodyColor = Brushes.Red },
new Body { Mass = 4e4, X = 450 + 300, Y = 350, VX = 0, VY = -22 / scaleFactor, Radius = 5, IsStatic = false, BodyColor = Brushes.Blue },
new Body { Mass = 2.5e4, X = 100 + 300, Y = 400, VX = -40 / scaleFactor, VY = 18 / scaleFactor, Radius = 5, IsStatic = false, BodyColor = Brushes.Yellow },
// Dodaj kolejne ciała według potrzeb
};
LoadPreset(bodies);
quadTree = new QuadTree(new Rectangle(0, 0, ClientSize.Width - panel1.Width, ClientSize.Height), 1);
QuadTreeInsertBodies();
// Inicjalizacja timera
timer = new Timer();
timer.Interval = 1; // Czas w milisekundach między krokami symulacji
timer.Tick += Timer_Tick;
SimStopwatch.Start();
}
private void QuadTreeInsertBodies()
{
quadTree.Clear();
foreach (var body in bodies)
{
quadTree.Insert(body);
}
}
private void LoadPreset(List<Body> bodies)
{
this.bodies = bodies;
this.Invalidate();
}
private void InitializePresets()
{
comboBox2.Items.Add("Example preset");
comboBox2.Items.Add("Earth orbiting Sun - 2 bodies");
comboBox2.SelectedIndex = 0;
}
private void InitializeColorComboBox()
{
// Pobierz właściwości Brushes za pomocą refleksji
var brushProperties = typeof(Brushes).GetProperties();
// Dodaj kolory do ComboBox
foreach (var brushProperty in brushProperties)
{
Brush brush = (Brush)brushProperty.GetValue(null, null);
// Sprawdź, czy kolor jest SolidColorBrush (możesz dostosować warunek do innych rodzajów pędzli, jeśli to konieczne)
if (brush is SolidBrush)
{
comboBox1.Items.Add(brushProperty.Name);
}
}
// Ustaw domyślny kolor
comboBox1.SelectedIndex = 0;
}
#endregion
private Body selectedBody = null;
private void Form1_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (selectionMode)
{
Point mousePosition = e.Location;
// Check if the mouse click is within the bounds of any body
bool clickedOnBody = false;
foreach (var body in bodies)
{
if (IsMouseOnBody(mousePosition, body))
{
// Unselect the currently selected body
if (selectedBody != null)
{
selectedBody.IsSelected = false;
}
body.IsSelected = true;
selectedBody = body;
clickedOnBody = true;
// Pobierz kolor z Brush
Color BodyColor = (body.BodyColor as SolidBrush)?.Color ?? Color.Black;
// Znajdź indeks koloru ciała w comboBox1
int colorIndex = comboBox1.Items.IndexOf(BodyColor.Name);
// Jeśli indeks jest większy lub równy zeru, ustaw go jako zaznaczony
if (colorIndex >= 0)
{
comboBox1.SelectedIndex = colorIndex;
this.BodyColor.BackColor = BodyColor;
}
checkBox4.Checked = selectedBody.IsStatic;
if (body.Mass > Math.Pow(10, 9) || body.Mass < Math.Pow(10, -3)) // Sprawdź, czy masa jest większa niż miliard
{
label9.Text = $"Mass: {ConvertToScientificNotation(body.Mass)} kg";
bodyMass.Text = ConvertToScientificNotation(body.Mass) + " kg";
}
else
{
label9.Text = $"Mass: {body.Mass.ToString("F2")} kg";
bodyMass.Text = body.Mass.ToString("F2") + " kg";
}
break;
}
}
// Clicked outside of any body, unselect all bodies
if (!clickedOnBody)
{
if (selectedBody != null)
{
selectedBody.IsSelected = false;
selectedBody = null;
}
foreach (var body in bodies)
{
body.IsSelected = false;
}
}
this.Invalidate(); // Redraw to reflect the changes
// Dodaj kod, który decyduje, czy panel2 powinien być widoczny
if (selectedBody != null)
{
// Panel powinien być widoczny, ponieważ jest wybrana jakakolwiek ciało
rjButton8.Location = new Point(5, 403);
label24.Location = new Point(75, 443);
comboBox2.Location = new Point(15, 460);
rjButton10.Location = new Point(6, 487);
panel2.Show();
rjButton9.Show();
rjButton13.Show();
label16.Show();
}
else
{
// Panel powinien być ukryty, ponieważ nie jest wybrane żadne ciało
rjButton8.Location = new Point(5, 191);
label24.Location = new Point(76, 231);
comboBox2.Location = new Point(12, 248);
rjButton10.Location = new Point(3, 275);
panel2.Hide();
rjButton9.Hide();
rjButton13.Hide();
label16.Hide();
}
}
}
private void Timer_Tick(object sender, EventArgs e)
{
if (timeStep >= 5000 | numSimulationSteps > 75000)
{
Application.Exit();
this.Close();
MessageBox.Show("An unexpected error occured! Too high timestep. Program has succesfully exited");
}
framesCount++;
frames++;
for (int step = 0; step < numSimulationSteps; step++)
{
UpdatePositions();
CalculateGravity();
// Check collisions and handle them
HandleCollisions();
// Update the QuadTree every frame
UpdateQuadTree();
foreach (var body in bodies)
{
CheckWindowCollision(body);
}
}
this.Invalidate(); // Wywołuje ponownie Paint, aby zaktualizować wyświetlanie ciał
// Update frame rate every second
if (frameStopwatch.ElapsedMilliseconds >= 1000)
{
frameRate = framesCount / (frameStopwatch.ElapsedMilliseconds / 1000.0);
framesCount = 0;
frameStopwatch.Restart();
}
}
private void UpdateQuadTree()
{
// Clear the QuadTree and reinsert all bodies
quadTree.Clear();
foreach (var body in bodies)
{
quadTree.Insert(body);
}
}
private void HandleCollisions()
{
for (int i = 0; i < bodies.Count; i++)
{
for (int j = i + 1; j < bodies.Count; j++)
{
Body body1 = bodies[i];
Body body2 = bodies[j];
double distanceSquared = (body1.X - body2.X) * (body1.X - body2.X) + (body1.Y - body2.Y) * (body1.Y - body2.Y);
double sumOfRadiiSquared = (body1.Radius + body2.Radius) * (body1.Radius + body2.Radius);
if (distanceSquared <= sumOfRadiiSquared)
{
// Introduce a damping factor to reduce velocity after collision
double dampingFactor = 0; // Możesz dostosować tę wartość według potrzeb
body1.VX *= dampingFactor;
body1.VY *= dampingFactor;
// Usunięcie zjedzonego ciała
if (body1.Mass > body2.Mass)
{
// Destroy body2
bodies.Remove(body2);
}
else
{
// Destroy body1
bodies.Remove(body1);
// Dodałem dodatkowy współczynnik tłumienia prędkości dla ciała, które pozostało po zjedzeniu innego ciała
body2.VX *= dampingFactor;
body2.VY *= dampingFactor;
}
}
}
}
}
private bool CheckWindowCollision(Body body)
{
int windowWidth = this.ClientSize.Width - 251;
int windowHeight = this.ClientSize.Height;
bool collisionDetected = false;
if (body.X - body.Radius < 0)
{
//body.VX = Math.Abs(body.VX); // Bounce off left border
collisionDetected = true;
}
else if (body.X + body.Radius > windowWidth)
{
//body.VX = -Math.Abs(body.VX); // Bounce off right border
collisionDetected = true;
}
if (body.Y - body.Radius < 0)
{
//body.VY = Math.Abs(body.VY); // Bounce off top border
collisionDetected = true;
}
else if (body.Y + body.Radius > windowHeight)
{
//body.VY = -Math.Abs(body.VY); // Bounce off bottom border
collisionDetected = true;
}
return collisionDetected;
}
private void UpdatePositions()
{
foreach (var body in bodies)
{
if (!body.IsStatic)
{
body.X += body.VX * timeStep;
body.Y += body.VY * timeStep;
}
}
}
private void CalculateGravity()
{
for (int i = 0; i < bodies.Count; i++)
{
for (int j = i + 1; j < bodies.Count; j++)
{
Body body1 = bodies[i];
Body body2 = bodies[j];
double dx = body2.X - body1.X;
double dy = body2.Y - body1.Y;
double distanceSquared = dx * dx + dy * dy;
double distance = Math.Sqrt(distanceSquared);
double forceMagnitude = (G * body1.Mass * body2.Mass) / distanceSquared;
double forceX = forceMagnitude * (dx / distance);
double forceY = forceMagnitude * (dy / distance);
body1.VX += forceX / body1.Mass * timeStep;
body1.VY += forceY / body1.Mass * timeStep;
body2.VX -= forceX / body2.Mass * timeStep;
body2.VY -= forceY / body2.Mass * timeStep;
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
frameStopwatch.Start();
label13.Text = $"Body count: {bodies.Count()}";
label2.Text = "VX: N/A, VY: N/A";
label7.Text = "PX: N/A, PY: N/A";
label9.Text = "Mass: N/A kg";
checkBox1.Checked = Settings.Default.debugMode;
checkBox2.Checked = Settings.Default.selectionMode;
checkBox3.Checked = Settings.Default.velocityVectors;
rjButton8.Location = new Point(5, 191);
label24.Location = new Point(76, 231);
comboBox2.Location = new Point(12, 248);
rjButton10.Location = new Point(3, 275);
}
private TimeSpan simulatedTime = TimeSpan.Zero;
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
g.TextContrast = 0;
StartRenderTimer();
// Rysowanie ciał
foreach (var body in bodies)
{
DrawBody(g, body, body.BodyColor); // Kolor można dostosować w zależności od ciała
if (body.IsSelected)
{
DrawSelectionSquare(g, body);
}
if (velocityVectors)
{
// Rysowanie wektorów prędkości
DrawVelocityVectors(g, body);
}
}
if (drawQuadTree)
{
// Rysuj Quad Tree
DrawQuadTree(g, quadTree.Root);
}
StopRenderTimer();
double totalForceX = 0.0;
double totalForceY = 0.0;
for (int i = 0; i < bodies.Count; i++)
{
for (int j = i + 1; j < bodies.Count; j++)
{
Body body1 = bodies[i];
Body body2 = bodies[j];
double dx = body2.X - body1.X;
double dy = body2.Y - body1.Y;
double distanceSquared = dx * dx + dy * dy;
double distance = Math.Sqrt(distanceSquared);
double forceMagnitude = (G * body1.Mass * body2.Mass) / distanceSquared;
double forceX = forceMagnitude * (dx / distance);
double forceY = forceMagnitude * (dy / distance);
body1.VX += forceX / body1.Mass * timeStep;
body1.VY += forceY / body1.Mass * timeStep;
body2.VX -= forceX / body2.Mass * timeStep;
body2.VY -= forceY / body2.Mass * timeStep;
totalForceX += forceX;
totalForceY += forceY;
}
}
if (selectedBody != null)
{
label2.Text = $"VX: {(selectedBody.VX * scaleFactor).ToString("F2")}, VY: {(selectedBody.VY * scaleFactor).ToString("F2")}";
label7.Text = $"PX: {selectedBody.X.ToString("F2")}, PY: {selectedBody.Y.ToString("F2")}";
if (selectedBody.Mass > Math.Pow(10, 9) || selectedBody.Mass < Math.Pow(10, -3)) // Sprawdź, czy masa jest większa niż miliard
{
label9.Text = $"Mass: {ConvertToScientificNotation(selectedBody.Mass)} kg";
}
else
{
label9.Text = $"Mass: {selectedBody.Mass.ToString("F2")} kg";
}
}
else
{
// If no body is selected, reset the labels
label2.Text = "VX: N/A, VY: N/A";
label7.Text = "PX: N/A, PY: N/A";
label9.Text = "Mass: N/A kg";
}
// Update label26.Text with the formatted time
double adjustedElapsedMilliseconds = SimStopwatch.ElapsedMilliseconds;
TimeSpan formattedRealTime = TimeSpan.FromMilliseconds(adjustedElapsedMilliseconds);
string formattedTime = $"{formattedRealTime.Hours:D2}h:{formattedRealTime.Minutes:D2}m:{formattedRealTime.Seconds:D2}s";
label26.Text = $"Real Time: {formattedTime}";
// Update label27.Text with the formatted simulated time
simulatedTime += TimeSpan.FromSeconds(timeStep); // Increment simulated time
// Ensure that simulatedTime is not negative
// Dodaj obliczenia dla formatowania czasu symulacji w latach, dniach, godzinach, minutach i sekundach
int years = simulatedTime.Days / 365;
int days = simulatedTime.Days % 365;
string formattedSimTime = $"{years:D2}yr:{days:D3}d:{simulatedTime.Hours:D2}h:{simulatedTime.Minutes:D2}m:{simulatedTime.Seconds:D2}s";
label27.Text = $"Simulated Time: {formattedSimTime}";
label3.Text = $"F: {Math.Sqrt(totalForceX * totalForceX + totalForceY * totalForceY).ToString("F8")}N";
label4.Text = $"Timestep: {timeStep.ToString("F3")}";
label5.Text = $"numSimulationSteps: {numSimulationSteps}";
label10.Text = $"Frame Rate: {frameRate.ToString("F2")} fps";
label6.Text = $"Frames: {frames} frames";
label13.Text = $"Body count: {bodies.Count()}";
if (bodies.Count <= 1)
{
timer.Stop();
MessageBox.Show("Simulation has ended! Reason: No enough bodies, F=0");
//Dodaj w tej linijce resetowanie do pozycji początkowych
// Wywołanie metody resetowania
ResetBodiesToInitialPositions();
rjButton1.Enabled = true;
rjButton2.Enabled = false;
timeStep = 1;
floatTrackBar1.Value = 1;
trackBar1.Value = 5000;
numSimulationSteps = 5000;
frames = 0;
panel2.Hide();
// Odświeżenie widoku
this.Invalidate();
}
}
private void DrawQuadTree(Graphics g, QuadTree.QuadTreeNode node)
{
if (node != null)
{
float thickness = 0.5f; // Grubość linii
Pen pen = new Pen(Brushes.White, thickness);
Point[] polygonPoints = new Point[]
{
new Point((int)node.Bounds.Left, (int)node.Bounds.Top),
new Point((int)node.Bounds.Right, (int)node.Bounds.Top),
new Point((int)node.Bounds.Right, (int)node.Bounds.Bottom),
new Point((int)node.Bounds.Left, (int)node.Bounds.Bottom)
};
g.DrawPolygon(pen, polygonPoints);
if (node.HasChildren)
{
foreach (var child in node.Children)
{
DrawQuadTree(g, child);
}
}
}
}
static string ConvertToScientificNotation(double value)
{
return value.ToString("0.###e0");
}
static string ConvertFromScientificNotation(string value)
{
if (double.TryParse(value, out double result))
{
return result.ToString();
}
return "Invalid input";
}
private void DrawSelectionSquare(Graphics g, Body body)
{
float x = (float)(body.X - body.Radius - 3); // Adjusting for padding
float y = (float)(body.Y - body.Radius - 3); // Adjusting for padding
float diameter = 2 * body.Radius + 6; // Adjusting for padding
g.DrawRectangle(new Pen(Brushes.Yellow, 1), x, y, diameter, diameter);
}
private bool IsMouseOnBody(Point mousePosition, Body body)
{
double distance = Math.Sqrt(Math.Pow(mousePosition.X - body.X, 2) + Math.Pow(mousePosition.Y - body.Y, 2));
return distance <= body.Radius;
}
private void DrawBody(Graphics g, Body body, Brush brush)
{
float x = (float)(body.X - body.Radius);
float y = (float)(body.Y - body.Radius);
float diameter = 2 * body.Radius;
g.FillEllipse(brush, x, y, diameter, diameter);
}
}
.......
public class QuadTree
{
public class QuadTreeNode
{
public Rectangle Bounds { get; }
public List<Body> Bodies { get; } = new List<Body>();
public QuadTreeNode[] Children { get; private set; }
public int MaxBodiesPerNode { get; private set; } = 4;
public bool HasChildren => Children != null;
private bool ShouldSubdivide() => !HasChildren && Bodies.Count >= MaxBodiesPerNode;
public QuadTreeNode(Rectangle bounds, int maxBodiesPerNode)
{
Bounds = bounds;
MaxBodiesPerNode = maxBodiesPerNode;
}
public void Subdivide()
{
int width = Bounds.Width / 2;
int height = Bounds.Height / 2;
Children = new QuadTreeNode[4];
Children[0] = new QuadTreeNode(new Rectangle(Bounds.Left, Bounds.Top, width, height), MaxBodiesPerNode);
Children[1] = new QuadTreeNode(new Rectangle(Bounds.Left + width, Bounds.Top, width, height), MaxBodiesPerNode);
Children[2] = new QuadTreeNode(new Rectangle(Bounds.Left, Bounds.Top + height, width, height), MaxBodiesPerNode);
Children[3] = new QuadTreeNode(new Rectangle(Bounds.Left + width, Bounds.Top + height, width, height), MaxBodiesPerNode);
}
}
public QuadTreeNode Root { get; private set; }
public QuadTree(Rectangle bounds, int maxBodiesPerNode = 4)
{
Root = new QuadTreeNode(bounds, maxBodiesPerNode);
}
public void Clear()
{
Root = new QuadTreeNode(Root.Bounds, Root.MaxBodiesPerNode);
}
public void Insert(Body body)
{
Insert(Root, body);
}
private void Insert(QuadTreeNode node, Body body)
{
if (node.Bounds.Contains(new Point((int)body.X, (int)body.Y)))
{
if (!node.HasChildren && node.Bodies.Count < node.MaxBodiesPerNode)
{
node.Bodies.Add(body);
}
else
{
if (!node.HasChildren)
{
node.Subdivide();
DistributeBodies(node);
}
foreach (var child in node.Children)
{
Insert(child, body);
}
}
}
}
private void DistributeBodies(QuadTreeNode node)
{
foreach (var storedBody in node.Bodies)
{
foreach (var child in node.Children)
{
Insert(child, storedBody);
}
}
node.Bodies.Clear();
}
}
这就是全部,我希望有人可以帮助我使用sharpDX 2D 渲染我的模拟。这是我第二次尝试 SharpDx,现在对我来说太难了!
代码的绘图部分并不难,因为您在代码中使用的绘图调用的 Direct2D 等效项大部分是相同的。
这是设置 Direct2D 设备的难点......
在函数
DrawQuadTree
中,我可以看到你正在绘制一个矩形,那么你可以使用DrawRectangle
。您使用 DrawPolygon
有什么原因吗?
private SharpDX.Direct2D1.SolidColorBrush WhiteBrush;
// It's better to create a brush once and reuse it
// instead of creating a new one every time
private void DrawQuadTree(SharpDX.Direct2D1.RenderTarget context, QuadTree.QuadTreeNode node)
{
if (node != null)
{
float thickness = 0.5f;
RawRectangleF rect = new RawRectangleF(
node.Bounds.Left,
node.Bounds.Top,
node.Bounds.Right,
node.Bounds.Bottom);
context.DrawRectangle(rect, WhiteBrush, thickness);
if (WhiteBrush == null)
{
WhiteBrush = new SharpDX.Direct2D1.SolidColorBrush(context,
new RawColor4(1.0f, 1.0f, 1.0f, 1.0f));
}
if (node.HasChildren)
{
foreach (var child in node.Children)
{
DrawQuadTree(context, child);
}
}
}
}
private SharpDX.Direct2D1.SolidColorBrush YellowBrush;
// It's better to create a brush once and reuse it
// instead of creating a new one every time
private void DrawSelectionSquare(SharpDX.Direct2D1.RenderTarget context, Body body)
{
float x = (float)(body.X - body.Radius - 3);
float y = (float)(body.Y - body.Radius - 3);
float diameter = 2 * body.Radius + 6;
RectangleF convert = new RectangleF(x, y, width: diameter, height: diameter);
// convert "X Y Width Height" to "Left Top Right Bottom"
RawRectangleF rect = new RawRectangleF(
convert.Left, convert.Top, convert.Right, convert.Bottom);
if (YellowBrush == null)
{
YellowBrush = new SharpDX.Direct2D1.SolidColorBrush(context,
new RawColor4(1.0f, 1.0f, 0.0f, 1.0f));
}
context.DrawRectangle(rect, YellowBrush);
}
private void DrawBody(SharpDX.Direct2D1.RenderTarget context, Body body,
SharpDX.Direct2D1.Brush brush)
{
float x = (float)(body.X - body.Radius);
float y = (float)(body.Y - body.Radius);
float diameter = 2 * body.Radius;
SharpDX.Direct2D1.Ellipse ellipse = new SharpDX.Direct2D1.Ellipse(
center: new RawVector2(x, y),
radiusX: diameter,
radiusY: diameter);
context.FillEllipse(ellipse, brush);
}
我注意到你有一个函数
DrawVelocityVectors
但你没有在这里发布它的源代码。
如果您需要在该函数中画一条线,Direct2D 中提供了 DrawLine
函数。
现在有两种方法可以做到这一点,传统方法和现代方法。
使用传统方式,您通常需要管理的事情更少。 用现代的方式你需要管理更多的东西,比如交换链, 但您将可以访问
ID2D1DeviceContext
,
它继承并扩展了ID2D1RenderTarget
,
虽然你似乎还不需要它,
到目前为止,您使用的每个绘图功能都已在 ID2D1RenderTarget
界面中可用。
传统方式:
private SharpDX.Direct2D1.Factory d2dFactory;
private SharpDX.Direct2D1.WindowRenderTarget d2dRenderTarget;
private void Form1_Load(object sender, EventArgs e)
{
// ...
IntPtr TargetHWND = Controls.Find("name of the control you will render to", true).First().Handle;
d2dFactory = new SharpDX.Direct2D1.Factory();
RenderTargetProperties properties = new RenderTargetProperties(
RenderTargetType.Default,
new PixelFormat(SharpDX.DXGI.Format.B8G8R8A8_UNorm, AlphaMode.Premultiplied),
0, 0, RenderTargetUsage.None, FeatureLevel.Level_DEFAULT);
HwndRenderTargetProperties hwndRenderTargetProperties = new HwndRenderTargetProperties
{
Hwnd = TargetHWND,
PixelSize = new SharpDX.Size2(0, 0),
#if VSYNC
PresentOptions = PresentOptions.None, // turn on VSync
#else
PresentOptions = PresentOptions.Immediately, // turn off VSync
#endif
};
d2dRenderTarget = new SharpDX.Direct2D1.WindowRenderTarget(
d2dFactory, properties, hwndRenderTargetProperties);
// Create all the brushes you need
WhiteBrush = new SolidColorBrush(d2dRenderTarget, new RawColor4(1.0f, 1.0f, 1.0f, 1.0f));
YellowBrush = new SolidColorBrush(d2dRenderTarget, new RawColor4(1.0f, 1.0f, 0.0f, 1.0f));
RedBrush = new SolidColorBrush(d2dRenderTarget, new RawColor4(1.0f, 0.0f, 0.0f, 1.0f));
BlueBrush = new SolidColorBrush(d2dRenderTarget, new RawColor4(0.0f, 0.0f, 1.0f, 1.0f));
}
private SharpDX.Direct2D1.SolidColorBrush WhiteBrush;
private SharpDX.Direct2D1.SolidColorBrush YellowBrush;
private SharpDX.Direct2D1.SolidColorBrush RedBrush;
private SharpDX.Direct2D1.SolidColorBrush BlueBrush;
您需要用
BeginDraw
和 EndDraw
包围绘制操作
private RawColor4 backgroundColor = new RawColor4(0.0f, 0.0f, 0.0f, 1.0f);
// change the background color to your liking
private void Form1_Paint(object sender, PaintEventArgs e)
{
d2dRenderTarget.BeginDraw();
d2dRenderTarget.Clear(backgroundColor);
// draw calls...
d2dRenderTarget.EndDraw();
}
请注意,退出应用程序后,您有责任关闭所有 DirectX 接口
private void Form1_Closed(object sender, EventArgs e)
{
d2dRenderTarget.Flush(); // wait for all operations on the GPU to finish
WhiteBrush.Dispose();
YellowBrush.Dispose();
RedBrush.Dispose();
BlueBrush.Dispose();
d2dRenderTarget.Dispose();
d2dFactory.Dispose();
}
现代方式:
private SharpDX.DXGI.Factory dxgiFactory;
#if USE_D3D10
private SharpDX.Direct3D10.Device d3d10Device;
#else
private SharpDX.Direct3D11.Device d3d11Device;
#endif
private SharpDX.DXGI.SwapChain dxgiSwapChain;
private SharpDX.DXGI.Surface backBuffer;
private SharpDX.DXGI.Device dxgiDevice;
private SharpDX.Direct2D1.Device d2dDevice;
private SharpDX.Direct2D1.DeviceContext d2dContext;
private SharpDX.Direct2D1.Bitmap1 d2dTarget;
private void Form1_Load(object sender, EventArgs e)
{
IntPtr TargetHWND = Controls.Find("name of the control you will render to", true).First().Handle;
dxgiFactory = new SharpDX.DXGI.Factory1();
SwapChainDescription swapChainDescription = new SwapChainDescription
{
ModeDescription = new ModeDescription(Format.B8G8R8A8_UNorm),
SampleDescription = new SampleDescription(1, 0),
Usage = Usage.RenderTargetOutput,
BufferCount = 2,
OutputHandle = TargetHWND,
IsWindowed = true,
SwapEffect = SwapEffect.Discard,
Flags = SwapChainFlags.None,
};
#if USE_D3D10
d3d10Device = new SharpDX.Direct3D10.Device(SharpDX.Direct3D10.DriverType.Hardware,
SharpDX.Direct3D10.DeviceCreationFlags.BgraSupport);
dxgiSwapChain = new SharpDX.DXGI.SwapChain(dxgiFactory, d3d10Device, swapChainDescription);
#else
d3d11Device = new SharpDX.Direct3D11.Device(SharpDX.Direct3D.DriverType.Hardware,
SharpDX.Direct3D11.DeviceCreationFlags.BgraSupport,
SharpDX.Direct3D.FeatureLevel.Level_11_1, SharpDX.Direct3D.FeatureLevel.Level_11_0);
dxgiSwapChain = new SharpDX.DXGI.SwapChain(dxgiFactory, d3d11Device, swapChainDescription);
#endif
backBuffer = dxgiSwapChain.GetBackBuffer<SharpDX.DXGI.Surface>(0);
dxgiDevice = d3d11Device.QueryInterface<SharpDX.DXGI.Device>();
d2dDevice = new SharpDX.Direct2D1.Device(dxgiDevice);
d2dContext = new SharpDX.Direct2D1.DeviceContext(d2dDevice, DeviceContextOptions.None);
d2dTarget = new Bitmap1(d2dContext, backBuffer);
// create all the brushes you need
WhiteBrush = new SolidColorBrush(d2dContext, new RawColor4(1.0f, 1.0f, 1.0f, 1.0f));
YellowBrush = new SolidColorBrush(d2dContext, new RawColor4(1.0f, 1.0f, 0.0f, 1.0f));
RedBrush = new SolidColorBrush(d2dContext, new RawColor4(1.0f, 0.0f, 0.0f, 1.0f));
BlueBrush = new SolidColorBrush(d2dContext, new RawColor4(0.0f, 0.0f, 1.0f, 1.0f));
}
private SharpDX.Direct2D1.SolidColorBrush WhiteBrush;
private SharpDX.Direct2D1.SolidColorBrush YellowBrush;
private SharpDX.Direct2D1.SolidColorBrush RedBrush;
private SharpDX.Direct2D1.SolidColorBrush BlueBrush;
除了
BeginDraw
和EndDraw
之外,您还需要设置渲染目标并在交换链上调用Present
private RawColor4 backgroundColor = new RawColor4(0.0f, 0.0f, 0.0f, 1.0f);
// change the background color to your liking
private void Form1_Paint(object sender, PaintEventArgs e)
{
d2dContext.BeginDraw();
d2dContext.Target = d2dTarget;
d2dContext.Clear(backgroundColor);
// draw calls...
d2dContext.Target = null;
d2dContext.EndDraw();
#if VSYNC
dxgiSwapChain.Present(1, PresentFlags.None);
#else
dxgiSwapChain.Present(0, PresentFlags.None);
#endif
}
如果您渲染的控制元素的大小可能会调整,您也需要处理它
private void Form1_SizeChanged(object sender, EventArgs e)
{
Size Resolution = Controls.Find("name of the control you will render to", true).First().Size;
d2dContext.Flush(); // wait for all operations on the GPU to finish
d2dTarget.Dispose();
backBuffer.Dispose();
dxgiSwapChain.ResizeBuffers(2, Resolution.Width, Resolution.Height, Format.B8G8R8A8_UNorm, SwapChainFlags.None);
backBuffer = dxgiSwapChain.GetBackBuffer<SharpDX.DXGI.Surface>(0);
d2dTarget = new Bitmap1(d2dContext, backBuffer);
}
记得关闭所有界面
private void Form1_Closed(object sender, EventArgs e)
{
d2dContext.Flush(); // wait for all operations on the GPU to finish
WhiteBrush.Dispose();
YellowBrush.Dispose();
RedBrush.Dispose();
BlueBrush.Dispose();
d2dTarget.Dispose();
d2dContext.Dispose();
d2dDevice.Dispose();
dxgiDevice.Dispose();
backBuffer.Dispose();
dxgiSwapChain.Dispose();
#if USE_D3D10
d3d10Device.Dispose();
#else
d3d11Device.Dispose();
#endif
dxgiFactory.Dispose();
}