Tur*_*olt 7 shader unity-game-engine compute-shader fluid-dynamics
我正在尝试在 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;
}
Run Code Online (Sandbox Code Playgroud)
它应该非常简单,我尽力过多地评论shadertoy以使其易于理解(它们是相同的代码,为不同的环境设置)。这是驱动模拟的 C# 代码。
我知道要求某人深入研究我的代码是一个不方便的请求,但我完全不确定我做错了什么,这让我发疯。我在 shadertoy 上使用了完全相同的算法,但它在 Unity 计算着色器中的表现非常奇怪,我不知道为什么。每次我“跨步”时,我都会确保切换读/写纹理,以便在模拟中干净利落地前进而不干扰。
任何解决我的问题的想法/技巧将不胜感激。
为了使这项工作顺利进行,我做了一些更改:
首先,像素坐标需要偏移0.5。在计算着色器中,坐标是整数,但在 ShaderToy 中,传入的坐标来自SV_Position输入,该输入已经应用了该偏移量。
SV_Position 描述像素位置。在所有着色器中可用,以获得偏移量为 0.5 的像素中心。
对于 也是如此gl_FragCoord。默认情况下偏移 0.5。当您向位置添加速度时,这一点很重要。如果没有偏移,您将不会在所有方向上具有相同的行为。
其次,我使用采样器作为输入,点过滤似乎不适用于此。除此之外,代码基本上是相同的,并且应该相当容易理解。
它看起来是这样的:
流体模拟.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;
}
}
Run Code Online (Sandbox Code Playgroud)
计算着色器:
#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);
}
Run Code Online (Sandbox Code Playgroud)
我还想提一件事,您可以通过单击ShaderToys 编辑器窗口底部的按钮来查看HLSLShaderToy 着色器的翻译代码。analyze我不知道它的存在,但在尝试将着色器从 ShaderToy 转换为 Unity 时它非常有用。
| 归档时间: |
|
| 查看次数: |
420 次 |
| 最近记录: |