Jump to content
  • Advertisement
Sign in to follow this  
simotix

Signed distance field rendering (Blending question)

This topic is 2770 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have been working on implementing the Valve technique for rendering decals, although I am doing it for text. I have seen some workings on it while searching this forum, but I am confused about how it should be rendered.

For testing, I am rendering my image on quad (although I will split it up and batch the letters later). It is just a quad with a Vertex/Texture Coordinate signature. I am confused about two things for how it should be rendered.

1) Blending: From what I read, it should enable blending, and have D3D11_BLEND_SRC_ALPHA with D3D11_BLEND_INV_SRC_ALPHA. Although, I am not exactly aure. This is how I generate my blend state, is this the correct blend state?


D3D11_BLEND_DESC blendDesc;
blendDesc.AlphaToCoverageEnable = false;
blendDesc.IndependentBlendEnable = false;
for (UINT i = 0; i < 8; ++i)
{
blendDesc.RenderTarget.BlendEnable = true;
blendDesc.RenderTarget.BlendOp = D3D11_BLEND_OP_ADD;
blendDesc.RenderTarget.BlendOpAlpha = D3D11_BLEND_OP_ADD;
blendDesc.RenderTarget.DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
blendDesc.RenderTarget.DestBlendAlpha = D3D11_BLEND_ONE;
blendDesc.RenderTarget.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
blendDesc.RenderTarget.SrcBlend = D3D11_BLEND_SRC_ALPHA;
blendDesc.RenderTarget.SrcBlendAlpha = D3D11_BLEND_ONE;
}
HRESULT hResult = m_pD3D11Device->CreateBlendState(&blendDesc, &m_pAlphaBlendState);



2) Texture Filtering. I create a basic Linear, with Wrapping sampler state. Is this would I should be creating?


ID3D11SamplerState *pSamplerLinear;
D3D11_SAMPLER_DESC sampDesc;
ZeroMemory( &sampDesc, sizeof(sampDesc) );
sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
sampDesc.MinLOD = 0;
sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
HRESULT hResult = m_pD3D11Device->CreateSamplerState( &sampDesc, &pSamplerLinear );

Share this post


Link to post
Share on other sites
Advertisement
You don't blend, you alpha test. So in D3D11 that means doing clip() or discard the pixel shader.

Share this post


Link to post
Share on other sites
I suppose that I read something wrong, I was under the impression that I needed to blend.

I took your advice and I am now using clip(), is there any sort of state that I should be setting? Currently all I do is the following in the pixel shader.


float4 color = txDiffuse.Sample(samLinear, input.Tex);
clip(color.a - 0.5);
return color;



Also, I suppose my linear sampler state is fine?

Share this post


Link to post
Share on other sites
Unfortunately the text I am trying to render really does not look great. I am rendering a decal that I created using signed distance fields at several distances and anything with a curved surface does not look very good. I was wondering if anyone has ever experienced this? I am still unsure of my texture filtering is correct, so maybe that is the problem.

This is when I created it with a block size of 32. I tried 16 and 64, but I will still get bad curves (or even worse).

Share this post


Link to post
Share on other sites
For hard (aliased) edged objects, you can use clip.

If you want soft edges, then turn on blending like you were originally... but you've got to calculate a sensible alpha value in your shader. Something like:
color.a = smoothstep(0.48,0.52,color.a);

Share this post


Link to post
Share on other sites
Quote:
Original post by Hodgman
If you want soft edges, then turn on blending like you were originally... but you've got to calculate a sensible alpha value in your shader. Something like:
color.a = smoothstep(0.48,0.52,color.a);


That did improve the look of the letters a lot, but they still look very rough at a distance and letters like X look very jagged. Do you (or anyone else) have any suggestions to what I should do? I tried to increase my scan size (it was at 100, I tried it at 200) but that did not improve anything.

Share this post


Link to post
Share on other sites
The code used to generate the distance fields was done in C#, it will save a .png then I will render that .png in DirectX11.

Create signed distance bitmap

