I know how to reconstruct a world-space position from a log-depth value from a previous topic, so I tried several different ways to get it to work for the soft particles.
For reference, this is the equation for the reconstruction:
float depthVal = DepthMap.Sample(depthSampler, texCoord).r;
depthVal = (pow(0.001 * FarPlane + 1, depthVal) - 1) / 0.001;
depthVal /= (1 * FarPlane) / (FarPlane - 1); //The 1 is the near plane value
float2 invProjPos = mul(input.ScreenPosition.xy * depthVal, InverseProjection);
float4 position = mul(float4(invProjPos, -depthVal, 1), InverseView);
position /= position.w;
So I figured to get the projection-space value, the equation should be just the first 3 lines. Next, I figured the particle's Z value should be calculated like this (from the vertex shader):float4 worldPosition = mul(float4(input.Position, 1), instanceTransform);
float4 viewPosition = mul(worldPosition, View);
output.Position = mul(viewPosition, Projection);
output.Position.z = log(0.001 * output.Position.z + 1) / log(0.001 * NearFar.y + 1) * output.Position.w;
output.Z = output.Position.z;
Now I know the value output to the depth buffer will be divided by the W normally, so I also tried changing the last line to:output.Z = output.Position.z / output.Position.w;
In either case, the result is the same: no fading what-so-ever. The particles still have their hard edges. I'm not really sure what I'm doing wrong here and was hoping someone could point it out.
Full shader code:
[spoiler]
const float SoftParticleContrast = 2.0;
const float SoftParticleScale = 1;
const float zEpsilon = 0.0;
float4x4 View;
float4x4 Projection;
float4 NearFar; //x = near, y = far, z = far * near, w = far - near
Texture2D Texture;
Texture2D<float> Depth;
SamplerState Sampler
{
Filter = MIN_MAG_MIP_LINEAR;
};
struct VertexShaderInput
{
float3 Position : POSITION0;
float2 TextureCoordinate : TEXCOORD0;
};
struct VertexShaderOutput
{
float4 Position : SV_POSITION;
float4 Color : COLOR0;
float2 TextureCoordinate : TEXCOORD0;
float Z : TEXCOORD1;
};
struct PixelShaderOutput
{
float4 Color : SV_TARGET0;
};
float Contrast(float Input, float ContrastPower)
{
//piecewise contrast function
bool IsAboveHalf = Input > 0.5 ;
float ToRaise = saturate(2*(IsAboveHalf ? 1-Input : Input));
float Output = 0.5*pow(ToRaise, ContrastPower);
Output = IsAboveHalf ? 1-Output : Output;
return Output;
}
// Vertex shader helper function shared between the two techniques.
VertexShaderOutput VertexShaderCommon(VertexShaderInput input, float4x4 instanceTransform, float4 color, float4 rect)
{
VertexShaderOutput output;
// Apply the world and camera matrices to compute the output position.
float4 worldPosition = mul(float4(input.Position, 1), instanceTransform);
float4 viewPosition = mul(worldPosition, View);
output.Position = mul(viewPosition, Projection);
output.Position.z = log(0.001 * output.Position.z + 1) / log(0.001 * NearFar.y + 1) * output.Position.w;
//output.Position.z = log(output.Position.z / 0.001) / log(FarPlane / 0.001) * output.Position.w;
output.TextureCoordinate = input.TextureCoordinate * rect.zw + rect.xy;
output.Color = color;
output.Z = output.Position.z / output.Position.w;
return output;
}
// Hardware instancing reads the per-instance world transform from a secondary vertex stream.
VertexShaderOutput HardwareInstancingVertexShader(VertexShaderInput input, float4x4 instanceTransform : BLENDWEIGHT, float4 color : COLOR, float4 rect : RECT)
{
return VertexShaderCommon(input, transpose(instanceTransform), color, rect);
}
PixelShaderOutput PixelShaderFunction(VertexShaderOutput input)
{
PixelShaderOutput output;
clip(input.Color.a <= 0 ? -1 : 1);
float depthVal = Depth.Load(int4(input.Position.xy, 0, 0));
depthVal = (pow(0.001 * NearFar.y + 1, depthVal) - 1) / 0.001;
depthVal /= NearFar.z / NearFar.w;
float zdiff = (depthVal - input.Z);
float c = Contrast(zdiff * SoftParticleScale, SoftParticleContrast);
if( c * zdiff <= zEpsilon )
{
discard;
}
//output.Color = float4(depthVal * 100, 0, 0, 1);
output.Color = Texture.Sample(Sampler, input.TextureCoordinate) * input.Color * c;
return output;
}
technique11 HardwareInstancing
{
pass Pass1
{
SetVertexShader(CompileShader(vs_4_0, HardwareInstancingVertexShader()));
SetPixelShader(CompileShader(ps_4_0, PixelShaderFunction()));
}
}
[/spoiler]