我改变了几件事来完成这项工作:
首先,像素坐标需要偏移 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;
}
}
计算着色器:
#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);
}
我还想提一件事,您可以通过单击ShaderToys 编辑器窗口底部HLSL
的按钮来查看您的 ShaderToy 着色器的翻译代码。analyze
我不知道这存在,但在尝试将着色器从 ShaderToy 转换为 Unity 时它会非常有用。