DirectX11:Position Based Fluid

news2024/11/25 23:38:34

前言

这是我本科毕业设计项目,使用DirectX11实现一个基于PBD的流体模拟仿真,同时也算是补了Games101的大作业了。

阅读本文假设你对以下内容比较熟悉:

流体模拟:Smoothed Particle Hydrodynamics

流体模拟:NeighborHood Search

DirectX11:GPU基数排序

流体模拟:Position Based Fluid

 

算法过程


具体过程

领域搜索(Neighbor Search)

我们采用空间哈希的方法对粒子所处的空间网格进行划分,通过计算其空间哈希值并将其进行排序,得到当前网格的起始与结束地址。(具体实现可参考:流体模拟:NeighborHood Search)

我们可以通过遍历当前粒子附近的27个网格得到出其的邻居粒子(即粒子之间距离少于一定距离,本项目设为粒子半径),最大邻居粒子数量设为96。

HLSL核心代码

#include "PBFSolverCommon.hlsli"
//Find Neighbor Paticle
[numthreads(THREAD_NUM_X, 1, 1)]
void CS( uint3 DTid : SV_DispatchThreadID )
{
    
    if (DTid.x >= g_ParticleNums)
    {
        return;
    }

    //curr particle pos
    float3 currPos = g_sortedNewPosition[DTid.x];
    float3 sceneMin = g_Bounds[0];
    int3 currCellPos = floor((currPos - sceneMin) / g_CellSize);
    
    //curr particle index
    //uint currParitcleIndex = g_ParticleIndex[DTid.x];
    
    int neighborCount = 0;
    int x = 0, y = 0, z = 0;
    [unroll(3)]
    for (z = -1; z <= 1; ++z)
    {
        [unroll(3)]
        for (y = -1; y <= 1; ++y)
        {
            [unroll(3)]
            for (x = -1; x <= 1; ++x)
            {
                //find 27 cell neighbor particle
                int3 neighCellPos = currCellPos + int3(x, y, z);
                if (neighCellPos.x < 0.0f || neighCellPos.y < 0.0f || neighCellPos.z < 0.0f)
                {
                    continue;
                }
                uint cellHash = GetCellHash(neighCellPos);
                uint neighborCellStart = g_CellStart[cellHash];
                uint neighborCellEnd = g_CellEnd[cellHash];
                if (neighborCellStart >= neighborCellEnd)
                {
                    continue;
                }
                for (uint index = neighborCellStart; index < neighborCellEnd; ++index)
                {
                    //get the cell particle pos
                    float3 neighborPartclePos = g_sortedNewPosition[index];
                    float3 distance = currPos - neighborPartclePos;
                    float distancesq = dot(distance, distance);
                    if (distancesq < g_ParticleRadiusSq)
                    {
                       //contact
                        int contactsIndex = DTid.x * g_MaxNeighborPerParticle + neighborCount;
                        g_Contacts[contactsIndex] = index;
                        neighborCount++;
                    }
                    if (neighborCount == g_MaxNeighborPerParticle)
                    {
                        g_ContactCounts[DTid.x] = neighborCount;
                        return;
                    }
                }

            }
        }
    }
    
    g_ContactCounts[DTid.x] = neighborCount;
}

不可压缩约束和拉格朗日乘子

HLSL核心代码:

#include "PBFSolverCommon.hlsli"
[numthreads(THREAD_NUM_X, 1, 1)]
void CS( uint3 DTid : SV_DispatchThreadID )
{
    if (DTid.x >= g_ParticleNums)
    {
        return;
    }

    //curr particle pos
    float3 currPos = g_sortedNewPosition[DTid.x];
    //curr neighbor count
    uint neightborCount = g_ContactCounts[DTid.x];
    
    //clac density
    float density = 0;
    //clac Lagrange multiplier
    float3 gradSum_i = float3(0.0f, 0.0f, 0.0f);
    float gradSum_j = 0;
    
    uint i = 0;
    for (i = 0; i < neightborCount; ++i)
    {
         //get the cell particle pos
        uint neightborParticleIndex = g_Contacts[DTid.x * g_MaxNeighborPerParticle + i];
        float3 neighborPartclePos = g_sortedNewPosition[neightborParticleIndex];
        //r=p_i-p_j
        float3 r = currPos - neighborPartclePos;
        density += WPoly6(r, g_sphSmoothLength);

        float3 currGrad = WSpikyGrad(r, g_sphSmoothLength);
        currGrad *= g_InverseDensity_0;
        gradSum_i += currGrad;

        if (neightborParticleIndex != DTid.x)
        {
            gradSum_j += dot(currGrad, currGrad);
        }
        
    }
    
    //debug show 
    g_Density[DTid.x] = density;
    float gradSumTotal = gradSum_j + dot(gradSum_i, gradSum_i);
    // evaluate density constraint
    float constraint = max(density * g_InverseDensity_0 - 1.0f, 0.0f);
    float lambda = -constraint / (gradSumTotal + g_LambdaEps);
    
    g_LambdaMultiplier[DTid.x] = lambda;
}

