粒子系统的绘制

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/DY_1024/article/details/87342991

粒子系统在上一篇博客中简单介绍了一下:DirectX中的粒子系统

现在我们知道,粒子系统是动态的,我们必须每一帧都去更新这个粒子系统,有一种直观但是效率不高的方法如下:

1、更新所有粒子的状态

2、创建一个足够容纳粒子系统中最大粒子数目的顶点缓存

3、将所有处于活动状态的粒子赋值到这个巨大的顶点缓存中去

4、将顶点缓存中的粒子全部绘制出来

该方法的效率不高的原因,其一是因为我们必须创建一个足够大的顶点缓存,里面需要存放粒子系统中最大的粒子数目;其二是因为我们在创建顶点缓存之后,需要将所有的顶点都复制到顶点缓存里面,数目非常的大,此时GPU的没有任何的事情在做的,相当于空闲状态,此时因为CPU和GPU没有协同工作,所以效率是不高的。

一种更好的方法就是我们现在创建一个合理大小的顶点缓存,然后我们将这个顶点缓存划分为几个片段,比如,我们创建一个可以容纳2000粒子的顶点缓存,然后我们把它分为4个片段,每一个片段可以容纳500个粒子,然后创建一个全局变量i来跟踪这个片段。

上面这个是一种理想化的情况,事实上我们总是在不断的创建和销毁粒子,所以粒子的数目很有可能不足500,填不满一个片段,比如现在我需要绘制200个粒子,我们可以将这种情况作为一个特殊的情况来处理,这种情况只会在当前帧时填充最后一个片段时发生,也就是我们将不够一个片段的粒子放在最后一个片段去填充。

该方案的优点在于,大大减少了顶点缓存的大小,而且使得CPU和GPU可以协同工作,因为我们先去赋值一部分的顶点在顶点缓存里,然后让GPU去绘制这些顶点,绘制的时候,再去复制下一批顶点到顶点缓存里面,就这样直到所有的粒子被绘制完毕,这个过程中,GPU就不会出现空闲的状态。

下面为这种绘制方案的具体代码实现:

void PSystem:: render()
{
	if(l_particles. empty())
	{
		//设置渲染状态 
		preRender();
		
		//设置纹理
		_device->SetTexture(0,_tex);
		//
		device->SetFVF(Particle:: FVF); 
		//绑定数据流
		device->SetStreamsource(0,_vb,0, sizeof(Particle));
		//start at beginning if we' re at the end of the vb 
		//如果偏移量大于顶点缓存的大小,则从头开始绘制
		if(_vboffset>=vbSize)
			_vboffset=0; 
		
		//定义一个粒子结构体的指针
		Particle*v=0;
		
		//对顶点缓存进行操作的时候先去上锁
		//_vbBatchSize每一批粒子的数目
		_vb->Lock(
			//偏移量乘以每个粒子的大小,这个参数就是从顶点缓存的起始位置偏移了多少字节
			_vboffset * sizeof(Particle),
			//每一个片段的数目乘每个粒子的大小,就是每一个片段的字节大小
			_vbBatchSize* sizeof(Particle),
			//刚才定义的粒子结构体的指针,因为缓存里面存放的是粒子结构,使用这个粒子指针来控制缓存
			(void**)&v,
			//逻辑运算符,动态锁定标记
			_vbOffset ? D3DLOCK_NOOVERWRITE : D3DLOCK_DISCARD
			); 
	
		
		//每一个片段的粒子数量
		DWORD numParticlesInBatch=0;
	
		//
		//Until all particles have been rendered.
		//直到所有的粒子被渲染
		
		//定义一个链表,然后链表里面的节点的类型是:属性
		std:: list<Attribute>:: iterator i; 
		//迭代器指向粒子的头,然后遍历这个粒子
		for(i=_particles. begin();i!=_particles.end();i++)
		{
			//判断这个粒子是否存活
			if(i->_isAlive)
			{
				//
				//copy a batch of the living particles to the
				//next vertex buffer segment 
				//将仍然存活的粒子复制到顶点缓存里面
				
				//i现在指的是粒子系统链表上的节点
				
				//把粒子系统上的节点的坐标和颜色复制到缓存v里面
				v->_position=i->_position; 
				v->_color = (D3DCOLOR)i->_color;.
				v++;	//下一个节点; 
				
				numParticlesInBatch++; //每一个片段里面的计数器
				
				//判断这个片段计数器是不是等于每一个片段的最大值
				//也就是判断当前片段有没有满
				if(numparticlesInBatch ==_vbBatchsize)
				{
					//
					//Draw the last batch of particles that was
					//copied to the vertex buffer.
					//绘制复制到顶点缓冲区的最后一批粒子,也就是把刚才填满的片段绘制出来
				
					//解锁,然后后画出来
					vb->Unlock(); 
					device->DrawPrimitive(
						D3DPT_POINTLIST,
						_vboffset, 
						vbBatchSize);
					
					//
					//While that batch is drawing, start filling the
					//next batch with particles.
					//上一个函数已经开始绘制了,现在继续填充下一批粒子
					//将偏移量移动到下一个片段的开头
					_vboffset +=_vbBatchsize;
				
					//don't offset into memory thats outside the vb's range.
					//If we' re at the end, start at the beginning.
					//判断当前整个顶点缓存有没有填充满,如果偏移量大于等于当前整个顶点缓存大小,则表示所有的片段都已经填充满了,此时就把偏移量归零,从头开始
					if(_vboffset >=_vbSize)
						vbOffset=0;
						
					_vb->Lock(
						vboffset *sizeof(Particle),
						_vbBatchsize *sizeof(Particle),
						(void**)&V,
						_vboffset ? D3DLOCK_NOOVERWRITE:D3DLOCK_DISCARD
						);
					//此时顶点缓存已经从头开始,所以片段计数器应该归零
					numParticlesInBatch=0;// reset for new batch
				}
			}
		}
		
		_vb->Unlock();
		//its possible that the LAsT batch being filled never
		//got rendered because the condition
		//(numParticlesInBatch == _vbBatchsize) would not have
		//been satisfied. We draw the last partially filled batch now.
		//因有一种情况是最后的那一点点粒子没有办法 去填充完整个片段,所以单独去绘制一次
		if(numParticlesInBatch)
		{
			device->DrawPrimitive(
				D3DPT_POINTLIST,
				_vboffset, numParticlesInBatch);
		}
		
		// next block
		//将偏移量移动到下一个片段的开头,
		//暂时还没有发现这个代码的具体用处,因为最后一次已经绘制完成,下一次绘制应该是从顶点缓存的起始开始的
		//此时继续往下偏移感觉没什么用,
		_vboffset += _vbBatchSize;
		
		//
		// reset render states
		// 重置绘制状态
		
		postRender();
	}//end if
}//end render

猜你喜欢

转载自blog.csdn.net/DY_1024/article/details/87342991
今日推荐