2D Raymarching Smoke

2d raymarching smoke. Based on uncharted4’s siggraph 2015 presentation =)
similar to vimeo.com/171238992
You can adjust march steps, add fire like explosion and adjust fire&smoke color
This is for udk. UE4 version is going. But there are little difference between udk transmission and ue4 subsurface(especially in translucency, masked looks right but… alpha is masked)
I prefer udk’s look. So I’m studying UE4 to make same look as udk.

12 Likes

@mattorialist Your work is spreading :wink: I’m so happy to see this solution being used more and more!

1 Like

I like his work & presentations =)

Good job man! Looking hot!

1 Like

Thanks for your comment & siggraph ppts!

Hi Matt, I’m a french follower of your work : ) Is there a way to see your presentations, or to learn how you made this ?

Thx

Here you go

https://drive.google.com/file/d/0B0nL9LedRBbDakFPd0sxd0doNlU/view

3 Likes

hope it helps!

im really happy people are using this!

3 Likes

thx for sharing, !!! I’m gonna reading this thoroughly

@mattorialist Was this some custom code implemented in the shading somehow? I understand the basic concept of ray marching, but wonder how it can be actually implemented

He has the code in his presentation. Someone posted an unreal test scene in the facebook group that had this exact implementation:

https://drive.google.com/file/d/0B2e7e-78RB_-SzdWQlJuNFJ4Rms/view

You can download that and play around with it to see how it’s implemented.

1 Like

Hello guys, Mederic here, TD VFX on Skull & Bones.

@mattorialist, @RyanB @Vich

I’ve been continuing to push on your work with this as a solution for my VFX pipeline. Bear with me this will be a bit of a long post. Here’s some of my findings.

Original code I started with from @RyanB

if(startZ<=threshold) return 0;
float TimeLerp = 1;
float DepthDiff = 0;
float LastDiff = -bias;

//scale Z by 2 since the heightmap represents two halves of as symmetrical volume texture, split along Z where texture = 0
float3 RayStepUVz = float3(TraceVec.x, TraceVec.y, TraceVec.z*2);

float accum = 0;
float3 RayUVz = float3(UV, (startZ - bias) );
int i = 0;
while (i < NumSteps)
{
    RayUVz += RayStepUVz;

    float SampleDepth = dot( channel, Tex.SampleLevel(TexSampler, RayUVz.xy,0) );
    DepthDiff = abs(RayUVz.z) - abs(SampleDepth);

    if (DepthDiff <= 0)
    {

        if(LastDiff > 0)
        {
            TimeLerp = saturate( LastDiff / (LastDiff - DepthDiff));
            accum += StepSize * (1-TimeLerp);
        }
        else
        {
            accum+=StepSize;
        }
    }
    else
    if(LastDiff <= 0)
    {
        TimeLerp = saturate( LastDiff / (LastDiff - DepthDiff));
        accum += StepSize * (TimeLerp);
    }

LastDiff = DepthDiff;

i++;
}

return accum;

First thing I started doing is I realized I could calculate the TimeLerp right after DepthDiff and before branching. Then the branching conditions were basically the opposite of each other so I decided to combine them to do just 1 check. That meant I needed to invert some additions to accum into subtractions which turned out to save a LOT of instruction in the loop after some math shuffles. Also, it meant I actually didn’t need to do any branching after all.

I added a jitter to mask the banding by doing only 10 max loop. So far I got it a lot speedier than what I started with @RyanB 's original code.


	float2 TimeLerp = float2(1.0f, 1.0f);
	// float2 DepthDiff = float2(0.0f, 0.0f);
	// float2 LastDiff = float2(-bias, -bias);
// new diff is just combining the above 2. 
	float4 diff = float4( 0.0f , 0.0f , -bias , -bias );

	//We scale Z by 2 since the heightmap represents two halves of as symmetrical volume texture, split along Z where texture = 0
	float3 RayStepUVz = float3(TraceVec.x, TraceVec.y, TraceVec.z*2);
	float2 accum = float2(0.0f, 0.0f);
	float3 RayUVz = float3(UV, startZ);
	jitterScale *= hash(float4(UV.xy+(float2)NumSteps, UV.yx*NumSteps));
	for(int i = 0; i < (int)NumSteps; i++)
	{
		RayUVz += RayStepUVz;
		RayUVz.xy = saturate(RayUVz.xy+jitterScale);
		float2 SampleDepth = SampleBias(tex, RayUVz.xy, 1).xy;
		diff.xy = (float2)abs(RayUVz.z) - SampleDepth;
		float4 DiffComposite = float4( diff < 0.001f );
		TimeLerp = saturate( diff.zw / (diff.zw - diff.xy));
		accum += StepSize * ( TimeLerp * ( DiffComposite.zw - DiffComposite.xy ) + DiffComposite.xy );
		diff.zw = diff.xy;	
	}

	return accum;

The hash function:

float hash(float4 p)  // replace this by something better
{
    p  = frac( p*0.3183099+(float4)0.1f );
	p *= 17.0;
    return frac( p.x*p.y*p.z*p.w*(p.x+p.y+p.z+p.w) );
}

Oh and yeah, as a side note, I do everything in float2 in the loop because I sample red and green channels on my texture to get the animated texture’s “frame ID n” on Red and “frame ID n+1” on green. Then after the raymarch function is returned in the main shader I just lerp between the x and y values based on the particle frame blend value.

If you are not doing this type of blending, then everything goes back to float and the diff and diffComposite float4 go into float2.

		/////////////////////////////////////////// Raymarching /////////////////////////////////////////////////////////////////
		float2 thickness1 = RaymarchShadow(vTex, startZ, UV0, NumSteps, stepSize, LightTraceVec.xyz, bias, jitterScale);
		
		// UV Anim Frame Blending for cost of 1 lerp, basically.
		float thickness = lerp(thickness1.x, thickness1.y, blend);
		beers = pow(2.718281828459f, (-1.0f*thickness*shadowDensity));

Note: If you keep your texture small / in a format that has less channels than RGBA (like IA8 or something) then you can have a bigger resolution texture, But if you want nice performances, you need to make sure your samples in loop are using the 64k texture cache. For me, using 4 channel texture in BC7, I can get away with 1k by 2k.

Currently the example below is just 1 particle. For reference, I am seeing 0.6ms cost for a total of 82,941 pixels drawn on screen on Xbox One.

Result:
https://drive.google.com/file/d/0B1VFiE1HRdtNMTlkd2ppOHhYX2M/view

5 Likes

Sorry I’m a bit slow. Nice screenshot! So your doing what here? 2d raymarching a heightmap-ey function?

Yes, but in much less instructions as compared to the original code. As well as putting in the “free” frame blending!

2 Likes