约束投影与拉伸不稳定性


HLSL核心代码:

#include "PBFSolverCommon.hlsli"
[numthreads(THREAD_NUM_X, 1, 1)]
void CS( uint3 DTid : SV_DispatchThreadID )
{
    if (DTid.x >= g_ParticleNums)
    {
        return;
    }

    //curr particle pos
    float3 currPos = g_sortedNewPosition[DTid.x];
    //curr neighbor count
    uint neightborCount = g_ContactCounts[DTid.x];

    float currLambda = g_LambdaMultiplier[DTid.x];
    float poly6Q = WSpiky(g_DeltaQ, g_sphSmoothLength);

    float3 deltaPos = float3(0.0f, 0.0f, 0.0f);
    uint i = 0;
    for (i = 0; i < neightborCount; ++i)
    {
        //get the cell particle pos
        uint neightborParticleIndex = g_Contacts[DTid.x * g_MaxNeighborPerParticle + i];
        float neighborLambda = g_LambdaMultiplier[neightborParticleIndex];
        //get the cell particle pos
        float3 neighborParticlePos = g_sortedNewPosition[neightborParticleIndex];
        //r=p_i-p_j
        float3 r = currPos - neighborParticlePos;
        float poly6 = WSpiky(r, g_sphSmoothLength);
        float diffPoly = poly6 / poly6Q;
        float scorr = -g_ScorrK * pow(abs(diffPoly), g_ScorrN);
        float coff_j = currLambda + neighborLambda + scorr;

        float3 currGrad = WSpikyGrad(r, g_sphSmoothLength);
        deltaPos += coff_j * currGrad;
    }
    
    deltaPos = deltaPos * g_InverseDensity_0;
    g_DeltaPosition[DTid.x] = deltaPos;
}

上述描述的平均约束力确保了收敛性,但是在某些情况下,这种平均会过于激进,并且达到解所需的迭代次数增加。因此,我们需要一个全局用户参数ω来控制逐次超松驰法(SOR)的速度。


HLSL核心代码:

#include "PBFSolverCommon.hlsli"
[numthreads(THREAD_NUM_X, 1, 1)]
void CS( uint3 DTid : SV_DispatchThreadID)
{
    if (DTid.x >= g_ParticleNums)
    {
        return;
    }
    
    uint totalNeighborNum = g_ContactCounts[DTid.x];
    float factor = max(g_SOR * totalNeighborNum, 1.0f);
    float3 resPos = g_sortedNewPosition[DTid.x] + g_DeltaPosition[DTid.x] * (1 / factor);
    
    g_sortedNewPosition[DTid.x] = resPos;
}

处理碰撞

粒子与边界碰撞的处理也一直是一个非常关键的问题,一般来说,在SPH方法中采用将边界粒子也视为粒子(即边界粒子)进行表达,我不想采用这种方法。

另一种处理方法是采用SDF(Signed Distance Field)的方式变大空间中容器的位置,然后判断粒子是否出去了这个空间并将其推回去,从而保证粒子在容器之中。因为时间有限以及场景都是简单的几何体,所以本项目直接使用了解析SDF函数来解决。若场景复杂,存在多面体模型的话,可以考虑先bake出当前场景的中3D SDF Texture,在Shader中进行采样进行计算。

因为本场景采用平面对粒子的容器范围进行限制,所以采用了平面的SDF距离场公式:

//sdf plane function
float sdfPlane(float3 p, float3 n, float h)
{
    // n must be normalized
    return dot(p, n) + h;
}

