从我的工作着色器转换为统一计算着色器时流体模拟中的奇怪行为

Turmolt:

我正在尝试在Unity的计算着色器中复制工作的2D流体着色器,希望很快将其移至3D。当我以相同的方式复制算法时,我得到一些非常奇怪的行为(在我拍摄的此视频中看到)。我已经尝试调试所有我能想到的东西,但我不知道为什么它们不一样。我正在可视化此捕获中的向量矩阵(与在查看我的阴影时按下Space相同)。

使用用于执行驱动速度矩阵的Navier-Stokes方程的代码创建了一个pastebin模拟的内容归结为:

float4 S(RWTexture2D<float4> target, uint2 id)
{
    return target[(id.xy + resolution)%resolution];
}

void Fluid(RWTexture2D<float4> target, uint2 id, float2 offset, float4 values, inout float2 velocity, inout float pressure, inout float divergence, inout float neighbors)
{
    float2 v = S(target, id.xy + offset);
    float4 s = S(target, id.xy + offset.xy - v);
    
    float2 o= normalize(offset);
    
    velocity += o * (s.w - values.w);
    
    pressure += s.w;
    
    divergence += dot(o, s.xy);
    
    ++neighbors;
}

void StepVelocity(uint3 id, RWTexture2D<float4> write, RWTexture2D<float4> read, bool addJet)
{
    //sample our current values, then sample the values from the cell our velocity is coming from
    float4 values = S(read, id.xy);
    values = S(read, id.xy - values.xy);
    
    float2 velocity = float2(0,0);
    float pressure = 0;
    float neighbors = 0;
    float divergence = 0;
    
    //sample neighboring cells
    Fluid(read, id.xy, float2(0, 1), values, velocity, pressure, divergence, neighbors);
    Fluid(read, id.xy, float2(0, -1), values, velocity, pressure, divergence, neighbors);
    Fluid(read, id.xy, float2(1, 0), values, velocity, pressure, divergence, neighbors);
    Fluid(read, id.xy, float2(-1, 0), values, velocity, pressure, divergence, neighbors);

    velocity = velocity / neighbors;
    divergence = divergence / neighbors;
    pressure = pressure/neighbors;
    
    values.w = pressure-divergence;
    values.xy -= velocity;
    
    if (addJet && distance(id.xy, float2(resolution / 2.0, resolution / 2.0)) < 10)
        values = float4(0, .25, 0, values.w);
    
    write[id.xy] = values;
}

这应该很简单,我尽了最大努力对阴影框进行了注释,以使其易于理解(它们是相同的代码,针对不同的环境进行设置)。这是驱动模拟的C#代码。

我知道要求别人深入研究我的代码是很不方便的,但是我完全不确定我做错了什么,这使我发疯。我在sharttory上使用完全相同的算法,但是在Unity计算着色器中的行为非常奇怪,我不知道为什么。每次执行“步骤”时,我都要确保切换读/写纹理,以便在仿真中干净利落地前进而不干扰。

任何解决我的问题的想法/技巧将不胜感激。

冥王星:

为了使这项工作有效,我做了几件事更改:

首先,像素坐标需要偏移0.5。在计算着色器中,坐标为整数,但在ShaderToy中,传递的坐标来自SV_Position输入,该输入已应用了该偏移量。

SV_Position描述像素位置。在所有着色器中均可用,以使像素中心偏移0.5。

的情况也是如此gl_FragCoord默认情况下它偏移0.5。当您向位置添加速度时,这一点很重要。没有偏移,您将不会在所有方向上都具有相同的行为。

其次,我使用了采样器作为输入,点过滤似乎对此不起作用。否则,代码基本上是相同的,并且应该很容易遵循。

外观如下:

在此处输入图片说明

FluidSimulation.cs:

public class FluidSimulation : MonoBehaviour
{
    private RenderTexture In;
    private RenderTexture Out;
    private RenderTexture DrawIn;
    private RenderTexture DrawOut;
    
    private int nthreads = 8;
    private int threadresolution => (resolution / nthreads);
    private int stepKernel;

    [Range(8, 1024)] public int resolution = 800;
    [Range(0, 50)] public int stepsPerFrame = 8;
    public ComputeShader Compute;
    public Material OutputMaterial;

    void Start() => Reset();   

    private void Reset()
    {
        In = CreateTexture(RenderTextureFormat.ARGBHalf);
        Out = CreateTexture(RenderTextureFormat.ARGBHalf);
        DrawIn = CreateTexture(RenderTextureFormat.ARGBHalf);
        DrawOut = CreateTexture(RenderTextureFormat.ARGBHalf);
        
        stepKernel = Compute.FindKernel("StepKernel");
        Compute.SetFloat("resolution", resolution);        
    }

    void Update()
    {
        for(int i = 0; i<stepsPerFrame; i++) Step();
    }

