SHP(光滑粒子流体动力学)流体模拟实现三:Marching Cube算法(2)

流体模拟(三)

Marching Cube算法(2)

我们在之前的流体系统类里新加入一些函数和成员,用来引用我们的MC类,便可以获得生成有表面的流体模型了,效果如图:

添加简单的天空盒以及Phong氏光照模型边有效果:

流体系统类的更改:

    class FluidSystem{

    public:
        ......

        //复制缓存
        void CuCopyArrayToDevice(float* device, const float* host, int offset, int size);
        
        // 隐式函数值计算
        virtual double GetImplicit(double x, double y, double z);
        virtual void CalImplicitField(int n[3], glm::vec3 minp, glm::vec3 d, float *hF);
        virtual void CalImplicitFieldDevice(int n[3], glm::vec3 minp, glm::vec3 d, float *dF);
        
        // 使用metaball隐式函数值
        double CalColorField(double x, double y, double z);
        
        float* getPointPosBuf(){return &posData[0];}     //获取点位置缓存
        float* getPolygonBuf();//获取三角面
        float* getSufPosBuf(){return &m_vrts[0].x;}//获取表面点
        
        void clearSuf(){m_nrms.clear();m_nrms.clear();m_face.clear();}
        int getSufVrtBufNum(){return (int)m_vrts.size();}
        int getPolyNum();

    private:
        ......
        rxMCMesh m_mcMesh;//mc类型的成员
        
        float* m_field;//密度场
        std::vector<float> m_polyBuf;//三角面缓存

        //表面信息
        vector<glm::vec3> m_vrts;//表面点坐标
        vector<glm::vec3> m_nrms;//点法线
        vector<rxFace> m_face;//表面三角面
        
        float m_thre;// 隐函数阈值
}

我们用m_mcMesh来控制我们的表面生成,m_field保存密度场,m_thre则为隐函数阈值(阈值内为表面内点,阈值外为表面外点)。还用了m_vrts,m_nrms,m_face来保存表面信息。添加的函数中,主要用函数GetImplicit,CalImplicitField,CalImplicitFieldDevice去计算我们的网格密度值,获取到所有网格的密度值,便可以用mc类获取表面信息。

函数实现如下:

  void FluidSystem::tick(){
        m_gridContainer.insertParticles(&m_pointBuffer);//每帧刷新粒子位置
        glm::vec3 tem=m_sphWallBox.min;

        CalImplicitFieldDevice(m_rexSize, tem, glm::vec3(0.125/m_gridContainer.getDelta()), m_field);
        clearSuf();//清空表面数据
        m_mcMesh.CreateMeshV(m_field, tem, 0.125/m_gridContainer.getDelta(), m_rexSize, m_thre, m_vrts, m_nrms, m_face);

        _computerPressure();
        _computerForce();
        _advance();
    }

 void FluidSystem::_init(unsigned short maxPointCounts, const fBox3 &wallBox, const fBox3 &initFluidBox, const glm::vec3 &gravity){
        m_pointBuffer.reset(maxPointCounts);
        m_sphWallBox=wallBox;
        m_gravityDir=gravity;
        m_pointDistance=pow(m_pointMass/m_restDensity, 1.0/3.0);//计算粒子间距
        _addFluidVolume(initFluidBox, m_pointDistance/m_unitScale);
        m_mcMesh=rxMCMesh();
        m_gridContainer.init(wallBox, m_unitScale, m_smoothRadius*2.0, 1.0,m_rexSize);//设置网格尺寸(2r)

        //初始化标量场
        m_field=new float[(m_rexSize[0]+1)*(m_rexSize[1]+1)*(m_rexSize[2]+1)]();
        posData=std::vector<float>(3*m_pointBuffer.size(),0);
    }

 //-----------------------------------------------------------------------------
    // MARK:隐含的函数值
    //-----------------------------------------------------------------------------
    double FluidSystem::GetImplicit(double x, double y, double z)
    {
        return CalColorField(x, y, z);
    }
    
    /*!
      *从粒子计算网格的隐含函数值
      * @param [in] n网格数
      * @param [in] minp网格的最小坐标
      * @param [in] d网格宽度
      * @param [out] hF隐含函数值(nx×ny×nz的数组)
      */
    void FluidSystem::CalImplicitField(int n[3], glm::vec3 minp, glm::vec3 d, float *hF)
    {
        int slice0 = n[0]+1;
        int slice1 = slice0*(n[1]+1);
        
        for(int k = 0; k < n[2]; ++k){
            for(int j = 0; j < n[1]; ++j){
                for(int i = 0; i < n[0]; ++i){
                    int idx = k*slice1+j*slice0+i;
                    glm::vec3 pos = minp+glm::vec3(i, j, k)*d;
                    hF[idx] = GetImplicit(pos[0], pos[1], pos[2]);
                }
            }
        }
    }
    
    /*!
      *从粒子计算网格的隐含函数值
      * @param [in] pnx,pny,pnz的网格数
      * @param [in] minp网格的最小坐标
      * @param [in] d网格宽度
      * @param [out] hF隐含函数值(nx×ny×nz的数组)
      */
    void FluidSystem::CalImplicitFieldDevice(int n[3], glm::vec3 minp, glm::vec3 d, float *dF)
    {
        float *hF = new float[(n[0]+1)*(n[1]+1)*(n[2]+1)]();
        
        CalImplicitField(n, minp, d, hF);
        CuCopyArrayToDevice(dF, hF, 0, (n[0]+1)*(n[1]+1)*(n[2]+1)*sizeof(float));
        
        delete [] hF;
    }