所以我们处理粒子的碰撞时,首先在约束求解前得到每个粒子接触的平面(最大的接触平面数为6)。在每次迭代求解后,对其进行判断是否出去了这个空间并将其推回去。

处理碰撞过程中,我还增加了一个friction model,具体公式如下:

HLSL核心代码:

//CollisionPlane_CS.hlsl
#include "PBFSolverCommon.hlsli"
[numthreads(THREAD_NUM_X, 1, 1)]
void CS( uint3 DTid : SV_DispatchThreadID )
{
    if (DTid.x >= g_ParticleNums)
    {
        return;
    }
    
    float3 currPos = g_sortedNewPosition[DTid.x];
    
    int count = 0;
    int i = 0;
    [unroll]
    for (i = 0; i < g_PlaneNums; ++i)
    {
        float distance = sdfPlane(currPos, g_Plane[i].xyz, g_Plane[i].w) - g_CollisionDistance;
        if (distance < g_CollisionThreshold && count < g_MaxCollisionPlanes)
        {
            int index = DTid.x * g_MaxCollisionPlanes + count;
            g_CollisionPlanes[index] = g_Plane[i];
            count++;
        }
    }
    
    g_CollisionCounts[DTid.x] = count;
    
}

 

//SolveContact_CS.hlsl
#include "PBFSolverCommon.hlsli"
[numthreads(THREAD_NUM_X, 1, 1)]
void CS( uint3 DTid : SV_DispatchThreadID )
{
    
    if (DTid.x >= g_ParticleNums)
    {
        return;
    }
    
    float3 currPos = g_sortedNewPosition[DTid.x];                  
    float3 oldPos = g_sortedOldPosition[DTid.x];
    
    int collisionCount = g_CollisionCounts[DTid.x];
    int i = 0;
    for (i = 0; i < collisionCount; ++i)
    {
        int index = DTid.x * g_MaxCollisionPlanes + i;
        float4 currPlane = g_CollisionPlanes[index];
        float distance = sdfPlane(currPos, currPlane.xyz, currPlane.w) - g_CollisionDistance; //d
        if (distance < 0.0f)
        {
            float3 sdfPos = (-distance) * currPlane.xyz; 
            
            //friction model
            float3 deltaPos = currPos - oldPos;
            float deltaX = dot(deltaPos, currPlane.xyz);
            float3 deltaDistane = (-deltaX) * currPlane.xyz + deltaPos; //DeltaX 
            float deltaLength = dot(deltaDistane, deltaDistane);
            [flatten]
            if (deltaLength <= (g_StaticFriction * distance))        //|deltaX|< u_s*disctance
            {
                sdfPos -= deltaDistane;
            }
            else
            {
                float dynamicFriction = min((-distance) * 0.01f * rsqrt(deltaLength), 1.0f); //
                sdfPos -= dynamicFriction * (deltaDistane);
            }
            currPos += sdfPos;
        }
    }
    
    g_UpdatedPosition[DTid.x] = currPos;
}

更新速度

这里的公式非常简单:

 HLSL核心代码:

#include "PBFSolverCommon.hlsli"
[numthreads(THREAD_NUM_X, 1, 1)]
void CS( uint3 DTid : SV_DispatchThreadID )
{
    if (DTid.x >= g_ParticleNums)
    {
        return;
    }

    float3 oldPos = g_sortedOldPosition[DTid.x];
    float3 updatePos = g_sortedNewPosition[DTid.x];

    float3 newVec = g_InverseDeltaTime * (updatePos - oldPos);
    g_UpdatedVelocity[DTid.x] = newVec;
}

涡轮控制和人工粘性

由于数值耗散,PBD方法通常会引入额外的阻尼,导致整个系统的能来损耗,由此会导致本来该有的一些涡流快速消失。PBF通过vorticity confinement由系统重新注入能量:

HLSL核心代码:

