Looking at the source code, it doesn't seem to be possible, as the definition of white-space relevant for visual-line-mode appears to be hard-coded in the C part of the source code.
I'm not sure about my conclusions, but here is the sequence of my source archaeology (I'll be linking to github for ease of reference, though when actually carrying out the investigation I had used C-h f, C-h v and finally a recursive grep of the C code.)
visual-line-mode enables word-wrap by setting the variable word-wrap to t. (You can test this by just setting the variable to true, without enabling visual-line-mode itself.)
The variable word-wrap is defined in buffer.c:
DEFVAR_PER_BUFFER ("word-wrap", &BVAR (current_buffer, word_wrap), Qnil,
[...]
Guessing (and my terminology will be off, because I'm a horrible person who hasn't yet read all of the texinfo manual), this means that the elisp-accessible variable "word-wrap", sets the C variable word_wrap.
Searching for where word_wrap is used, we find:
it->line_wrap = NILP (BVAR (current_buffer, word_wrap))
? WINDOW_WRAP : WORD_WRAP;
i.e. if word_wrap is set in the current buffer, the it->line_wrap is set to WORD_WRAP (A ? B : C is a ternary operator...).
Looking further on, we get, among other things, this condition:
if (it->line_wrap == WORD_WRAP && it->area == TEXT_AREA)
{
if (IT_DISPLAYING_WHITESPACE (it))
may_wrap = true;
[...]
I'm eliding quite a large amount of code, which I don't really, fully understand, but it seems that unless IT_DISPLAYING_WHITESPACE (it) then either may_wrap will be explicitly set to false, or we'll escape the block and do something interesting elsewhere.
IT_DISPLAYING_WHITESPACE in turn, is defined here:
#define IT_DISPLAYING_WHITESPACE(it) \
((it->what == IT_CHARACTER && (it->c == ' ' || it->c == '\t')) \
|| ((STRINGP (it->string) \
&& (SREF (it->string, IT_STRING_BYTEPOS (*it)) == ' ' \
|| SREF (it->string, IT_STRING_BYTEPOS (*it)) == '\t')) \
|| (it->s \
&& (it->s[IT_BYTEPOS (*it)] == ' ' \
|| it->s[IT_BYTEPOS (*it)] == '\t')) \
|| (IT_BYTEPOS (*it) < ZV_BYTE \
&& (*BYTE_POS_ADDR (IT_BYTEPOS (*it)) == ' ' \
|| *BYTE_POS_ADDR (IT_BYTEPOS (*it)) == '\t')))) \
suggesting that the only two recognised white-space characters are the space and the tab.
Hence, it appears that tabs and spaces are hard-coded as the only characters at which wrapping is allowed to occur, but as already written above, I might (hopefully) be wrong.