• D3D中的粒子系统(3)


    14.2.1 绘制粒子系统

    因为粒子系统是动态的,在每一个帧中我们需要更新系统中的粒子,对于渲染粒子系统的一种直观但效率低下的方法如下:

    创建一个足够大的顶点缓存保存最大数量的粒子。

    每一帧里执行:

    A.       更新所有粒子。

    B.       COPY所有活着的粒子到顶点缓存。

    C.       绘制顶点缓存。

    这个方法正确,不过不是最有效率的。第一,顶点缓冲必须足够大以保存系统中所有粒子。但是非常重要的是,当我们从列表拷贝所有粒子到顶点缓冲(步骤B)时,显卡却什么也不做。举个例子,假设我们系统有10,000个粒子,首先我们需要一个能容纳10,000个粒子的顶点缓冲,这是一个很大的内存。另外显卡将停着什么也不做直到列表中的10,000个粒子拷到顶点缓冲,直到我们调用DrawPrimitive。这个特定情况是CPU与显卡不同时工作的一个很好的例子。

    更好的办法(SDK中点精灵例程中用到的方法)就象这样:

    提示:这是一个简单的描述,但它说明了这一思想。它假定我们总是有500个粒子以填充一个缓存片段,但是这是不可能发生的,因为我们经常杀死并创建粒子,所以从一帧到另一帧粒子数量是变化的。举个例子,假设我们只剩下200个粒子要在当前帧拷贝并渲染。因为200个粒子不能填充整个缓存片段,我们用代码处理这个特定情形。这个特定情形只有在最后的缓存片段中才会出现,因为如果不是最后的片断,就意味着必然有500个粒子将被移到下一缓存片段。

    创建一个合适尺寸的顶点缓存(能够保存2000个粒子),然后我们划分顶点缓存为几个小的块,就像这个例子,我们设置每个缓存片断的尺寸为500个粒子。

    l然后创建一个全局变量 i = 0 ,用来记录片段。

    每一帧里执行:

    A.       更新所有粒子。

    B.       直到所有粒子渲染完毕。

    1.        如果顶点缓存没有满:

    a         用D3DLOCK_NOOVERWRITE标记锁定缓存片段i

    b         COPY 500个粒子到片段i

    2.        如果顶点缓存满了:

    a         从起始的地方开始顶点缓冲: i=0

    b         用D3DLOCK_NOOVERWRITE标记锁定缓存段i

    c          COPY 500个粒子到片段i

    3.        渲染片段i.

    4.        下一片段: i+ +

    备注:顶点缓存是动态的, 因此我们能利用动态锁定标记D3DLOCK_NOOVERWRITE和 D3DLOCK_DISCARD。这两个标记允许我们锁定顶点缓存的某一部分。当顶点缓存中的其他部分被渲染时,它是不能渲染的。例如,假如我们正在使用D3DLOCK_NOOVERWRITE标记渲染片段0时, 当渲染片段0的时候我们能锁定并填充片段1。这样可以防止渲染的延迟。

    这个方法更有效率。首先,我们减少顶点缓存的尺寸;然后, CPU与显卡在协调的工作。也就是说,当我们绘制一小批粒子时(graphics card work),同时拷贝另一小批粒子到顶点缓存 (CPU work)。这个动作是连续执行的,直到所有的粒子都被渲染完毕,就像你了解的一样, 显卡在全部顶点缓存被填充的时候是不用处于空闲状态的。

    我们现在将注意力转向这一个渲染方案的实现,为了方便使用这个粒子系统的渲染方案, 我们使用 cParticleSystem 类中的下列数据成员:

    m_vb_num—在给定时间内我们的顶点缓存能够保存的粒子数量。这个值与实际的粒子系统中的粒子数无关。

    m_vb_offset—这个变量是顶点缓存中的偏移,在顶点缓存里我们将用它开始COPY下一批粒子,例如,如果第一批在缓存中是0到499,偏移到第二批COPY的开始处将是500。

    m_vb_batch_size—定义一批缓存中的粒子数量。

    我们现在介绍渲染方法的代码:
        void cParticleSystem::render()
        {
            // The render method works by filling a section of the vertex buffer with data, then we render that section. 
            // While that section is rendering we lock a new section and begin to fill that section. 
            // Once that sections filled we render it. This process continues until all the particles have been drawn. 
            // The benifit of this method is that we keep the video card and the CPU busy. 
            if(m_particles.empty())
                return;
            // set render states
            pre_render();
            m_device->SetTexture(0, m_texture);
            m_device->SetFVF(PARTICLE_FVF);
            m_device->SetStreamSource(0, m_vertex_buffer, 0, sizeof(sParticle));
            //
            // render batches one by one
            //
            // start at beginning if we're at the end of the vertex buffer
            if(m_vb_offset >= m_vb_num)
                m_vb_offset = 0;
            sParticle* v;
            m_vertex_buffer->Lock(
                m_vb_offset * sizeof(sParticle),
                m_vb_batch_num * sizeof(sParticle),
                (void**)&v,
                m_vb_offset ? D3DLOCK_NOOVERWRITE : D3DLOCK_DISCARD);
            DWORD num_particles_in_batch = 0;
            // until all particles have been rendered
            for(list<sParticleAttribute>::iterator iter = m_particles.begin(); iter != m_particles.end(); iter++)
            {
                if(! iter->is_alive)
                    continue;
                // copy a batch of the living particles to the next vertex buffer segment
                v->position = iter->position;
                v->color    = (D3DCOLOR) iter->color;
                v++;    // next element
                num_particles_in_batch++;
                // if this batch full?
                if(num_particles_in_batch == m_vb_batch_num)
                {
                    // draw the last batch of particles that was copied to the vertex buffer
                        m_vertex_buffer->Unlock();
                    m_device->DrawPrimitive(D3DPT_POINTLIST, m_vb_offset, m_vb_batch_num);
                    //
                    // While that batch is drawing, start filling the next batch with particles.
                    //
                    // move the offset to the start of the next batch
                        m_vb_offset += m_vb_batch_num;
                    // Don't offset into memory that is outside the vb's range.
                    // If we're at the end, start at the beginning.
                    if(m_vb_offset >= m_vb_num)
                        m_vb_offset = 0;
                    m_vertex_buffer->Lock(
                        m_vb_offset * sizeof(sParticle),
                        m_vb_batch_num * sizeof(sParticle),
                        (void**)&v,
                        m_vb_offset ? D3DLOCK_NOOVERWRITE : D3DLOCK_DISCARD);
                    num_particles_in_batch = 0;    // reset for new batch
                }
            }
            m_vertex_buffer->Unlock();
            // Its possible that the LAST batch being filled never got rendered because the condition
            // (num_particles_in_batch == m_vb_batch_num) would not have been satisfied. 
            // We draw the last partially filled batch now.   
            if(num_particles_in_batch)
                m_device->DrawPrimitive(D3DPT_POINTLIST, m_vb_offset, num_particles_in_batch);
            m_vb_offset += m_vb_batch_num;    // next block
                post_render();                    // reset render states
        }
    14.2.2 随机

    如果我们模拟雪花,不能让所有雪花以完全相同的方式落下。我们要让它们按相似的方式落下而不是完全相同的方式。为了使粒子系统的随机功能更简单,我们增加了下列两个函数到d3dUtility.h/cpp文件。

    第一个函数在[low_bound, high_bound]区间内随机的返回一个float类型值:
        float get_random_float(float low_bound, float high_bound)
        {
            if(low_bound >= high_bound)    // bad input
                return low_bound;
            // get random float in [0, 1] interval
            float f = (rand() % 10000) * 0.0001f;
            // return float in [low_bound, high_bound] interval
            return f * (high_bound - low_bound) + low_bound;
        }

    第二个函数在边界盒的范围内,输出一个随机的向量。
        void get_random_vector(D3DXVECTOR3* out, D3DXVECTOR3* min, D3DXVECTOR3* max)
        {
            out->x = get_random_float(min->x, max->x);
            out->y = get_random_float(min->y, max->y);
            out->z = get_random_float(min->z, max->z);
        }

  • 相关阅读:
    北邮《数据库原理与应用》应用阶段作业二带答案
    指针(字符串排序)
    Mysql—二进制日志(binlog)
    Mysql—日志文件系统
    7zip批量压缩,并批量改.jar
    解决:Windows 强制升级为8.1之后 Mysql连接不上, VisualSVN Server无服务
    推荐一个很好的博客
    c/c++ 继承与多态 继承时如何改变个别成员的访问属性
    c/c++ 继承与多态 友元与继承
    c/c++ 继承与多态 由子类向父类的转换规则
  • 原文地址:https://www.cnblogs.com/flying_bat/p/1137669.html
Copyright © 2020-2023  润新知