#include "PBFSolverCommon.hlsli"
//Clac curl
[numthreads(THREAD_NUM_X, 1, 1)]
void CS( uint3 DTid : SV_DispatchThreadID )
{
    if (DTid.x >= g_ParticleNums)
    {
        return;
    }

   //curr neighbor count
    uint neightborCount = g_ContactCounts[DTid.x];
    
    //curr particle pos
    float3 currPos = g_sortedNewPosition[DTid.x];
    float3 currVec = g_UpdatedVelocity[DTid.x];
    float3 currOmega = float3(0.0f, 0.0f, 0.0f);
    
    uint i = 0;
    for (i = 0; i < neightborCount; ++i)
    {
        //get the cell particle pos
        uint neightborParticleIndex = g_Contacts[DTid.x * g_MaxNeighborPerParticle + i];
        //get the cell particle pos
        float3 neighborParticlePos = g_sortedNewPosition[neightborParticleIndex];
        //r=p_i-p_j
        float3 r = currPos - neighborParticlePos;
        //v_j-v_i
        float3 deltaVelocity = g_UpdatedVelocity[neightborParticleIndex] - currVec;
        float3 currGrad = WSpikyGrad(r, g_sphSmoothLength);
         //calc omega
        float3 omega_j = cross(deltaVelocity, currGrad);
        currOmega += omega_j;
    }
    
  
    float curlLength = length(currOmega);
    g_Curl[DTid.x] = float4(currOmega.xyz, curlLength);   
}
#include "PBFSolverCommon.hlsli"
[numthreads(THREAD_NUM_X, 1, 1)]
void CS( uint3 DTid : SV_DispatchThreadID )
{
    if (DTid.x >= g_ParticleNums)
    {
        return;
    }
    
    float3 currPos = g_sortedNewPosition[DTid.x];
    float3 currVec = g_UpdatedVelocity[DTid.x];
    float3 oldVec = g_sortedVelocity[DTid.x];
    
    
    uint counter = g_ContactCounts[DTid.x];
    
    float3 deltaTotalVec = float3(0.0f, 0.0f, 0.0f);
    float3 etaTotal = float3(0.0f, 0.0f, 0.0f);
    float density;
    for (uint i = 0; i < counter; ++i)
    {
        //get the cell particle pos
        uint neightborParticleIndex = g_Contacts[DTid.x * g_MaxNeighborPerParticle + i];
        //get the cell particle pos
        float3 neighborParticlePos = g_sortedNewPosition[neightborParticleIndex];
        //r=p_i-p_j
        float3 r = currPos - neighborParticlePos;
            //v_j-v_i
        float3 deltaVelocity = g_UpdatedVelocity[neightborParticleIndex] - currVec;
        
        float3 currGrad = WSpikyGrad(r, g_sphSmoothLength);
        
        
        //vorsitory confinement
        float neighCurlLength = g_Curl[neightborParticleIndex].w;
        etaTotal += currGrad * neighCurlLength;                            
                
           //XSPH
        float3 deltaVec_j = deltaVelocity * WSpiky(r, g_sphSmoothLength);
        deltaTotalVec += deltaVec_j;
        
         //density debug 
        density += WPoly6(r, g_sphSmoothLength);

    }
    
    float3 impulse = float3(0.0f, 0.0f, 0.0f);
    //vorticity Confinement
    if (length(etaTotal) > 0.0f && g_VorticityConfinement > 0.0f && density>0.0f)
    {
        float epsilon = g_DeltaTime * g_DeltaTime * g_InverseDensity_0 * g_VorticityConfinement;
        
        float3 currCurl = g_Curl[DTid.x].xyz;       //r2
        
        float3 N = normalize(etaTotal);
        float3 force = cross(N, currCurl);
        
        impulse += epsilon * force;

    }
    
    //XSPH
    impulse += g_VorticityC * deltaTotalVec;
    
    // solve plane 
    uint planeCounts = g_CollisionCounts[DTid.x];
    uint resCounts = 0;
    float3 restitutionVec = float3(0.0f, 0.0f, 0.0f);
    for (uint j = 0; j < planeCounts; ++j)
    {
        float4 plane = g_CollisionPlanes[DTid.x * g_MaxCollisionPlanes + j];
        float distance = sdfPlane(currPos, plane.xyz, plane.w) - 1.001f * g_CollisionDistance;
        
        float oldVecD = dot(oldVec, plane.xyz);
        if (distance < 0.0f && oldVecD < 0.0f)
        {
            float currVecD = dot(currVec, plane.xyz);
            float restitutionD = oldVecD * g_Restituion + currVecD;
            restitutionVec += plane.xyz * (-restitutionD);
            resCounts++;
        }
    }
    resCounts = max(resCounts, 1);
    restitutionVec /= resCounts;
    
    
    impulse += restitutionVec;
    g_Impulses[DTid.x] = impulse;
}

