https://threejs.org/examples/#canvas_particles_sprites
根据这个改的
首先看一下源码,了解一下
上面是官方的例子
接着修改参数,得到我想要的结果
该有的参数都有的,最后的效果
class CFireRoutine //烟火程序{
public:
CFireRoutine()//构造函数
virtual ~CFireRoutine()//虚态的析构函数
// Functs (public)
void InitFire()//初始化
void ClrHotSpots()//清除燃烧点
void InitPallette()//初始化调色板
void SetHotSpots()//设置燃烧点
void MakeLines()//生成扫描线
//渲染(视频内存,宽度,高度)
void Render(DWORD* pVideoMemory,
int iwidth,
int iheight)
//均值(在点x,y处)(像素均值?)
unsigned char Average(int x, int y)
// props
int m_iFlameHeight//火焰高度
int m_iWidth//宽度
int m_iHeight//高度(应该是作图区高度)
//火焰来源,即从点火处的y坐标
int m_iFireSource//The y position for the lit spots
int m_iFireChance//燃烧几率
int m_iAvgFlameWidth//火焰平均宽度
int m_iAlpha//α(深度)
COLORREF m_FireColors[4]//火焰颜色
BYTE* m_pFireBits//火焰数据
DWORD m_pPalletteBuffer[256]//调色板
long* m_pYIndexes//颜色索引
}
//熔浆:
class CPlasmaRoutine
{
public:
CPlasmaRoutine()//构造函数
virtual ~CPlasmaRoutine()//虚态析构函数
// Methods
void SetDefaultValues(VARIANT* pExtParms)//设置缺省参数值(外部变量)
void InitializePlasma(VARIANT* pExtParms)//初始化岩浆参数
void Create(int iWidth,int iHeight)//按视图生成岩浆
void Render(DWORD* pBits, //按照像素位,视图宽度,高度,扫描线渲染
int iwidth,
int iheight,
int iLineLength)
//设置颜色索引的RGB值
void SetRGB(int iIndex,int R,int G,int B)
void InitCostBLTable()//初始化损耗平衡表?BL是不是balance的意思啊?不知道,反正不是对比度和亮度,还是色彩饱和度?好像都不是
void InitPallette()//初始化调色板
void CalcPlasma()//计算岩浆
//按照颜色开始,颜色结束,颜色步长创建渐变色,存到buffer里面
void CreateGradient(COLORREF clrStart,COLORREF clrEnd,long lSteps,COLORREF* pBuffer)
// Props
int m_iWidth//视图宽度
int m_iHeight//视图高度
int m_iAlpha//视图深度
BYTE* m_pPlasmaBits//岩浆序列缓冲
DWORD m_pPalletteBuffer[256]//调色板
int m_icostbl[256]//损耗平衡表
COLORREF m_PlasmaColors[16]// Yep 16 colors needed to generate our pallete... 采用16种颜色产生调色板
//以下应该是曲线拟合用的贝塞尔曲线四个控制点和矢量坐标
unsigned char m_a1,m_a2,m_a3,m_a4,m_b1,m_b2,m_b3,m_b4
int m_iModifier1
int m_iModifier2
int m_iModifier3
int m_iModifier4
//双方向修改器(两点拉拽形成的矢量)
int m_iXModifier1
int m_iXModifier2
int m_iYModifier1
int m_iYModifier2
}
// FireRoutine.cpp: implementation of the CFireRoutine class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "FireRoutine.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction 构造函数,初始化所有参数,注意火源初始值为2,燃烧概率初始值为10
//////////////////////////////////////////////////////////////////////
CFireRoutine::CFireRoutine()
{
m_iWidth = 0
m_iHeight = 0
m_pFireBits = NULL
m_pYIndexes = NULL
m_iFireSource = 2
m_iFireChance = 10
m_iFlameHeight = 50
m_iAlpha = 255
m_FireColors[0] = RGB(0,0,0)// Black
m_FireColors[1] = RGB(255,0,0)// Red
m_FireColors[2] = RGB(255,255,0)// Yellow
m_FireColors[3] = RGB(255,255,255)// White
m_iAvgFlameWidth = 35
}
//析构函数,如果颜色索引和渲染缓冲区存在则注销
CFireRoutine::~CFireRoutine()
{
if(m_pFireBits != NULL)
delete [] m_pFireBits
if(m_pYIndexes != NULL)
delete [] m_pYIndexes
m_pFireBits = NULL
m_pYIndexes = NULL
}
void CFireRoutine::InitFire()
{
// Get Rid of anything already there 初始化火焰,如果存在颜色索引和渲染缓冲则首先注销
if(m_pFireBits != NULL)
delete [] m_pFireBits
if(m_pYIndexes != NULL)
delete [] m_pYIndexes
// Add three to the height 高度自动加三
m_iHeight+=3
m_pYIndexes = new long[m_iHeight]//颜色索引时以火焰高度为长度的一个长整形数组
m_pFireBits = new BYTE[m_iWidth*m_iHeight]//渲染缓冲区为一个W*H长度的字符型数组
// Clear the Fire bits 将火焰缓冲区置零
memset(m_pFireBits,0,(m_iWidth*m_iHeight))
// do all the y index pre-calc.. 在计算之前先将颜色缓冲值初始化为每个元素=宽度*高度
for (int y = m_iHeighty >0y--)
m_pYIndexes[y] = y * m_iWidth
// Create our pallete
InitPallette()//初始化调色板
ClrHotSpots()//清除燃烧点
}
//所谓清除燃烧点就是将渲染缓冲区的某一行清零。哪一行呢?就是颜色索引中火源指向的那一行。
void CFireRoutine::ClrHotSpots()
{
// clear the fire line
memset(&m_pFireBits[m_pYIndexes[m_iFireSource]],0,m_iWidth)
}
//初始化调色板。您可能需要首先了解一下调色板的概念:虽然一个图可能是彩色的,但是每个像素如果
//都按照RGB三个字节方式存储,则成本太高了。调色板在于,假设这个图还是彩图,但是经过色彩分析,
//其中只有256种颜色是图中的常用颜色,这样一个字节就可以代表一个像素了,不过这个字节的值指的
//并不是RGB这种色彩矢量,而是256种颜色的代码,这256种颜色就是这幅图的调色板。
void CFireRoutine::InitPallette()
{
// Create a gradient between all the colors we have for our fire... 为我们的火焰创造一个过渡色使用的所有颜色
long iCount = 0
COLORREF clrStart
COLORREF clrEnd
for(int iColor = 1iColor<4iColor++) //火焰的四个颜色定义
{
clrStart = m_FireColors[iColor-1]//设置过渡色的起始颜色
clrEnd = m_FireColors[iColor]//设置过渡色的终止颜色
int r, g, b // First distance, then starting value 先计算距离,再计算值
float rStep, gStep, bStep // Step size for each color //每种颜色的过渡步进值
// Get the color differences 取得三个颜色RGB的起始颜色和过渡颜色色差
r = (GetRValue(clrEnd) - GetRValue(clrStart))
g = (GetGValue(clrEnd) - GetGValue(clrStart))
b = (GetBValue(clrEnd) - GetBValue(clrStart))
int nSteps = max(abs(r), max(abs(g), abs(b)))//过渡步长数量取为RGB红绿蓝中色差最大者
float fStep = (float)(255/3)/ (float)nSteps//将色差步长值转换为浮点数
// Calculate the step size for each color
rStep = r/(float)nSteps//求得各颜色分量的色差步长值
gStep = g/(float)nSteps
bStep = b/(float)nSteps
// Reset the colors to the starting position 将颜色设置为渐进色初始颜色
r = GetRValue(clrStart)
g = GetGValue(clrStart)
b = GetBValue(clrStart)
for (int iOnBand = 0iOnBand <nStepsiOnBand++) //按照RGB计算出在渐变色全程中每个颜色的实际值
{
//COLORREF color = RGB(r+rStep*iOnBand, g + gStep*iOnBand, b + bStep *iOnBand)
COLORREF color = RGB(b + bStep *iOnBand, g + gStep*iOnBand,r+rStep*iOnBand)
long lIndex = (int)(iOnBand * fStep)
if(lIndex+((iColor-1)*85) <255)
m_pPalletteBuffer[lIndex+((iColor-1)*85)] = color//计算结果放到调色板里面去
}
}
// Step on the second color a little bit...向终止颜色过渡,以下颜色生成的内容与上面基本一致。
clrStart = m_FireColors[0]
clrEnd = m_FireColors[1]
for(int kj=0kj<m_iFlameHeightkj++)
m_pPalletteBuffer[kj] = 0
int r, g, b // First distance, then starting value
float rStep, gStep, bStep // Step size for each color
// Get the color differences
r = (GetRValue(clrEnd) - GetRValue(clrStart))
g = (GetGValue(clrEnd) - GetGValue(clrStart))
b = (GetBValue(clrEnd) - GetBValue(clrStart))
int nSteps = max(abs(r), max(abs(g), abs(b)))
float fStep = (float)(85-m_iFlameHeight)/ (float)nSteps
// Calculate the step size for each color
rStep = r/(float)nSteps
gStep = g/(float)nSteps
bStep = b/(float)nSteps
// Reset the colors to the starting position
r = GetRValue(clrStart)
g = GetGValue(clrStart)
b = GetBValue(clrStart)
for (int iOnBand = 0iOnBand <nStepsiOnBand++)
{
//COLORREF color = RGB(r+rStep*iOnBand, g + gStep*iOnBand, b + bStep *iOnBand)
COLORREF color = RGB(b + bStep *iOnBand, g + gStep*iOnBand,r+rStep*iOnBand)
long lIndex = (int)(iOnBand * fStep)
m_pPalletteBuffer[lIndex+(85-m_iFlameHeight)] = color //填写颜色值
}
}
// Macro to get a random integer within a specified range */ 这是一个按照范围取随机数的宏
#define getrandom( min, max ) (( rand() % (int)((( max ) + 1 ) - ( min ))) + ( min ))
#include <time.h>
void CFireRoutine::SetHotSpots() //设置燃烧点
{
ClrHotSpots()//首先清除燃烧点
//m_iAvgFlameWidth
long lPosition = 0//按照横轴位置进行累加,直到抵达宽度界限
while(lPosition <m_iWidth)
{
// see if we should do a flame
if (getrandom(0,100) <m_iFireChance) //得到一个随机数,看看是不是在燃烧概率范围内,如果不是就跳过去
{
// get a flame width
long lFlameWidth = getrandom(1,m_iAvgFlameWidth)//随机取得一个火焰宽度
for(int i=0i<lFlameWidthi++)
{
if(lPosition <m_iWidth) //如果位置在屏幕范围内,设置一个燃烧点
{
m_pFireBits[m_pYIndexes[m_iFireSource]+lPosition] =254// set a hot spot!
lPosition++
}
}
}
lPosition++
}
// for (x = 0x <m_iWidthx++)
// {
// if (getrandom(0,100) <m_iFireChance)
// {
// }
// }
}
void CFireRoutine::MakeLines() //生成渲染扫描线
{
int x, y
for (x = 0x <m_iWidthx++) //横向循环,从屏幕左侧到右侧
{
for (y = m_iFireSourcey<m_iHeight-1y++) //纵向循环,从火源到火焰高度
// for (y = m_iHeighty >m_iFireSourcey--)
{
//m_pFireBits[m_pYIndexes[y-1]+x] =Average(x,y)
m_pFireBits[m_pYIndexes[y+1]+x] =Average(x,y)//火焰值为扫描线均值
}
}
}
unsigned char CFireRoutine::Average(int x, int y)
{
unsigned char ave_color
unsigned char ave1, ave2, ave3, ave4, ave5, ave6, ave7
// Make sure we are not at the last line... 只要不是扫描线最后一样,平均值按照下列方式计算。
if(y == m_iHeight)
ave1 = m_pFireBits[m_pYIndexes[y-1] + x]
else
ave1 = m_pFireBits[m_pYIndexes[y + 1] + x]
//扫描线均值的方法,以x,y为中心,小半径为1,大半径为2的横向椭圆,从这个范围内取得颜色,然后求均值:
//基本上是下面这个图的样式:格式:取点编号(坐标)
/*
1#(x, y+1)
6#(x-2, y) 4#(x-1, y) 7#(x,y) 3#(x+1, y) 5#(x+2, y)
2#(x, y-1)
*/
ave2 = m_pFireBits[m_pYIndexes[y - 1] + x]
ave3 = m_pFireBits[m_pYIndexes[y] + x + 1]
ave4 = m_pFireBits[m_pYIndexes[y] + x - 1]
ave5 = m_pFireBits[m_pYIndexes[y] + x + 2]
ave6 = m_pFireBits[m_pYIndexes[y] + x - 2]
ave7 = m_pFireBits[m_pYIndexes[y] + x]
ave_color = (ave1 + ave2 + ave3 + ave4 + ave5 + ave6 + ave7) / 7
//求出颜色均值并返回
return(ave_color)
}
//按照视频内存进行渲染
void CFireRoutine::Render(DWORD* pVideoMemory,
int iwidth,
int iheight
)
{
SetHotSpots() // generate random hotspots 产生燃烧点
MakeLines() // do all the math and screen updating //产生扫描线,更新屏幕
// Right now Im just going to blit it right onto the video memory //向视频内存做BitBlt缓冲拷贝
unsigned char* pSrcBitlin// = m_pFireBits+(m_iWidth*3)// get rid of our fire source //获取火源
BYTE *dst//=(BYTE*)Dib->pVideoMemory
BYTE r
BYTE g
BYTE b
for(int i=0i<m_iHeight-3i++) //逐行扫描
{
dst = (BYTE*)&pVideoMemory[(iwidth*i)]//取得视频当前行
pSrcBitlin =&m_pFireBits[m_pYIndexes[i+3]]//设置扫描线数据指针
for(int x=0x<m_iWidthx++)
{
//合成颜色,注意,是索引颜色取RGB分量后加上深度和饱和度,移位
r = GetRValue(m_pPalletteBuffer[pSrcBitlin[x]])
g = GetGValue(m_pPalletteBuffer[pSrcBitlin[x]])
b = GetBValue(m_pPalletteBuffer[pSrcBitlin[x]])
dst[0]=(BYTE)(((r-dst[0])*m_iAlpha+(dst[0]<<8))>>8)
dst[1]=(BYTE)(((g-dst[1])*m_iAlpha+(dst[1]<<8))>>8)
dst[2]=(BYTE)(((b-dst[2])*m_iAlpha+(dst[2]<<8))>>8)
dst+=4
}
}
}
关于岩浆和水波的源代码,概念差不多,都是首先建立颜色模型、然后匹配到缓冲中去,仔细对比一下就看懂了
<script type="text/javascript">
function trim(s) {
return s ? s.replace(/^\s*|\s*$/g, '') : s
}
function evaluate(s) {
var EL_PREFIX = "Data:"
if (!new RegExp("^\\s*" + EL_PREFIX).test(s))
return undefined
var properties = trim(s).substring(EL_PREFIX.length).split(//)
var o = {}
for (var k = 0 k < properties.length k++) {
var entry = properties[k]
var index = entry.indexOf('=')
if (index < 0)
continue
var name = trim(entry.substring(0, index))
var value = trim(entry.substring(index + 1))
var root = name.substring(0, 1).toLowerCase()
if (!o[root])
o[root] = []
var prop={}
prop[name]=value
o[root].push(prop)
}
return o
}
//================================
var reversed=evaluate("Data:Title=helloname=jacknumber=1433address=USzCode=1244nickname=kk")
alert([
"Title=" + reversed.t[0].Title,
"name=" + reversed.n[0].name,
"number=" + reversed.n[1].number+'...'
].join('\n'))
</script>