Skip to content

Instantly share code, notes, and snippets.

@aras-p
Created January 3, 2020 10:37
  • Star 51 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save aras-p/657a4947bb8a4549fda53a84bb91fb25 to your computer and use it in GitHub Desktop.
"Print a value" custom function node code for Unity ShaderGraph
// Quick try at doing a "print value" node for Unity ShaderGraph.
// Tested on Unity 2019.2.17 with ShaderGraph 6.9.2.
//
// Use with CustomFunction node, with two inputs:
// - Vector1 Value, the value to display,
// - Vector2 UV, the UVs of area to display at.
// And one output:
// - Vector4 Color, the color.
// Function name is DoDebug.
// "print in shader" based on this excellent ShaderToy by @P_Malin:
// https://www.shadertoy.com/view/4sBSWW
float DigitBin(const int x)
{
return x==0?480599.0:x==1?139810.0:x==2?476951.0:x==3?476999.0:x==4?350020.0:x==5?464711.0:x==6?464727.0:x==7?476228.0:x==8?481111.0:x==9?481095.0:0.0;
}
float PrintValue(float2 vStringCoords, float fValue, float fMaxDigits, float fDecimalPlaces)
{
if ((vStringCoords.y < 0.0) || (vStringCoords.y >= 1.0))
return 0.0;
bool bNeg = (fValue < 0.0);
fValue = abs(fValue);
float fLog10Value = log2(abs(fValue)) / log2(10.0);
float fBiggestIndex = max(floor(fLog10Value), 0.0);
float fDigitIndex = fMaxDigits - floor(vStringCoords.x);
float fCharBin = 0.0;
if (fDigitIndex > (-fDecimalPlaces - 1.01))
{
if(fDigitIndex > fBiggestIndex)
{
if((bNeg) && (fDigitIndex < (fBiggestIndex+1.5))) fCharBin = 1792.0;
}
else
{
if(fDigitIndex == -1.0)
{
if(fDecimalPlaces > 0.0) fCharBin = 2.0;
}
else
{
float fReducedRangeValue = fValue;
if(fDigitIndex < 0.0) { fReducedRangeValue = frac( fValue ); fDigitIndex += 1.0; }
float fDigitValue = (abs(fReducedRangeValue / (pow(10.0, fDigitIndex))));
fCharBin = DigitBin(int(floor(fmod(fDigitValue, 10.0))));
}
}
}
return floor(fmod((fCharBin / pow(2.0, floor(frac(vStringCoords.x) * 4.0) + (floor(vStringCoords.y * 5.0) * 4.0))), 2.0));
}
float PrintValue(const in float2 fragCoord, const in float2 vPixelCoords, const in float2 vFontSize, const in float fValue, const in float fMaxDigits, const in float fDecimalPlaces)
{
float2 vStringCharCoords = (fragCoord.xy - vPixelCoords) / vFontSize;
return PrintValue( vStringCharCoords, fValue, fMaxDigits, fDecimalPlaces );
}
void DoDebug_float(float val, float2 uv, out float4 res)
{
res = float4(0.3,0.2,0.1,1);
res = PrintValue(uv*200, float2(10,100), float2(8,15), val, 10, 3);
}
@jdrobert
Copy link

Thank you. This helped me a ton.

@bestknighter
Copy link

bestknighter commented Jan 29, 2022

Hello! This is amazing and I made some improvements on my fork.
Now it can properly handle +Inf, -Inf and NaN, showing exactly that for each of them.
It used to show 00000000000000.000 (technically infinite 0s) for +Inf and -Inf; and 0.000 for NaN.

@iamabigartist
Copy link

iamabigartist commented Feb 12, 2022

On my!!

@susiehwangart
Copy link

Thank you my lord

@bjornicus
Copy link

Thanks for this, it really helps debugging.

For anyone new to custom nodes in shadergraph, you do have to use this with type=file. You can't just use type=sting and paste it because when you use string it tries to generate a function using the contents of the string (which in this case already contains functions).

@Arlorean
Copy link

Just trying to display the number 0.5 as a constant but I can't seem to get it to work, it just shows a white square or a black square depending on the UV input (Unity 2021.2.9f1, also tried 2022.1.20f1). Am I missing something obvious? I downloaded the raw DebugNode.hlsl and put it at the top level of a new/empty 3D URP project, then created a new Unlit URP Shader Graph:
image