//计算颜色场(密度场)
    double FluidSystem::CalColorField(double x, double y, double z)
    {
        // MRK:CalColorField
        float c = 0.0;
        glm::vec3 pos(x, y, z);
        
        if(pos[0] < m_sphWallBox.min[0]) return c;
        if(pos[0] > m_sphWallBox.max[0]) return c;
        if(pos[1] < m_sphWallBox.min[1]) return c;
        if(pos[1] > m_sphWallBox.max[1]) return c;
        if(pos[2] < m_sphWallBox.min[2]) return c;
        if(pos[2] > m_sphWallBox.max[2]) return c;
        
        float h = m_smoothRadius;
        
        int cell[8];
        m_gridContainer.findCells(pos, h/m_unitScale, cell);
        
        // 近傍粒子(各向同性)
        for(int i=0;i<8;i++){
            if(cell[i] < 0) continue;
            int pndx=m_gridContainer.getGridData(cell[i]);
            while(pndx!=-1){
                Point* p=m_pointBuffer.get(pndx);
                float r = glm::distance(pos,p->pos)*m_unitScale;
                float q = h*h-r*r;
                if(q>0){
                    c += m_pointMass*m_kernelPoly6*q*q*q;
                }
                pndx=p->next;
            }
        }
        return c;
    }
    
//获取三角片面缓存
    float* FluidSystem::getPolygonBuf(){
        m_polyBuf.clear();
        for(int i=0;i<m_face.size();i++){
            for(int j=0;j<m_face[i].vert_idx.size();j++) {
                glm::vec3 posTem=m_vrts[m_face[i].vert_idx[j]];
                m_polyBuf.push_back(posTem.x);
                m_polyBuf.push_back(posTem.y);
                m_polyBuf.push_back(posTem.z);
            }
        }
        return &m_polyBuf[0];
    }
    
//获取顶点个数
    int FluidSystem::getPolyNum(){
        int tem=0;
        for(int i=0;i<m_face.size();i++){
            for(int j=0;j<m_face[i].vert_idx.size();j++){
                tem++;
            }
        }
        return 3*tem;
    }
    
//保存所有网格隐函数值
    void FluidSystem::CuCopyArrayToDevice(float* device, const float* host, int offset, int size){
        memcpy(device+offset, host, size);
    }
}

可以看到我们的密度场由CalColorField函数计算,它依旧用了公式3.3:

获取所有网格的密度值。我们在_init函数里添加了对标量场的初始化。在tick函数里多了CalImplicitFieldDevice操作,用来先计算所有网格的密度值,然后调用clearSuf函数清除所有表面数据,然后根据m_mcMesh.CreateMeshV函数,用密度场重新计算新的表面数据,以生成表面。

当所有的计算完成时,我们的m_vrts,m_nrms以及m_face都有了新的数据,在opengl中将其传入VBO便可以绘制出有表面的粒子和网格效果如下:

加上天空盒和简单的Phong氏光照后,效果勉强还过得去:

完成了表面绘制后,其实这里的流体表面还是有一些问题,较为明显的则是表面不够光滑,如上图就可以看出流体表面有强烈的颗粒感,Yu, Jihun 等人的“Reconstructing surfaces of particle-based fluids using anisotropic kernels”这篇文章则非常好的解决了这个问题,他利用了给每个粒子构建了各项异性的缩放旋转矩阵,能够使流体达到一个非常光滑的形态,该算法下节会详细介绍。

发布了22 篇原创文章 · 获赞 22 · 访问量 2587

猜你喜欢

转载自blog.csdn.net/qq_39300235/article/details/102718996
今日推荐