    void Step()
    {
        Compute.SetTexture(stepKernel, "In", In);
        Compute.SetTexture(stepKernel, "Out", Out);
        Compute.SetTexture(stepKernel, "DrawIn", DrawIn);
        Compute.SetTexture(stepKernel, "DrawOut", DrawOut);     

        Compute.Dispatch(stepKernel, threadresolution, threadresolution, 1);
        
        OutputMaterial.SetTexture("_MainTex", DrawOut);

        SwapTex(ref In, ref Out);
        SwapTex(ref DrawIn, ref DrawOut);        
    }

    protected RenderTexture CreateTexture(RenderTextureFormat format)
    {
        RenderTexture tex = new RenderTexture(resolution, resolution, 0, format);

        //IMPORTANT FOR GPU SHADERS, allows random access (like gpus will do)
        tex.enableRandomWrite = true;
        tex.dimension = UnityEngine.Rendering.TextureDimension.Tex2D;        
        tex.filterMode = FilterMode.Bilinear;
        tex.wrapMode = TextureWrapMode.Clamp;
        tex.useMipMap = false;
        tex.Create();

        return tex;
    }

    void SwapTex(ref RenderTexture In, ref RenderTexture Out)
    {
        RenderTexture tmp = In;
        In = Out;
        Out = tmp;       
    }
}

计算着色器:

#pragma kernel StepKernel

float resolution;

Texture2D<float4> In;
SamplerState samplerIn;
RWTexture2D<float4> Out;

Texture2D<float4> DrawIn;
SamplerState samplerDrawIn;
RWTexture2D<float4> DrawOut;

float4 Sample(Texture2D<float4> t, SamplerState s, float2 coords) {
    return t.SampleLevel(samplerIn, coords / resolution, 0);
}

void Fluid(float2 coord, float2 offset, inout float2 velocity, inout float pressure, inout float divergence, inout float neighbors)
{
    // Sample buffer C, which samples B, which samples A, making our feedback loop    
    float4 s = Sample(In, samplerIn, coord + offset - Sample(In, samplerIn, coord + offset).xy);

    // gradient of pressure from the neighboring cell to ours
    float sampledPressure = s.w;    

    //add the velocity scaled by the pressure that its exerting
    velocity += offset * sampledPressure;

    // add pressure
    pressure += sampledPressure;

    // divergence of velocity
    divergence += dot(offset, s.xy);

    //increase number of neighbors sampled
    neighbors++;
}

float4 StepVelocity(float2 id) {

    //sample from the previous state    
    float4 values = Sample(In, samplerIn, id - Sample(In, samplerIn, id).xy);
    
    float2 velocity = float2(0, 0);
    float divergence = 0.;
    float pressure = 0., neighbors = 0.;

    Fluid(id.xy, float2( 0., 1.), velocity, pressure, divergence, neighbors);
    Fluid(id.xy, float2( 0.,-1.), velocity, pressure, divergence, neighbors);
    Fluid(id.xy, float2( 1., 0.), velocity, pressure, divergence, neighbors);
    Fluid(id.xy, float2(-1., 0.), velocity, pressure, divergence, neighbors);

    //average the samples
    velocity /= neighbors;
    divergence /= neighbors;
    pressure /= neighbors;

    //output pressure in w, velocity in xy
    values.w = pressure - divergence;
    values.xy -= velocity;
    
    float2 p1 = float2(.47, .2);
    if (length(id.xy - resolution * p1) < 10.) {
        values.xy = float2(0, .5);
    }
    float2 p2 = float2(.53, .8);
    if (length(id.xy - resolution * p2) < 10.) {
        values.xy = float2(0, -.5);
    }
    return values;
}

float4 StepFluid(float2 id) {

    for (int i = 0; i < 4; i++)
        id -= Sample(In, samplerIn, id).xy;

    float4 color = Sample(DrawIn, samplerDrawIn, id);

    float2 p1 = float2(.47, .2);
    if (length(id.xy - resolution * p1) < 10.) {
        color = float4(0, 1, 0, 1);
    }
    float2 p2 = float2(.53, .8);
    if (length(id.xy - resolution * p2) < 10.) {
        color = float4(1, 0, 0, 1);
    }
    color *= .999;
    return color;
}

[numthreads(8, 8, 1)]
void StepKernel (uint3 id : SV_DispatchThreadID)
{     
    float2 coord = float2(id.x + .5, id.y + .5);
    Out[id.xy] = StepVelocity(coord);
    DrawOut[id.xy] = StepFluid(coord);
}

我想提一件事,您可以HLSL通过单击analyzeShaderToys编辑器窗口底部按钮来查看ShaderToy着色器的翻译代码我不知道它的存在,但是在尝试将着色器从ShaderToy转换为Unity时,它可能非常有用。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章