image

@bestknighter
Copy link

@Arlorean When you have [0.5, 0.5] as the UV input, the entire output will assume the color of the pixel at the dead center. Its input needs to be the output of UV node. You can verify that by scaling and offsetting the UV node output before plugging in the DoDebug node and observing what happens.

@Arlorean
Copy link

Arlorean commented Oct 20, 2022

Thank you for saving me from my own stupidity!

[Update] @bestknighter - I've added the UV node but the 0.5 result is showing as 0.599. I think any missing digits may end up as 9s:
image

@bestknighter
Copy link

@Arlorean No worries, it happens!

Hmm, I re-read the code and this isn't supposed to happen, unless they changed how some of their math functions work. Highly unlikely but not impossible. It also could be that Shaderlab is truncating the Value input shown.
Could you do some tests and report back so I could reproduce, investigate and fix?

I'd need you to

  • Type a new value in x with 4+ digits before and after the decimal point, preferably no repeating consecutive digits. Take a screenshot of the result.
  • Erase the left most and the right most digit and take another screenshot.
  • Repeat last step until the final number is .
    If any of the steps produce an unexpected result, please reply with those prints.

You could also make the value input receive the result of a divide node and check with divisions just to make sure.

@Arlorean
Copy link

It wouldn't let me enter 4 digits before and after the decimal place unfortunately. But all these tests produce slightly incorrect results:

314.159 -> 314.158
image

14.15 -> 13.149
image

4.1 -> 4.099
image

Even when using a divide block though to produce 1/2 (0.5), which should be exactly representable in a float, the result is still incorrect at 0.599:
image

The same is true for 1/4 (0.249), 1/8 (0.124).

@bestknighter
Copy link

Hmmmm, ok. Thanks for those! I'll investigate this as soon as I can.

@bestknighter
Copy link

bestknighter commented Oct 24, 2022

Ok, I've got an update.

It seems that the cause is a rounding error happening in line 47.
The division that happens after exponentiation (before taking the absolute value) is not returning correctly.

The following is a test run I made with Value being 0.01 (and getting 0.009 in the shader output).

Here is the state before the division, where r0.z has fReducedValue and r0.w has the result of pow(10, fDigitIndex):
image

And here is after the division:
image
It was expected to have 1 at r0.z

This is not an issue with this shader, so all I can to do is file a bug report and try to find a workaround. I'll keep you updated if I manage to find one.


UPDATE:
Turns out the issue is NOT in the division, but one step earlier. On the exponentiation. In its first step (log instruction).

These three lines in the assembly are the entire pow function in action. Note that the result is going into r0.w.
image

In r0.z is the original 0.01 typed inside the Shader Graph. Note how the values are different (least significant bit is off by one).
image

0.01 in Hexadecimal, following IEE-754, is 3C23D70A. r0.z is correct, r0.w is off by one.
This error seems to be happening due to precision error. As the result of log2(10) in 32-bit is 3.32192802 and in 64-bit is 3.3219280948873623478703194294894. This difference of ~0.000000075 is propagating itself on the next two lines, causing the issue. The more negative fDigitIndex is, the more imprecise the result of pow gets.
I say this because, when I try to do the math myself using those different precisions, the error shows up with 32-bit but not 64-bit.

Since we can't request 64-bit precision for a specific node, I'll try to create a custom pow function, less performant of course, that avoids this issue.

@bestknighter
Copy link

@Arlorean Workaround found! You can check my fork for a "fixed" version.
https://gist.github.com/bestknighter/660e6a53cf6a6643618d8531f962be2c

@Arlorean
Copy link

Thanks @bestknighter, that's great work. I'll use your forked version.

@emilioparker
Copy link

image

Small issue...

@bestknighter
Copy link

@emilioparker are you using my fork? This version has some small bugs with float precision and I made a workaround on my fork.

@j1mmie
Copy link

j1mmie commented Feb 7, 2023

King

@WillardPeng
Copy link

Building upon the work of @aras-p and @bestknighter, I've developed a DebugSubGraph to streamline the process of setting up debug nodes .

@bestknighter
Copy link

Building upon the work of @aras-p and @bestknighter, I've developed a DebugSubGraph to streamline the process of setting up debug nodes .

Wow! Amazing stuff! Thank you for crediting us! That's very much appreciated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment