Yeah, I see the same behavior on MMA 10.4: something funny is going on here. This is really an extended comment rather than an answer.
You could define your own graylevel function that also prints the values passed to it for evaluation, to check what is going on:
Clear[gray]
gray[x_] := Module[{}, Print[x]; RGBColor[x, x, x]]
and use in MatrixPlot:
MatrixPlot[{{0, 0.5}}, ColorFunctionScaling -> False, ColorFunction -> gray]

As you can see the correct values seem to be passed to our color function, and interpreted correctly.
Normally MatrixPlot would send each value to be converted into color to the colorfunction separately:
MatrixPlot[{{0.2, 0.2}}, ColorFunctionScaling -> False, ColorFunction -> gray]

But when those values are all zeros, then only one single value is sent to the colorfunction:

and the incorrectly colored plot is returned.
Even in cases in which the correct plot is returned, the zero values are conflated into one call to the colorfunction:
MatrixPlot[{{0, 0}, {0, 0.5}}, ColorFunctionScaling -> False, ColorFunction -> gray]

Perhaps MatrixPlot
is being too clever here and applying some kind of internal optimization?
ArrayPlot[]
exhibits the same problem, apparently. $\endgroup$ – J. M. will be back soon♦ Apr 13 '16 at 21:19