
// A shader for deep fog particles that sample a copy of the depth buffer to eliminate sharp edges from intersecting geometry.
// This shader need both a diffuse texture and a noise image in the range -1 to +1.
// The depth is the length of the object's X axis divided by 2.
// The target draw surface must have an extra depth buffer for this to work.

/*
Pseudo code for generating a noise texture:
	// This shader only allow the noise texture to have the dimensions 128 x 128.
	Noise = DGE.DrawSurface_CreateFixed(128, 128, 0, False)
	X <- [0..127] {
	    Y <- [0..127] {
	        DGE.DrawSurface_SetPixelColor(Noise, X, Y, Random[-1..+1], Random[-1..+1], Random[-1..+1], Random[-1..+1])
	    }
	}
	// Integer rounding and gamma correcting image formats would create extreme artifacts.
	InsertStringToEngine "NoiseRGBA.DDS"
	DGE.DrawSurface_SaveToFile_InSB Noise
*/

// You may modify this to move more data from the vertex shader to the pixel shader.
// It needs at least the position to work.
struct VS_to_PS {
	float4 Pos : SV_POSITION;
	float4 Tex : TEXCOORD0;
	float4 Color : COLOR;
	float4 Pos_WorldSpace : TEXCOORD1;
	float4 Pos_CameraSpace : TEXCOORD2;
};

//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
VS_to_PS VS( VS_structure input ) {
	VS_to_PS output = (VS_to_PS)0;
	
	// Convert vertice positions from object space to world space
	output.Pos_WorldSpace = mul( input.Pos, ObjectToWorld );
	// Convert vertice positions from world space to camera space
	output.Pos_CameraSpace = mul( output.Pos_WorldSpace, WorldToCamera );
	
	// Convert vertice positions from camera space to 2D projection
	output.Pos = mul( output.Pos_CameraSpace, CameraToImage );
	
	// Give the texture coordinates to the pixel shader
	output.Tex = input.Tex;
	
	// Give the vertex color multiplied with the instance color to the pixel shader
	output.Color = lerp(input.Color * InstanceColor, float4(0.0f,0.0f,1.0f,1.0f), input.Selected);
	
	return output;
}

//tex_0 is the diffuse texture
//tex_1 is the light offset texture

//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 PS( VS_to_PS input) : SV_Target {
	// If the camera has a world space cutting plane, clip on the negative side.
	UseCuttingPlane(input.Pos_WorldSpace)
	
	// Sample the diffuse map and opacity
	float4 finalColor = tex_0.Sample( samAnisotropicMipmap, input.Tex.xy ) * input.Color;
	
	// Sample a noise texture in screen space for randomizing the light's sampling depth
	float4 lightOffset = tex_1.Sample( samSharp, (input.Pos.xy / 128.0f) );
	
	float3 DepthDirection = normalize(input.Pos_WorldSpace - CameraPos_WorldSpace);
	
	float3 UndirectedLightR;
	float3 UndirectedLightG;
	float3 UndirectedLightB;
	float3 UndirectedLightA;
	
	// sample global light at 4 random depths
	Engine_GetUndirectedLight(input.Pos_WorldSpace + DepthDirection * ((lightOffset.r * 0.25f) - 0.75f), UndirectedLightR);
	Engine_GetUndirectedLight(input.Pos_WorldSpace + DepthDirection * ((lightOffset.g * 0.25f) - 0.25f), UndirectedLightG);
	Engine_GetUndirectedLight(input.Pos_WorldSpace + DepthDirection * ((lightOffset.b * 0.25f) + 0.25f), UndirectedLightB);
	Engine_GetUndirectedLight(input.Pos_WorldSpace + DepthDirection * ((lightOffset.a * 0.25f) + 0.75f), UndirectedLightA);
	
	// Take the average
	float3 LightColor = ((UndirectedLightR + UndirectedLightG + UndirectedLightB + UndirectedLightA) * 0.25f);
	
	// Sample a copy of the depth map and convert from ZW to linear depth using the current camera's projection matrix
	float DepthBufferLinear = CameraToImage._43 / (tex_depthBufferCopy.Sample( samConstant, input.Pos.xy / Dimensions ) - CameraToImage._33);
	
	// Use the length of the object's X axis divided by 2 as the thickness
	float Thickness = length(float3(ObjectToWorld._11,ObjectToWorld._12,ObjectToWorld._13)) / 4.0f;
	
	// Fade alpha when the pixel is close to the background
	float CloseToBackgroundFading = saturate((DepthBufferLinear - input.Pos_CameraSpace.z) / Thickness);
	
	// Fade alpha when the pixel is close to the camera
	float CloseToCameraFading = saturate((input.Pos_CameraSpace.z - NearClipPlane) / Thickness);
	
	// Combine the result of the light samples and the fading and multiply the color with it
	finalColor = finalColor * float4(LightColor, CloseToCameraFading * CloseToBackgroundFading);
	
	// Show the final color with fog on R,G,B channels
	return float4((lerp(finalColor,FogColor,saturate(length(input.Pos_CameraSpace) / FogDistance) * MaxFogIntensity)).xyz,finalColor.w);
}
