我怎样才能模拟像窗户油漆一样的喷雾?我认为它会随机产生分数,你的意见是什么?
6 回答
是的,我会说它在选择点的某个半径内为随机像素着色。一个像素的着色和另一个像素的着色之间也可能存在时间延迟,因为今天的机器速度足够快,可以在您放开鼠标按钮之前为每个可能的像素着色(只要半径很小)。
另外,我认为 Paint 使用的算法可以选择一个像素进行绘制,即使它已经被绘制,因为有时你可能会得到一个内部有几个未绘制像素的绘制圆圈。
喷漆的图案是半随机的。如果你拿出一罐 Krylon 并在墙上慢慢喷一条线,你最终会得到一条宽实线,该线逐渐淡出到背景中,边缘有渐变。在一个点喷洒十秒钟,你会在中心得到一个大点,颜色完全饱和,与背景呈放射状渐变。
所以 - 你的模拟变量包括:
- 拿着“喷雾器”(鼠标按钮)的时间
- “罐头”(鼠标)的运动
- “罐头”的速度(快速移动形成一条轻快的、不饱和的线。慢速移动形成一条带有渐变的粗、饱和的线)
- 扩散模式:喷雾是像喷枪一样集中,还是像喷雾罐一样大?
- 《距离》:“喷子”离“画布”有多远?
您收到了许多答案,这些答案为您指明了开始处理喷雾效果的用户体验的正确方向。根据您对我的评论的回复,您还需要一种算法来生成半径内的随机点。
有很多方法可以做到这一点,最明显的可能是使用 极坐标选择随机点,然后将极坐标转换为笛卡尔 (x,y) 坐标来渲染像素。这是这种方法的一个简单示例。为了简单起见,我刚刚为每个点绘制了一个简单的 1x1 椭圆。
private Random _rnd = new Random();
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
int radius = 15;
using (Graphics g = this.CreateGraphics())
{
for (int i = 0; i < 100; ++i)
{
// Select random Polar coordinate
// where theta is a random angle between 0..2*PI
// and r is a random value between 0..radius
double theta = _rnd.NextDouble() * (Math.PI * 2);
double r = _rnd.NextDouble() * radius;
// Transform the polar coordinate to cartesian (x,y)
// and translate the center to the current mouse position
double x = e.X + Math.Cos(theta) * r;
double y = e.Y + Math.Sin(theta) * r;
g.DrawEllipse(Pens.Black, new Rectangle((int)x - 1, (int)y - 1, 1, 1));
}
}
}
或者,您可以从适合喷雾圆的矩形中随机选择 x,y 坐标,并使用圆方程 r^2 = x^2 + y^2 测试该点以确定它是否位于圆内,如果是的话随机选择另一个点并再次测试,直到您有一个位于圆圈内的点。这是此方法的快速示例
private Random _rnd = new Random();
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
int radius = 15;
int radius2 = radius * 2;
using (Graphics g = this.CreateGraphics())
{
double x;
double y;
for (int i = 0; i < 100; ++i)
{
do
{
// Randomy select x,y so that
// x falls between -radius..radius
// y falls between -radius..radius
x = (_rnd.NextDouble() * radius2) - radius;
y = (_rnd.NextDouble() * radius2) - radius;
// If x^2 + y^2 > r2 the point is outside the circle
// and a new point needs to be selected
} while ((x*x + y*y) > (radius * radius));
// Translate the point so that the center is at the mouse
// position
x += e.X;
y += e.Y;
g.DrawEllipse(Pens.Black, new Rectangle((int)x - 1, (int)y - 1, 1, 1));
}
}
}
您可以通过对极坐标的一些数量(与所需的强度和扩散相关)进行采样来创建各种强度的喷雾模式。为此,请通过以下方式确定每个样本的随机极坐标 (ρ, θ):
从 N(0, 1) 采样的 ρ:使用正态(高斯)分布表示距喷雾模式精确中心的距离。我不记得 .NET 库中是否有普通的变量生成器。如果没有,您可以从 U(0, 1) 生成器创建一个。
从 U(0, π) 采样的 θ:从均匀连续分布中采样角度分量。在不损失性能或通用性的情况下,您可以改为在 U( n π, m π) 上对n < m进行采样,但 U(0, π) 可能适合您的需要。
每个样本的笛卡尔坐标由 (T x + S x ρ cos θ, T y + S y ρ sin θ) 给出,其中 (T x , T y ) 是您要创建的喷雾模式的中心;S x和 S y分别是您希望在 x 和 y 方向上的扩展因子。
尝试使用计时器
public partial class Form1 : Form
{
int Radious = 5;
Random _rnd = new Random();
Timer T = new Timer();
int InterVal = 1000;
MouseEventArgs MEA = null;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
T.Tick += (O, E) =>
{
StartSpray();
};
this.MouseDown += (O, E) =>
{
MEA = E;
T.Interval = InterVal;
T.Start();
};
this.MouseUp += (O, E) =>
{
T.Stop();
};
}
private void StartSpray()
{
Point P = DrawPoint(Radious, MEA.X, MEA.Y);
// Draw the point on any graphics area you can add the color or anything else
}
private Point DrawPoint(int Radious, int StatX, int StartY)
{
double theta = _rnd.NextDouble() * (Math.PI * 2);
double r = _rnd.NextDouble() * Radious;
Point P = new Point { X = StatX + Convert.ToInt32(Math.Cos(theta) * r), Y = StartY + Convert.ToInt32(Math.Sin(theta) * r) };
return P;
}
}
请修改间隔和半径。
我认为很难在 C# 上找到示例。下面我将介绍一种开始您的旅程的方法。在这里,我使用纹理画笔。
private void Button1_Click(System.Object sender, System.EventArgs e)
{
try
{
Bitmap image1 = (Bitmap)Image.FromFile(@"C:\temp\mybrush.bmp", true);
TextureBrush t = new TextureBrush(image1);
t.WrapMode = System.Drawing.Drawing2D.WrapMode.Tile;
Graphics formGraphics = this.CreateGraphics();
formGraphics.FillEllipse(t, new RectangleF(90.0F, 110.0F, 100, 100));
formGraphics.Dispose();
}
catch (System.IO.FileNotFoundException)
{
MessageBox.Show("Image file not found!");
}
}
正如杰西所说,我认为你应该找到一种算法来传播随机像素。