Open Borders in Fluid / Smoke Simulation

Started by
3 comments, last by HellGate 8 years, 6 months ago

first im sorry if this is the wrong section but i decided to post it here because its mainly about the math inside the shader.

I am trying to adopt a fluid / smoke simulation based of the source files of this video to my needs but came across a few problems when trying to make it open at the borders (flows into the void).

What i tryed:

removing the "SetBorders" passes did mostly work for density and velocity however not for pressure and divergence (still bounces back at the borders). this also causes density and velocity values to get stuck at the borders (since the sampler is set to clamp the neighbor values outside bounds return the same value as the current cell). adding a 1 texel border that sets velocity and density to 0 fixed the values getting stuck problem but setting the velocity to 0 gave it the same border effect as before.

the hlsl code without the project:


/*
    Fluid.fx - An efficient HLSL fluid shader
    Copyright (C) 2013 Michael Stone (Neoaikon)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

// Samplers, from DrawFluid
sampler2D buffer : register(s0);
sampler2D Velocity : register(s1);
sampler2D Density : register(s2);
sampler2D Divergence : register(s3);
sampler2D Pressure : register(s4);
sampler2D VelocitySources : register(s5);
sampler2D DensitySources : register(s6);

// Dimensions of the textures making up the fluid stages
float FluidSize = 256.0f;
float dT = 1.0f; // Delta time
float VelocityDiffusion = 1.0f;
float DensityDiffusion = 1.0f;
float4 VelocityColor;

// Struct for when we use two render targets
struct DoubleOutput
{
	float4 Vel : COLOR0;
	float4 Den : COLOR1;
};

// Bilerps between the 4 closest texels
float4 QuadLerp(sampler2D samp, float2 s)
{
  float x0 = floor(s.x*FluidSize);
  float x2 = x0 + 1.f;
  float y0 = floor(s.y*FluidSize);
  float y1 = y0 + 1.f;
  
  float4 tex12 = tex2D(samp, float2(x0/FluidSize, y1/FluidSize)); 
  float4 tex22 = tex2D(samp, float2(x2/FluidSize, y1/FluidSize));   
  float4 tex11 = tex2D(samp, float2(x0/FluidSize, y0/FluidSize)); 
  float4 tex21 = tex2D(samp, float2(x2/FluidSize, y0/FluidSize)); 
  
  float fx = ((s.x*FluidSize) - x0);
  float fy = ((s.y*FluidSize) - y0);

  float4 l1 = lerp(tex11, tex21, fx);
  float4 l2 = lerp(tex12, tex22, fx);

  return lerp(l1, l2, fy);
}

// Sets the edges of the texture, clips everything else
float2 SetBounds(float2 v)
{
	float2 pos = v;
	float Offset = (1.0f/(FluidSize));
	float HalfPixel = (.5f/FluidSize);
	if(pos.x <  HalfPixel)
        pos.x += Offset;
    else if(pos.y <  HalfPixel)
        pos.y += Offset;
    else if(pos.x >=  (1.0f-HalfPixel)-Offset)
        pos.x -= Offset;
    else if(pos.y >=  (1.0f-HalfPixel)-Offset)
		pos.y -= Offset;		
	else
		clip(-1);
	return pos;
}

float4 PSVelocityColorize(float2 TexCoords : TEXCOORD0) : COLOR0
{
	return (tex2D(buffer, TexCoords) * -VelocityColor) * FluidSize;
}

// Adds the sources to Density and Velocity
DoubleOutput PSAddSources(float2 TexCoords : TEXCOORD0)
{
	DoubleOutput Output;	
	float4 uv = tex2D(VelocitySources, TexCoords - float2(.5f/FluidSize, .5f/FluidSize));	
	Output.Vel = max(-.8f, min(.8f, tex2D(Velocity, TexCoords - float2(.5f/FluidSize, .5f/FluidSize)) + uv));
	Output.Den = tex2D(Density, TexCoords - float2(.5f/FluidSize, .5f/FluidSize)) + (tex2D(DensitySources, TexCoords - float2(.5f/FluidSize, .5f/FluidSize))/8.0f);	
	Output.Vel.w = 1.0f;
	Output.Den.w = 1.0f;
	return Output;
}

// Sets the boundaries for Density and Velocity
DoubleOutput PSSetBoundsDouble(float2 TexCoords : TEXCOORD0)
{
	DoubleOutput Output;
	float2 Pos = TexCoords - float2(.5f/FluidSize, .5f/FluidSize);
    Pos = SetBounds(Pos);
	Output.Vel = -tex2D(Velocity, Pos);
	Output.Den = tex2D(Density, Pos);
	return Output;
}

// Sets the boundries for Pressure
float4 PSSetBoundsSingle(float2 TexCoords : TEXCOORD0) : COLOR0
{	
	float2 Pos = TexCoords - float2(.5f/FluidSize, .5f/FluidSize);
    Pos = SetBounds(Pos);
	return tex2D(buffer, Pos);	
}

// Advects Velocity and Density
DoubleOutput PSAdvection(float2 TexCoords : TEXCOORD0)
{
	DoubleOutput Output;
	float2 Pos = TexCoords - float2(.5f/FluidSize, .5f/FluidSize);	
    Pos -= tex2D(Velocity, Pos) / FluidSize;	
	Output.Vel = VelocityDiffusion*QuadLerp(Velocity, Pos);
	Output.Den = DensityDiffusion*QuadLerp(Density, Pos);    
    return Output;
}

// Calculates and spits out a divergence texture
float4 PSDivergence(float2 TexCoords : TEXCOORD0) : COLOR0
{
	float2 Pos = TexCoords - float2(.5f/FluidSize, .5f/FluidSize);
	float Offset = 1.0f/FluidSize;	
	float4 left   = tex2D(buffer, float2(Pos.x - Offset, Pos.y));
	float4 right  = tex2D(buffer, float2(Pos.x + Offset, Pos.y));
	float4 top    = tex2D(buffer, float2(Pos.x, Pos.y - Offset));
	float4 bottom = tex2D(buffer, float2(Pos.x, Pos.y + Offset));
	return .5f * ((right.x - left.x) + (bottom.y - top.y));
}

// Calculates and spits out the pressure texture over a series of iterations
float4 PSJacobi(float2 TexCoords : TEXCOORD0) : COLOR0
{
	float2 Pos = TexCoords - float2(.5f/FluidSize, .5f/FluidSize);
	float Offset = 1.0f/FluidSize;	
	float4 center = tex2D(Divergence, Pos);
	float4 left   = tex2D(buffer, float2(Pos.x - Offset, Pos.y));
	float4 right  = tex2D(buffer, float2(Pos.x + Offset, Pos.y));
	float4 top    = tex2D(buffer, float2(Pos.x, Pos.y - Offset));
	float4 bottom = tex2D(buffer, float2(Pos.x, Pos.y + Offset));
	return ((left + right + bottom + top) - center) * .25f;
}

// Subtracts the pressure texture from the velocity texture
float4 PSSubtract(float2 TexCoords : TEXCOORD0) : COLOR0
{
	float2 Pos = TexCoords - float2(.5f/FluidSize, .5f/FluidSize);	
	float Offset = 1.0f/FluidSize;
	float left   = tex2D(Pressure, float2(Pos.x - Offset, Pos.y)).x;
	float right  = tex2D(Pressure, float2(Pos.x + Offset, Pos.y)).x;
	float top    = tex2D(Pressure, float2(Pos.x, Pos.y - Offset)).y;
	float bottom = tex2D(Pressure, float2(Pos.x, Pos.y + Offset)).y;

	float2 grad = float2(right-left, bottom-top) * .5f;
	float4 v = tex2D(buffer, Pos);
	v.xy -= grad;
	return v;
}

float4 PSFinal(float2 TexCoords : TEXCOORD0) : COLOR0
{
	return QuadLerp(buffer, TexCoords - float2(.5f/FluidSize, .5f/FluidSize));
}

// Techniques and passes
Technique VelocityColorize
{
	pass VelocityColorize
	{
		PixelShader = compile ps_2_0 PSVelocityColorize();
	}
}

technique DoAddSources
{
	pass DoAddSources
	{
		PixelShader = compile ps_2_0 PSAddSources();
	}
	pass SetBounds
	{
		PixelShader = compile ps_2_0 PSSetBoundsDouble();
	}
}

technique DoAdvection
{
	pass DoAdvection
	{
		PixelShader = compile ps_2_0 PSAdvection();
	}
	pass SetBounds
	{
		PixelShader = compile ps_2_0 PSSetBoundsDouble();
	}
}

technique DoDivergence
{
	pass DoDivergence
	{
		PixelShader = compile ps_2_0 PSDivergence();
	}
}

Technique DoJacobi
{
	pass DoJacobi
	{
		PixelShader = compile ps_2_0 PSJacobi();
	}
	pass SetBounds
	{
		PixelShader = compile ps_2_0 PSSetBoundsSingle();
	}
}

Technique Subtract
{
	pass Subtract
	{
		PixelShader = compile ps_2_0 PSSubtract();
	}
}

Technique Final
{
	pass Final
	{
		PixelShader = compile ps_2_0 PSFinal();
	}
}

thanks for any help

Advertisement
It is bounded because all the computation happens inside a finite texture space. You cannot run the simulation outside bounds of the texture.
My current game project Platform RPG

It is bounded because all the computation happens inside a finite texture space. You cannot run the simulation outside bounds of the texture.

yes im not trying to continue the simulation outside the bounds but instead have borders that flow into the void or have a "eraser" border to prevent echo's of preassure and others

quick example in blender:

what i have and what i try to get


// Sets the edges of the texture, clips everything else
float2 SetBounds(float2 v)
{
float2 pos = v;
float Offset = (1.0f/(FluidSize));
float HalfPixel = (.5f/FluidSize);
if(pos.x < HalfPixel)
pos.x += Offset;
else if(pos.y < HalfPixel)
pos.y += Offset;
else if(pos.x >= (1.0f-HalfPixel)-Offset)
pos.x -= Offset;
else if(pos.y >= (1.0f-HalfPixel)-Offset)
pos.y -= Offset;
else
clip(-1);
return pos;
}

This is your boundary function, which determines how the simulation behaves. You can have any kind of boundary conditions, such as a bunch of planes (which are the case here), a texture lookup, or whatnot. By eliminating the conditions you don't want or adding ones you need, you can make the simulation adhere to whatever topography you want. For starters try clearing the entire boundary function:


float2 SetBounds(float2 v)
{
clip(-1);
return v;
}

This will remove all four edge boundaries. Next have a closer look at what you just removed and add back whatever you need

Mind you that I'm not really sure what function the call to clip(-1) has in this case.


// Sets the edges of the texture, clips everything else
float2 SetBounds(float2 v)
{
float2 pos = v;
float Offset = (1.0f/(FluidSize));
float HalfPixel = (.5f/FluidSize);
if(pos.x < HalfPixel)
pos.x += Offset;
else if(pos.y < HalfPixel)
pos.y += Offset;
else if(pos.x >= (1.0f-HalfPixel)-Offset)
pos.x -= Offset;
else if(pos.y >= (1.0f-HalfPixel)-Offset)
pos.y -= Offset;
else
clip(-1);
return pos;
}

This is your boundary function, which determines how the simulation behaves. You can have any kind of boundary conditions, such as a bunch of planes (which are the case here), a texture lookup, or whatnot. By eliminating the conditions you don't want or adding ones you need, you can make the simulation adhere to whatever topography you want. For starters try clearing the entire boundary function:


float2 SetBounds(float2 v)
{
clip(-1);
return v;
}

This will remove all four edge boundaries. Next have a closer look at what you just removed and add back whatever you need

Mind you that I'm not really sure what function the call to clip(-1) has in this case.

i already have figured that out but it doesnt seems to work (it calls the SetBounds function)

removing the "SetBorders" passes did mostly work for density and velocity however not for pressure and divergence (still bounces back at the borders).

meaning this 2 dont care about this function at all

and since divergence affects preassure and preassure affects velocity its still noticable

clip(-1) is just an ugly discard, meaning it will only process for the texels from setBounds function (the 1 texel border) so this is something that i need to remove for sure but its not all and i cant find anything else

This topic is closed to new replies.

Advertisement