最终处理

最终我们只需将求解后的最终结果(位置与速度信息)输出即可,本项目还对粒子的最大速度进行一定的限制。

HLSL核心代码:

#include "PBFSolverCommon.hlsli"
[numthreads(THREAD_NUM_X, 1, 1)]
void CS( uint3 DTid : SV_DispatchThreadID )
{
    if (DTid.x >= g_ParticleNums)
    {
        return;
    }

    uint prevIndex = g_Particleindex[DTid.x];
    
    g_SolveredPosition[prevIndex] = g_sortedNewPosition[DTid.x];
    
    float3 currVec = g_UpdatedVelocity[DTid.x];
    float3 impulse = g_Impulses[DTid.x];
    float3 oldVec = g_oldVelocity[prevIndex];
    float3 deltaVec = currVec + impulse - oldVec;
    float deltaVecLengthsq = dot(deltaVec, deltaVec);
    if (deltaVecLengthsq > (g_MaxVeclocityDelta * g_MaxVeclocityDelta))
    {
        deltaVec = deltaVec * rsqrt(deltaVecLengthsq) * g_MaxVeclocityDelta;
    }
    float3 finVec = oldVec + deltaVec;
    g_SolveredVelocity[prevIndex] = finVec;
}

C++核心部分

因为代码太多,这里这粗略展示算法核心过程代码,具体代码可去下载地址查看:

void FluidSystem::TickLogic(ID3D11DeviceContext* deviceContext, PBFSolver::PBFParams params)
{
	m_pPBFSolver->SetPBFParams(params);

	for (int i = 0; i < params.subStep; ++i)
	{
		m_pPBFSolver->PredParticlePosition(deviceContext, *m_pPBFSolverEffect,
			m_pParticlePosBuffer->GetBuffer(), m_pParticleVecBuffer->GetBuffer());

		//NeighborSearch 
		m_GpuTimer_NeighBorSearch.Start();
		m_pNeighborSearch->BeginNeighborSearch(deviceContext, m_pPBFSolver->GetPredPosition(), m_pParticleIndexBuffer->GetBuffer(), params.cellSize);
		m_pNeighborSearch->CalcBounds(deviceContext, *m_pNeighborSearchEffect, m_pPBFSolver->GetPredPosition(), m_pParticleIndexBuffer->GetBuffer(), params.cellSize);
		m_pNeighborSearch->RadixSort(deviceContext, *m_pNeighborSearchEffect);
		m_pNeighborSearch->FindCellStartAndEnd(deviceContext, *m_pNeighborSearchEffect);
		m_pNeighborSearch->EndNeighborSearch();

		// Constraint iter solver
		m_pPBFSolver->BeginConstraint(deviceContext, *m_pPBFSolverEffect, m_pNeighborSearch->GetSortedParticleIndex(),
			m_pNeighborSearch->GetSortedCellStart(), m_pNeighborSearch->GetSortedCellEnd(), m_pNeighborSearch->GetBounds());
		m_pPBFSolver->SolverConstraint(deviceContext, *m_pPBFSolverEffect);
		m_pPBFSolver->EndConstraint(deviceContext, *m_pPBFSolverEffect);


		//update data
		m_pParticlePosBuffer->UpdataBufferGPU(deviceContext, m_pPBFSolver->GetSolveredPosition());
		m_pParticleVecBuffer->UpdataBufferGPU(deviceContext, m_pPBFSolver->GetSolveredVelocity());
	}
	m_pPBFSolver->CalcAnisotropy(deviceContext, *m_pPBFSolverEffect);
}

 

