Closed David-DiGioia closed 1 year ago
Shadertoy code runs in WebGL, while Godot shader code runs in native GLSL (on Vulkan or OpenGL). This means some differences are expected, especially with regards to floating-point precision.
I considered that but differences in floating point precision should not produce such stark differences in output. The splotches fall well outside the [-1, 1] range and even getting outside the [-10, 10] range, an order of magnitude difference from what's expected.
But there's no reason to speculate, I ran the same shader code in a custom Vulkan app and the result matches with the shadertoy result. Though I am using a different GPU right now (RX 6800) so when I get home I'll test it with my GTX 1080 to rule out it being an NVIDIA driver bug (possibly related to #67150).
I ran the shader in the custom Vulkan app on my GTX 1080, and still get the expected result matching shadertoy. This suggest the problem is on Godot's side.
I found that substituting n_
with 1.0/7.0
allowed for the correct result. Looking at the generated GLSL via "Inspect Native Shader Code" shows each float literal is being rounded prematurely.
Yes float constants in the shader language are parsed as floats, not doubles or string constants, should be the source of the problem
@celyk Thanks for the workaround, that's really helpful! Inspecting native shader code seems very useful, where is the option to do that?
So surely this is a bug and should be marked as such right?
@David-DiGioia I don't think it's a bug, I think it's the GLSL spec not being very clear
From the spec floats are single precision, not sure where things go weird but it shouldn't by just rounding the values, though there is some situations with constants that might get different results when compiled due to instruction combining, could be improved by adding our own constant folding to shader compiler
@Zireael07 I disagree. The GLSL spec says
The precision of highp single- and double-precision floating-point variables is defined by the IEEE 754 standard for 32-bit and 64-bit floating-point numbers.
and the Godot documentation says that highp is the default precision level. Then the precision of n_ is highp and is defined by the IEEE 754 standard for 32-bit floating-point numbers, and according to this standard
0.142857142857 has the hex representation 0x3e124925 0.142857 has the hex representation 0x3e12491b
which do not match. Then it is Godot that is rounding the number and losing precision, not a limitation of 32 bit floats. And technicalities aside, it's a very poor user experience to paste GLSL code and get a different result in Godot than everywhere else that runs that code.
Then that is the issue here, needs a fix somewhere for printing floating point numbers, will look into it
And technicalities aside
Now this is a technicality issue, we can't do more than follow the specification
This is not an issue of following the specification though, this is an issue with Godot's process of converting the Godot shader into GLSL.
The process as I understand it is: 1) I write a decimal representation of a float in Godot's shader (0.142857142857) 2) Godot parses this and converts it to a float (0x3e124925) 3) Godot creates a GLSL file where it prints this float as a truncated decimal constant (0.142857) 4) The GLSL compiler converts this decimal value into a binary representation (0x3e12491b)
This process of converting back and forth between decimal and binary loses precision and is made worse by the fact Godot's function to print the float is truncating it at 6 decimal places. Ideally Godot would just store the string "0.142857142857" and paste this into GLSL as is. But if not this then at least Godot should be printing maximum precision of the float it has stored.
What I'm saying is that the technicalities are what matters, as I said there is an issue and I've identified it and will look into a fix for it, relating generally to the representation of floats
The reason for this is that rtoss
prints 6 decimals only
Now just storing the string might not make sense or work, as it is not just a translator but does various other things afik
The issue is that the way floats are formatted is built in and kind of core and needs a bit of a larger approach and discussion
Godot version
4.0.3 stable
System information
Windows 10, gtx 1080, driver version 531.79 Vulkan backend.
Issue description
I am implementing 3d simplex noise in a spatial shader using this implementation https://github.com/ashima/webgl-noise/blob/master/src/noise3Dgrad.glsl
In shadertoy the implementation produces the expected result:
But using almost identical code in a Godot shader (only modified to give functions unique names since Godot doesn't support shader function overloading) produces an incorrect result with splotches of the noise falling well outside the [-1, 1] range it is suppose to be in, some even falling outside [-10, 10]:
Stepping through the shader code in shadertoy and Godot, the values begin to diverge at the line
Steps to reproduce
Create a new 3D scene, add a MeshInstance3D node as a child of the root. Give mesh instance node a new PlaneMesh (or any other mesh) and under Surface Material Override, add a new material by selecting New ShaderMaterial. Open the text editor of the shader material and paste the following shader:
To see the shadertoy comparison, go to https://www.shadertoy.com/new and paste the following code:
Minimal reproduction project
shader_bug.zip