我正在尝试在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
通过单击analyze
ShaderToys编辑器窗口底部的按钮来查看ShaderToy着色器的翻译代码。我不知道它的存在,但是在尝试将着色器从ShaderToy转换为Unity时,它可能非常有用。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句