public static Bitmap CreateSignedDistanceBitmap(Bitmap bitmap, int scanSizeWidth, int scanSizeHeight, int blockSize)
{
int textureOutWidth = bitmap.Width / blockSize;
int textureOutHeight = bitmap.Height / blockSize;

Bitmap signedDistanceBitmap = new Bitmap(textureOutWidth, textureOutHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

int blockWidth = blockSize;
int blockHeight = blockSize;

float[,] signedDistances = new float[signedDistanceBitmap.Width, signedDistanceBitmap.Height];

float max = 0, min = 0;
for (int x = 0; x < signedDistanceBitmap.Width; ++x)
{
for (int y = 0; y < signedDistanceBitmap.Height; ++y)
{
float signedDistance = CalculateSignedDistance(bitmap,
(x * blockWidth) + (blockWidth / 2),
(y * blockHeight) + (blockHeight / 2),
scanSizeWidth, scanSizeHeight);

signedDistances[x, y] = signedDistance;
if (signedDistance != float.MaxValue && signedDistance > max)
max = signedDistance;
else if (signedDistance != float.MinValue && signedDistance < min)
min = signedDistance;
}

}

float scale = Math.Max(Math.Abs(min), Math.Abs(max));
for (int x = 0; x < textureOutWidth; ++x)
{
for (int y = 0; y < textureOutHeight; ++y)
{
float signedDistance = signedDistances[x, y];

if (signedDistance == float.MaxValue)
{
signedDistance = 1.0f;
}
else if (signedDistance == float.MinValue)
{
signedDistance = 0.0f;
}
else
{
signedDistance /= scale;
signedDistance /= 2;
signedDistance += 0.5f;
}

signedDistances[x, y] = signedDistance;

// Set the signed distance into the alpha channel
signedDistanceBitmap.SetPixel(x, y, Color.FromArgb((int)Math.Round(signedDistance * 255), 255, 255, 255));
}
}

return signedDistanceBitmap;
}



Calculate signed distance

public static float CalculateSignedDistance(Bitmap image, int imageX, int imageY, int scanWidth, int scanHeight)
{
Color baseColor = image.GetPixel(imageX, imageY);

// Check to see if it is a solid color, if it is a solid color then that means there is no texture on the texel
bool solidColor = baseColor.R > 0;

float closestDistance = float.MaxValue;
bool closestValid = false;

int startX = imageX - (scanWidth / 2);
int endX = startX + scanWidth;
int startY = imageY - (scanHeight / 2);
int endY = startY + scanHeight;

// No need in searching past the images bounds
if (startX < 0)
startX = 0;

if (endX >= image.Width)
endX = image.Width;

if (startY < 0)
startY = 0;

if (endY >= image.Height)
endY = image.Height;

for (int x = startX; x < endX; ++x)
{
for (int y = startY; y < endY; ++y)
{
Color texelColor = image.GetPixel(x, y);

if (solidColor)
{
if (texelColor.R == 0)
{
float dist = Separation(imageX, imageY, x, y);
if (dist < closestDistance)
{
closestDistance = dist;
closestValid = true;
}
}
}
else
{
if (texelColor.R > 0)
{
float dist = Separation(imageX, imageY, x, y);
if (dist < closestDistance)
{
closestDistance = dist;
closestValid = true;
}
}
}
}
}

if (solidColor)
{
if (closestValid)
return closestDistance;
else
return float.MaxValue;
}
else
{
if (closestValid)
return -closestDistance;
else
return float.MinValue;
}
}



Seperation

private static float Separation(float startX, float startY, float endX, float endY)
{
float x = startX - endX;
float y = startY - endY;

return (float)Math.Sqrt(x * x + y * y);
}

Share this post


Link to post
Share on other sites
Your blockSize stuff looks a bit suspect - I'd say that's whats causing the loss of detail from your high-res inputs. Did you get this concept of calculating the field in blocks from somewhere, or was it an optimisation you came up with?

In my tool, I generate the distance field with a blockSize of 1 (i.e. the distance field is calculated at high-resolution).
Then after you've got the high-res distance field, you shrink the image using a bilinear/box filter.

Share this post


Link to post
Share on other sites
I recall reading about the block size from a source that I can't seem to find at the moment, I did not think of that optimization my self.

How did you learn to create the distance fields? Currently with my method, if I run a block size of 1 (with a scan size of 200) it will seemingly never finish. I have had it running for six hours now ...

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!