【分享】C#制作的模拟水波的程序
按照惯例 效果图虽然网上已经有了 不过还是想自己搞一个
代码 没有多少 一共也就100+行 加上前面的using什么的
不过话说回来 从编程角度去考虑的话 这个程序没啥技术含量 就是操作像素而已
然后关键就是怎么去操作像素 这个是算法问题了
所以 这个程序里面重点就是那几个算法 而程序本身是没啥技术含量的
其实 我也是把别人的算法搬过来自己写了而已
不过 因为要实时渲染每一帧 所以 效率肯定不能很好 所以在编写的时候 循环里面能少一个步骤就尽量少一个步骤
算了 p话 不多说 还是直接上连接
http://download.csdn.net/detail/crystal_lz/4607442 --------------------编程问答-------------------- 反正代码也不多 代码也干脆直接贴出来
哦对了 在连接里面我打包有 当时写这个程序的时候找的质料 可以参考一下
--------------------编程问答-------------------- 如果 对波形是如何传递的那个地方不怎么明白的话 可以用一个小点的数组 然后把里面的值打印出来看看就知道了 弄个循环 打印一帧 再打印 看看变化的过程就知道了
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace _WATER_WAVE
{
public partial class Form1 : Form
{
public Form1() {
InitializeComponent();
}
Bitmap m_bmp;
byte[] m_byArrClrInfo; //图片原始颜色信息
byte[] m_byArrClrBuff; //图片新的颜色信息
int[,] m_nArrWaveCurrent; //当前波形
int[,] m_nArrWaveNext; //下一帧的波形
int m_nBmpWidth;
int m_nBmpHeight;
int m_nBmpWidthBySize; //图片每行占用字节数
private void Form1_Load(object sender, EventArgs e) {
//加载图像 设置界面显示
Bitmap bmp = new Bitmap("123.jpg");//打开一张图将起转换为24位
m_bmp = bmp.Clone(new Rectangle(0, 0, bmp.Width, bmp.Height), PixelFormat.Format24bppRgb);
pictureBox1.Image = m_bmp;
pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize;
this.Width = pictureBox1.Width + 2 * pictureBox1.Left + (this.Size - this.ClientSize).Width;
this.Height = pictureBox1.Height + 2 * pictureBox1.Top + (this.Size - this.ClientSize).Height;
this.BackColor = Color.Black;
//加载图像信息 初始化变量
BitmapData bmpData = m_bmp.LockBits(new Rectangle(0, 0, m_bmp.Width, m_bmp.Height), ImageLockMode.ReadOnly, m_bmp.PixelFormat);
m_byArrClrInfo = new byte[bmpData.Stride * bmpData.Height];
m_byArrClrBuff = new byte[m_byArrClrInfo.Length];
m_nArrWaveCurrent = new int[m_bmp.Width, m_bmp.Height];
m_nArrWaveNext = new int[m_bmp.Width, m_bmp.Height];
m_nBmpWidth = m_bmp.Width;
m_nBmpHeight = m_bmp.Height;
m_nBmpWidthBySize = bmpData.Stride;
Marshal.Copy(bmpData.Scan0, m_byArrClrInfo, 0, m_byArrClrInfo.Length);
m_bmp.UnlockBits(bmpData);
//启动水波的模拟
timerDraw.Interval = 25; //绘制水波
timerDraw.Enabled = true;
timerSetWave.Interval = 500; //随机产生波源
timerSetWave.Enabled = true;
}
//绘制水波
private void timerDraw_Tick(object sender, EventArgs e) {
int nNewX = 0;
int nNewY = 0;
BitmapData bmpData = m_bmp.LockBits(new Rectangle(0, 0, m_bmp.Width, m_bmp.Height), ImageLockMode.ReadWrite, m_bmp.PixelFormat);
Marshal.Copy(bmpData.Scan0, m_byArrClrBuff, 0, m_byArrClrBuff.Length);
for (int y = 1; y < m_nBmpHeight - 1; y++) {
for (int x = 1; x < m_nBmpWidth - 1; x++) {
m_nArrWaveNext[x, y] = (( //能量传递
m_nArrWaveCurrent[x - 1, y] + //注意 能量传递是通过当前波形计算新的波形
m_nArrWaveCurrent[x + 1, y] + //m_nArrWaveCurrent m_nArrWaveNext 不要弄翻
m_nArrWaveCurrent[x, y - 1] +
m_nArrWaveCurrent[x, y + 1])
>> 1) - m_nArrWaveNext[x, y];
m_nArrWaveNext[x, y] -= m_nArrWaveNext[x, y] >> 5; //产生阻尼
//像素偏移 (模拟折射)
nNewX = ((m_nArrWaveNext[x + 1, y] - m_nArrWaveNext[x - 1, y]) >> 0) + x; //右移越大 折射变大
nNewY = ((m_nArrWaveNext[x, y + 1] - m_nArrWaveNext[x, y - 1]) >> 0) + y; //左移也可 折射变小
if (nNewX == x && nNewY == y) continue; //没有产生像素偏移 直接跳过
if (nNewX < 0) nNewX = -nNewX; //也可将其赋值为 0
if (nNewX >= m_nBmpWidth) nNewX = m_nBmpWidth - 1;
if (nNewY < 0) nNewY = -nNewY;
if (nNewY >= m_nBmpHeight) nNewY = m_nBmpHeight - 1;
//模拟光的反射 也可以跳过 不过波纹明暗度不明显
//m_byArrClrBuff[y * m_nBmpWidthBySize + x * 3] = m_byArrClrInfo[nNewY * m_nBmpWidthBySize + nNewX * 3];
//m_byArrClrBuff[y * m_nBmpWidthBySize + x * 3 + 1] = m_byArrClrInfo[nNewY * m_nBmpWidthBySize + nNewX * 3 + 1];
//m_byArrClrBuff[y * m_nBmpWidthBySize + x * 3 + 2] = m_byArrClrInfo[nNewY * m_nBmpWidthBySize + nNewX * 3 + 2];
//continue;
int nIncrement = m_nArrWaveNext[x, y]; //用当前像素点的能量作为光线明暗度变化标志
nIncrement >>= nIncrement < 0 ? 5 : 3; //如果负数变暗 正数变量 (适当的位移一下不然差距太大)
//重置RGB值
int r = m_byArrClrInfo[nNewY * m_nBmpWidthBySize + nNewX * 3] + nIncrement;
int g = m_byArrClrInfo[nNewY * m_nBmpWidthBySize + nNewX * 3 + 1] + nIncrement;
int b = m_byArrClrInfo[nNewY * m_nBmpWidthBySize + nNewX * 3 + 2] + nIncrement;
if (nIncrement < 0) { //如果是负数便是变暗 则不能让其越界 0 - 255
r = r < 0 ? 0 : r;
g = g < 0 ? 0 : g;
b = b < 0 ? 0 : b;
} else {
r = r > 255 ? 255 : r;
g = g > 255 ? 255 : g;
b = b > 255 ? 255 : b;
}
m_byArrClrBuff[y * m_nBmpWidthBySize + x * 3] = (byte)r;
m_byArrClrBuff[y * m_nBmpWidthBySize + x * 3 + 1] = (byte)g;
m_byArrClrBuff[y * m_nBmpWidthBySize + x * 3 + 2] = (byte)b;
}
}
Marshal.Copy(m_byArrClrBuff, 0, bmpData.Scan0, m_byArrClrBuff.Length);
m_bmp.UnlockBits(bmpData);
pictureBox1.Refresh();
//交换能量缓存 将新产生的波形 赋值给当前波形的缓存 计算下一帧的波形
int[,] temp = m_nArrWaveCurrent;
m_nArrWaveCurrent = m_nArrWaveNext;
m_nArrWaveNext = temp;
}
//设置波源 x,y波源坐标 r波源半径 h波源的能量大小
public void SetWavePoint(int x, int y, int r, int h) {
//判断波源所在矩形位置是否越出图像 以便将越出部分坐标重置
int nXStart = x - r < 0 ? 0 : x - r; //波源矩形位置x轴起点
int nYStart = y - r < 0 ? 0 : y - r; //波源矩形位置y轴起点
int nXLen = x + r >= m_nBmpWidth ? m_nBmpWidth - 1 : x + r; //波源x轴矩形长度
int nYlen = y + r >= m_nBmpHeight ? m_nBmpHeight - 1 : y + r; //波源y轴矩形长度
for (int posX = nXStart; posX < nXLen; posX++) {
for (int posY = nYStart; posY < nYlen; posY++) { //以点(x,y)半径为r内的点赋值一个能量
if ((posX - x) * (posX - x) + (posY - y) * (posY - y) < r * r)
m_nArrWaveCurrent[posX, posY] = -h;
}
}
}
private void timerSetWave_Tick(object sender, EventArgs e) {
Random rd = new Random();
for (int i = 0; i < 5; i++) //随机产生五个波源
SetWavePoint(rd.Next(m_nBmpWidth - 1), rd.Next(m_nBmpHeight - 1), rd.Next(5, 10), rd.Next(32, 128));
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e) {
Random rd = new Random(); //将鼠标滑动过的位置产生波源
SetWavePoint(e.X, e.Y, rd.Next(5, 10), rd.Next(32, 128));
}
}
}
--------------------编程问答-------------------- 哥们,很厉害啊。 --------------------编程问答-------------------- 哇塞啊啊啊 --------------------编程问答-------------------- 呵呵,学习了!!!!! --------------------编程问答-------------------- 一直不怎么明白这个东西,今天终于可以好好学习一下,谢谢楼主了! --------------------编程问答-------------------- 学习了. --------------------编程问答-------------------- 代码能够优化的地方一堆一堆的。 --------------------编程问答-------------------- 写的不错,研究研究 --------------------编程问答-------------------- LZ太强悍了。 --------------------编程问答-------------------- 学习。 --------------------编程问答-------------------- 好厉害啊 ,我不会做 --------------------编程问答-------------------- 厉害 得到顶顶顶顶顶顶顶顶顶顶
--------------------编程问答-------------------- --------------------编程问答-------------------- 哟,不错哦。 --------------------编程问答-------------------- 在同一个快速连续点击鼠标大约7次以上,就会出现无法挽回的黑白波纹,波纹慢慢扩大侵蚀了整个水波世界,感觉那里的能量积累太大产生了一个黑洞似的 --------------------编程问答-------------------- 楼主,人品+1.十一快乐!~ --------------------编程问答-------------------- 强帖留名,以备后用 --------------------编程问答--------------------
很厉害的说 --------------------编程问答-------------------- 还不知道学C#好还是C++,指教指教 --------------------编程问答-------------------- 我试试
--------------------编程问答-------------------- 建議樓上的C++ 收入可觀 但是。。。。。。
樓主很給力額 先學習下 --------------------编程问答-------------------- binding --------------------编程问答-------------------- 如16樓所說 讓人頭暈的圈圈 --------------------编程问答-------------------- V5 --------------------编程问答-------------------- [img=http://t2.qpic.cn/mblogpic/c93ea1a68398e341040a/460][/img] --------------------编程问答-------------------- 牛逼的一腿啊 --------------------编程问答-------------------- mark.. --------------------编程问答-------------------- Mark..看看lz成果~
--------------------编程问答-------------------- Mark..看看lz成果~ --------------------编程问答-------------------- 这个很酷啊。 --------------------编程问答-------------------- cool! --------------------编程问答-------------------- 感谢楼主分享! --------------------编程问答-------------------- --------------------编程问答-------------------- 牛!!!! --------------------编程问答-------------------- 很好诶
--------------------编程问答-------------------- 牛人啊 --------------------编程问答-------------------- 牛B人不少啊 --------------------编程问答-------------------- LZ厉害,领教 --------------------编程问答-------------------- 最喜欢技术贴,c#新手 --------------------编程问答-------------------- 如16楼所言 鼠标连击之后直接毁了画面了。效果在26楼的童鞋贴出来了。。 --------------------编程问答-------------------- --------------------编程问答--------------------
真有这个Bug啊。不过还是很不错哈。谢谢分享。 --------------------编程问答-------------------- mark 有空自己试下 --------------------编程问答--------------------
次品鼠标是发现bug的神器...我不是故意要挑毛病的.... --------------------编程问答--------------------
其实 这个东西 在我测试的时候 就发现了 因为能量一直在一个地方聚集 然后越来越大 然后就悲剧了
其实 可以在 计算能量的时候 给定一个峰值 比如 -500 - 500 之间 不然的话没有限制 会很悲剧
还有
--------------------编程问答--------------------
//绘制水波
private void timerDraw_Tick(object sender, EventArgs e) {
int nNewX = 0;
int nNewY = 0;
BitmapData bmpData = m_bmp.LockBits(new Rectangle(0, 0, m_bmp.Width, m_bmp.Height), ImageLockMode.ReadWrite, m_bmp.PixelFormat);
Marshal.Copy(bmpData.Scan0, m_byArrClrBuff, 0, m_byArrClrBuff.Length);
//上面这一句改一下比较合适
//Array.Copy(m_byArrClrInfo,m_byArrClrBuff,m_byArrClrBuff.Length)
//从原始颜色信息拷贝一个过来 从当前图像的话 因为 本来当前图像就是扭曲的
//所以 波纹过后 下面的图像会有所模糊 因为 在下面 判断的时候没有产生像素偏移就跳过 不重置颜色
for (int y = 1; y < m_nBmpHeight - 1; y++) {
for (int x = 1; x < m_nBmpWidth - 1; x++) {
m_nArrWaveNext[x, y] = (( //能量传递
//绘制水波
private void timerDraw_Tick(object sender, EventArgs e) {
int nNewX = 0;
int nNewY = 0;
BitmapData bmpData = m_bmp.LockBits(new Rectangle(0, 0, m_bmp.Width, m_bmp.Height), ImageLockMode.ReadWrite, m_bmp.PixelFormat);
//Marshal.Copy(bmpData.Scan0, m_byArrClrBuff, 0, m_byArrClrBuff.Length);
Array.Copy(m_byArrClrInfo, m_byArrClrBuff, m_byArrClrBuff.Length);
for (int y = 1; y < m_nBmpHeight - 1; y++) {
for (int x = 1; x < m_nBmpWidth - 1; x++) {
m_nArrWaveNext[x, y] = (( //能量传递
m_nArrWaveCurrent[x - 1, y] + //注意 能量传递是通过当前波形计算新的波形
m_nArrWaveCurrent[x + 1, y] + //m_nArrWaveCurrent m_nArrWaveNext 不要弄翻
m_nArrWaveCurrent[x, y - 1] +
m_nArrWaveCurrent[x, y + 1])
>> 1) - m_nArrWaveNext[x, y];
m_nArrWaveNext[x, y] -= m_nArrWaveNext[x, y] >> 5; //产生阻尼
if (m_nArrWaveNext[x, y] < -500) m_nArrWaveNext[x, y] = -500; //设定一个峰值
if (m_nArrWaveNext[x, y] > 500) m_nArrWaveNext[x, y] = 500;
//像素偏移 (模拟折射)
nNewX = ((m_nArrWaveNext[x + 1, y] - m_nArrWaveNext[x - 1, y]) >> 0) + x; //右移越大 折射变小
nNewY = ((m_nArrWaveNext[x, y + 1] - m_nArrWaveNext[x, y - 1]) >> 0) + y; //左移也可 折射变大
............
}
加一个范围 就不会出现这情况了 --------------------编程问答-------------------- 顶一个。
马克了学习。
不讨论钱。
纯粹谈谈个人看法,感觉如果是学生要学好编程,我觉得应该先打C++基础,然后再看,是否继续C++,是否做得了C++牛人(我个人一直认为C++要写出垃圾代码太容易了,要学好写好C++太不容易了,如果成不了牛人就写不出好的代码还容易坑害逼人),还是转Java或C#,C#在MS平台上是很好的语言也综合了很多现代编程思想,而且后续只要MS不倒总会不断推进,而C#是更容易维护定位错误培养良好编程习惯的语言,不容易写出很垃圾的代码(因为不像C++那么灵活),即便写出垃圾代码,也容易改进的多。 --------------------编程问答-------------------- 其实 我也觉得 我的代码很臃肿 应该是一直以来 写代码 都感觉自己的代码很臃肿
--------------------编程问答-------------------- 能不能先发代码再给链接啊,尼玛,下完以后才发现代码就在下面 --------------------编程问答-------------------- - -! 你要淡定 其实 我都发完了 才想着 代码也不多 才把代码直接贴上来的
--------------------编程问答--------------------
擦,我还以为是柏林噪声... --------------------编程问答-------------------- 共享精神可嘉,代码可供学习. --------------------编程问答-------------------- Bitmap bmp = new Bitmap("123.jpg");//打开一张图将起转换为24位
顺便问一下,这个123.jpg文件放哪了?以前没做过这个,不了解,谢谢 --------------------编程问答-------------------- 神贴大牛 备用 --------------------编程问答-------------------- 牛B 牛B 学习学习 --------------------编程问答-------------------- 挺棒的!收藏了! --------------------编程问答-------------------- 罗云彬写的那个DLL效率相当高,不知道C#最大程度优化之后的效率如何 --------------------编程问答--------------------
你看下我优化的这个的效率如何。
http://files.cnblogs.com/Imageshop/%E6%B0%B4%E6%B3%A2%E6%95%88%E6%9E%9C%EF%BC%88%E9%AB%98%E9%80%9F%E7%89%88%E6%9C%AC%EF%BC%89.rar
采用和楼主一样的算法实现的。对于楼主提供的图片,在我电脑上,每渲染一帧需要仅仅7MS.
--------------------编程问答-------------------- 说实在的,GDI+对图像的管理模式不适合做类似这种高速的实时渲染,理由如下:
1、每次需要修改图像数据,都需要Lock和Unlock,这个在内部涉及到内存的重新分配以及图像数据的转换(如果需要)。像VB.NET这种不能使用unsafe的语言,还需要用Marshal.Copy之类的方法将数据拷贝到另外一耳光数组中,处理后又拷贝回图像数据。当然,C#可以指针,到要好些。(楼主没有使用)
2、 pictureBox1.Refresh之类的应该也是使用GDI+的相关绘图函数,众所周知,DrawImage函数的效率很低下,这对实时渲染当然影响很大。
--------------------编程问答-------------------- 针对楼主的代码可以优化的地方主要有:
1、把 m_nArrWaveCurrent[x - 1, y] 这里的二维数组的方式更改为一维数组的方式,这样
m_nArrWaveCurrent[x - 1, y] + m_nArrWaveCurrent[x + 1, y] + m_nArrWaveCurrent[x, y - 1] + m_nArrWaveCurrent[x, y + 1]) 就可以优化为一些简单的下标加减。
2、 nNewY * m_nBmpWidthBySize + nNewX * 3 这样的下标计算有重复,用中间变量提速。
3、 int[,] temp = m_nArrWaveCurrent;
m_nArrWaveCurrent = m_nArrWaveNext;
m_nArrWaveNext = temp;
这个其实没有必要进行实体数据的交换,如果用指针来访问数组的话,只要交换两个指针就可以了。
当然还有不少可以优化的地方,你可以下载我的代码参考参考。
--------------------编程问答-------------------- 精神可嘉,图也漂亮。
实话:这个算法很老了
当年我们图形学老师在课上给我们演示的就是这个算法
是一个基础算法
但它的细节确实太差
效率也不高 --------------------编程问答-------------------- --------------------编程问答--------------------
你的代码我看了
1.其实最开始的时候我是用的一维数组 可是想到一维还不是得计算得到下标 所以干脆用二维方便(当时没有想到 把计算索引的提取出来)
2.同上
3.他们应该没有交换实体数据吧 交换的引用吧
int[] a = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
int[] b = a;
a[0] = 55;
MessageBox.Show(b[0].ToString());
弹出来是 55 a 和 b 指向的是同一个内存块
其实交换的 应该就相当于指针吧 用unsafe 括起来给指针赋值的时候都是直接 用数组的名称就可以了
我看你的代码然后 我试验了一下 我在原来的代码的timer时间的两天开始计时和结束即使 要 14毫秒
然后 我换成一位数组 变成了10毫秒
然后 提取中间变量出来 8 毫秒
我提取的貌似 比你多几个 - -!、、
for (int y = 1; y < m_nBmpHeight - 1; y++) {
int index = y * m_nBmpWidth;
int indexPreX = index - 1;
int indexNextX = index + 1;
int indexPreY = index - m_nBmpWidth;
int indexNextY = index + m_nBmpWidth;
for (int x = 1; x < m_nBmpWidth - 1; x++) {
index++;
indexPreX++;
indexNextX++;
indexPreY++;
indexNextY++;
m_nArrWaveNext[index] = (( //能量传递
m_nArrWaveCurrent[indexPreX] + //注意 能量传递是通过当前波形计算新的波形
m_nArrWaveCurrent[indexNextX] + //m_nArrWaveCurrent m_nArrWaveNext 不要弄反
m_nArrWaveCurrent[indexPreY] +
m_nArrWaveCurrent[indexNextY])
>> 1) - m_nArrWaveNext[index];
m_nArrWaveNext[index] -= m_nArrWaveNext[index] >> 5; //产生阻尼
- -!、、还有 看了你的代码之后 我才发现 我把 RGB 弄反了 虽然阴差阳错的没有出啥问题
但是 经常遇到 我没有 把数据按小段模式储存的情况- -!、这是一个悲剧 --------------------编程问答--------------------
能找到这个算法 当时我已近很满足了 - -!、、貌似 也只找到这个算法 --------------------编程问答-------------------- RGB弄反了,我本来想说了,可是连续发了三次贴了。 --------------------编程问答--------------------
只能说 这是一个美丽的错误 提取的时候搞反了 写入的时候 也搞反了 综合起来就正常了 - -!、、
只能说 切记啊 这种错误不是一次出现了 --------------------编程问答-------------------- --------------------编程问答-------------------- 很好,看一看。 --------------------编程问答-------------------- NB啊 --------------------编程问答-------------------- 我基本不回贴看了你的程序,我真的佩服!中国能有这样的人材不容易呀! --------------------编程问答-------------------- 学习了,不错! --------------------编程问答-------------------- 不错,顶一下。 --------------------编程问答-------------------- 佩服了 --------------------编程问答-------------------- 不错啊 --------------------编程问答-------------------- 不错! 顶一个 --------------------编程问答-------------------- 牛人,收藏了 --------------------编程问答-------------------- 程序里面重点就是那几个算法 而程序本身是没啥技术含量的
其实 我也是把别人的算法搬过来自己写了而已 --------------------编程问答-------------------- --------------------编程问答-------------------- 收藏了 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 不错,就是CPU消耗吃不消。 --------------------编程问答-------------------- 额就是CPU消耗吃不消。 --------------------编程问答-------------------- 很好,这样也行! --------------------编程问答-------------------- 很厉害,收藏学习! --------------------编程问答-------------------- 学习了。。。 膜拜一下。。 --------------------编程问答-------------------- 厉害啊。。。。。。 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 学习了,初学 --------------------编程问答-------------------- --------------------编程问答-------------------- --------------------编程问答-------------------- 嗯嗯。。可以可以。。 收藏了。 --------------------编程问答-------------------- 漂亮,非常不错的C#例子 --------------------编程问答-------------------- 很厉害, 学习学习! --------------------编程问答-------------------- --------------------编程问答-------------------- 这个牛 --------------------编程问答-------------------- 强啊,好好学习下
补充:.NET技术 , C#