void PBFSolver::SolverConstraint(ID3D11DeviceContext* deviceContext, PBFSolverEffect& effect)
{
    effect.SetOutPutUAVByName("g_LambdaMultiplier", m_pLambdaMultiplierBuffer->GetUnorderedAccess());
    effect.SetOutPutUAVByName("g_Density", m_pDensityBuffer->GetUnorderedAccess());
    effect.SetOutPutUAVByName("g_DeltaPosition", m_pDeltaPositionBuffer->GetUnorderedAccess());
    effect.SetOutPutUAVByName("g_UpdatedPosition", m_pUpdatedPositionBuffer->GetUnorderedAccess());
    for (int i = 0; i < m_PBFParams.maxSolverIterations; ++i)
    {
        //calc lagrange multiplier
        effect.SetCalcLagrangeMultiplierState();
        effect.Apply(deviceContext);
        deviceContext->Dispatch(m_BlockNums, 1, 1);

        //calc Displacement
        effect.SetCalcDisplacementState();
        effect.Apply(deviceContext);
        deviceContext->Dispatch(m_BlockNums, 1, 1);

        //add deltapos
        effect.SetADDDeltaPositionState();
        effect.Apply(deviceContext);
        deviceContext->Dispatch(m_BlockNums, 1, 1);

        //solver contacts
        effect.SetSolverContactState();
        effect.Apply(deviceContext);
        deviceContext->Dispatch(m_BlockNums, 1, 1);


        m_pSortedNewPostionBuffer->UpdataBufferGPU(deviceContext, m_pUpdatedPositionBuffer->GetBuffer(),m_ParticleNums);
    }
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2221192.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UNIX网络编程-传输层

概述 传输层主要包括&#xff1a;TCP、UDP、SCTP&#xff08;流控制传输协议&#xff09;&#xff01; 绝大多数客户端/服务器网络应用都使用TCP/UDP。SCTP是一个较新的协议&#xff0c;最初设计用于跨因特网传输电话信令。 这些传输协议都转而使用网络协议IP&#xff1a;或是…

windows中命令行批处理脚本学习

目录 一 基础知识二 常见命令1. 输出 echo2. 注释 rem .... %...% :: goto if (10) ()3. 变量 set4. 获取参数 %数字 %*5. 退出 exit6. 复制 copy7.读取输出文件内容 type8. 帮助 命令xxx /?9.等待当前命令运行结束后,才执行下一条命令 call10. 修改字体编码 chcp11. 特殊变量…

集合框架16:HashMap的使用

视频链接&#xff1a;13.35 HashMap使用&#xff08;1&#xff09;_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1zD4y1Q7Fw?spm_id_from333.788.videopod.episodes&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5&p35 1.创建Student类&#xff0c;添加无参构造…

UML之用例图详解

~犬&#x1f4f0;余~ “我欲贱而贵&#xff0c;愚而智&#xff0c;贫而富&#xff0c;可乎&#xff1f; 曰&#xff1a;其唯学乎” 零、什么是用例图 用例图&#xff08;Use Case Diagram&#xff09;是UML中一种重要的图表类型&#xff0c;它主要用于描述系统的功能性需求&am…

Java使用HttpClient5实现发送HTTP请求

Java 实现发送 HTTP 请求&#xff0c;系列文章&#xff1a; 《Java使用原生HttpURLConnection实现发送HTTP请求》 《Java使用HttpClient5实现发送HTTP请求》 《SpringBoot使用RestTemplate实现发送HTTP请求》 1、HttpClient5 的介绍 HttpClient5 是 Apache HttpComponents 项目…

文件处理新纪元:微信小程序的‘快递员’与‘整理师’

嗨&#xff0c;我是中二青年阿佑&#xff0c;今天阿佑将带领大家如何通过巧妙的文件处理功能&#xff0c;让用户体验从‘杂乱无章’到‘井井有条’的转变&#xff01; 文章目录 微信小程序的文件处理文件上传&#xff1a;小程序的“快递服务”文件下载&#xff1a;小程序的“超…

植物大战僵尸杂交版游戏分享

植物大战僵尸杂交版游戏下载&#xff1a;夸克网盘分享 无捆绑之类的隐形消费&#xff0c;下载即玩

vue3 解决背景图与窗口留有间隙的问题

需要实现一个登录界面&#xff0c;login.vue的代码如下&#xff1a; <script> import { ref } from vue;export default {setup() {return {};}, }; </script><template><div id"login-container" class"login-container"><di…

Taro构建的H5页面路由切换返回上一页存在白屏页面过渡

目录 项目背景&#xff1a;Taro与Hybrid开发问题描述&#xff1a;白屏现象可能的原因包括&#xff1a; 解决方案解决后的效果图 其他优化方案可参考&#xff1a; 项目背景&#xff1a;Taro与Hybrid开发 项目使用Taro框架同时开发微信小程序和H5页面&#xff0c;其中H5页面被嵌…

Nodes 节点

Goto Tree List 树列表 Nodes 节点 Tree List 节点是组织成树状层次结构的数据行。 Add New Nodes 添加新节点 如果 Tree List 具有数据源&#xff0c;则会自动生成节点&#xff08;TreeListNode 类对象&#xff09;。要在未绑定模式下添加节点&#xff0c;请调用“树列表设…

【K8S系列】Kubernetes Pod节点Pending状态及解决方案详解【已解决】

在 Kubernetes 中&#xff0c;Pod 的状态为 Pending 表示 Pod 已被创建&#xff0c;但尚未被调度到节点上&#xff0c;或者已调度到节点上但容器尚未开始运行。这一状态常常是因为某些条件未满足&#xff0c;导致 Pod 无法正常启动。以下是对 Pending 状态的详细分析及解决方案…

自由学习记录(12)

综合实践 2D的Shape&#xff0c;Tilemap都要导包的&#xff0c;编辑器也要导包&#xff0c;。。和2d沾边的可能3d都要主动导包 应该综合的去运用&#xff0c;不见得Tilemap就很万能&#xff0c;如果要做什么顶方块的有交互反应的物体&#xff0c; 那直接拖Sprite会更方便一些…

APIJSON 为零代码提供了新的思路

APIJSON 核心概念 APIJSON 是一种用于构建 RESTful API 的 JSON 格式&#xff0c;它提供了一种标准化的方式来定义和处理 API 请求和响应。APIJSON 的设计目标是简化前端和后端之间的数据交互&#xff0c;减少开发工作量&#xff0c;提高开发效率。 在线解析 自动生成文档&am…

【SpringBoot】16 文件上传(Thymeleaf + MySQL)

Gitee仓库 https://gitee.com/Lin_DH/system 介绍 文件上传是指将本地的图片、视频、音频等文件上传到服务器&#xff0c;供其他用户浏览下载的过程&#xff0c;文件上传在日常项目中用的非常广泛。 实现代码 第一步&#xff1a;在配置文件新增如下配置 application.yml s…

docker-compose-lnmp-wordpress

使用 docker-compose 在 CentOS 7 上编写并部署 LNMP (Linux, Nginx, MySQL, PHP) 环境的 YAML 文章目录 部署步骤&#xff1a;1. 安装 Docker 和 Docker Compose1.1安装 Docker&#xff1a;1.2安装 Docker Compose&#xff1a; 2.创建目录结构3.编写docker-compose.yml4.ngin…

Java项目-基于springboot框架的财务管理系统项目实战(附源码+文档)

作者&#xff1a;计算机学长阿伟 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、ElementUI等&#xff0c;“文末源码”。 开发运行环境 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBoot、Vue、Mybaits Plus、ELementUI工具&#xff1a;IDEA/…

科研进展 | RSE:全波形高光谱激光雷达数据Rclonte系列处理算法一

《环境遥感》&#xff08;Remote Sensing of Environment&#xff0c;IF11.1&#xff09;近日发表一项来自中国科学院空天信息创新研究院王力、牛铮研究员团队的全波形高光谱激光雷达&#xff08;hyperspectral LiDAR&#xff0c;HSL&#xff09;数据处理算法研究&#xff0c;论…

计算机组成原理一句话

文章目录 计算机系统概述存储系统 计算机系统概述 指令和数据以同等地位存储在存储器中&#xff0c;形式上没有差别&#xff0c;但计算机应能区分他们。通过指令周期的不同阶段。 完整的计算机系统包括&#xff0c;1&#xff09;软件系统&#xff1a;程序、文档和数据&#xff…

DC系列靶机-DC6

一&#xff0c;环境的搭建 VM17 官网下载 kali 2023.4版 https://mirrors.tuna.tsinghua.edu.cn/kali-images/kali-2023.4/ 靶场文件 https://download.vulnhub.com/dc/DC-6.zip 二&#xff0c;攻略 首先进行主机发现&#xff1b; 接下来进行端口扫描&#xff1b; 开放了2…

初识git · 远程操作

目录 前言&#xff1a; 理解分布式版本控制系统 远程仓库 仓库操作 克隆仓库 推送和抓取 特殊文件 取别名 标签管理 前言&#xff1a; 在基本操作&#xff0c;分支管理这几个部分&#xff0c;我们都会在本地仓库操作了&#xff0c;但是目前还没有办法将自己的代码远程…