Hi,
I am working in Unity on pieces of shader code to convert between a memory address and a coordinate in a uniform grid. To do this I use the Modulo operator, but find odd behaviour I cannot explain.
Below is a visualisation of the grid. It simply draws a point at each gridpoint. The locations for each vertex are computed from the offset into the fixed size uniform grid. I.e. The vector cell is computed from the vertex shader instance ID, and this is in turn converted into NDCs and rendered.
I start with the naive implementation:
uint3 GetFieldCell(uint id, float3 numcells)
{
uint3 cell;
uint layersize = numcells.x * numcells.y;
cell.z = floor(id / layersize);
uint layeroffset = id % layersize;
cell.y = floor(layeroffset / numcells.x);
cell.x = layeroffset % numcells.x;
return cell;
}
And see the following visual artefacts:
[attachment=35344:modulo_1.PNG]
I discover that this is due to the modulo operator. If I replace it with my own modulo operation:
uint3 GetFieldCell(uint id, float3 numcells)
{
uint3 cell;
uint layersize = numcells.x * numcells.y;
cell.z = floor(id / layersize);
uint layeroffset = id - (cell.z * layersize);
cell.y = floor(layeroffset / numcells.x);
cell.x = layeroffset - (cell.y * numcells.x);
return cell;
}
The artefact disappears:
[attachment=35345:modulo_3.PNG]
I debug one of the errant vertices in the previous shader with RenderDoc, and find that it is implemented using frc, rather than a true integer modulo op, leaving small components that work their way into the coordinate calculations:
[attachment=35346:modulo_2.PNG]
So I try again:
uint3 GetFieldCell(uint id, float3 numcells)
{
uint3 cell;
uint layersize = numcells.x * numcells.y;
cell.z = floor(id / layersize);
uint layeroffset = floor(id % layersize);
cell.y = floor(layeroffset / numcells.x);
cell.x = floor(layeroffset % numcells.x);
return cell;
}
And it wor...!
Oh...
...That's unexpected:
[attachment=35347:modulo_4.PNG]
Can anyone explain this behaviour?
Is it small remainders of the multiplication of the frc result with the 'integer' as I suspect? If not, what else? If so, why does surrounding the result with floor() not work? (Its not optimised away, I've checked it in the debugger...)
Sj