Cycles is capable of this by using an OSL shader. The trick is to use the 'trace' function to trace multiple rays from the surface receiving the projection to the reflective surface and calculating the reflected ray using the surface normal at that point. Most of the rays will not find their way back to the projection source but some will. The shader sums the results of all those rays that reflect back to the projector source (or, at least, close enough).
shader volume_meshproximityreflect_texture(
vector Point = P,
vector ReflectorTarget = P,
vector ProjectorOrigin = P,
string FileName = "",
int Resolution = 10,
float Threshold = 0.999,
float ImageScale = 1.0,
output color Color = color(0.5,0.5,0.5)
) {
color Accumulation = color(0,0,0);
int AccumulationCount = 0;
float Distance = 0.0;
vector Normal = vector(0,0,0);
float Hit = 0.0;
vector Target_to_Projector = ProjectorOrigin - ReflectorTarget;
vector imageHorizVector = normalize(cross(Target_to_Projector, vector(0,0,1)));
vector imageVertVector = normalize(cross(imageHorizVector, Target_to_Projector));
float Step = 2.0 / Resolution;
vector randomvect = noise("cell", Point*5000);
float randoffsetx = randomvect[0]*Step;
float randoffsetz = randomvect[2]*Step;
for (float x = -1.0; x <= 1.0 ; x+= Step)
{
for (float z = -1.0; z <= 1.0 ; z+= Step)
{
vector ReflectorPoint = vector(ReflectorTarget[0]+x+randoffsetx, ReflectorTarget[1], ReflectorTarget[2]+z+randoffsetz);
vector point_to_reflector = ReflectorPoint - Point;
// trace the ray to find the normal at the point it hits
if(trace(Point,normalize(point_to_reflector)))
{
getmessage("trace", "hitdist", Distance);
getmessage("trace", "N", Normal);
//getmessage("trace", "hit", Hit);
}
else
{
continue;
}
// reflect it
vector reflected_ray = reflect(point_to_reflector, Normal);
vector reflector_to_projectororigin = ProjectorOrigin - ReflectorPoint;
vector separationVect = (normalize(reflector_to_projectororigin) - normalize(reflected_ray));
float separationDist = sqrt(dot(separationVect, separationVect));
if (separationDist < (Threshold*3))
{
//...it's a hit...
float x_offset = (dot(imageHorizVector, normalize(reflected_ray)) / ImageScale ) + 0.5;
float y_offset = (dot(imageVertVector, normalize(reflected_ray)) / ImageScale ) + 0.5;
if ((x_offset >=0.0 ) && (x_offset <= 1.0) && (y_offset >= 0.0) && (y_offset <= 1.0))
{
Accumulation += texture(FileName, x_offset, y_offset)/pow(2,separationDist/Threshold);
AccumulationCount++;
}
}
}
}
Color = Accumulation/(Threshold*Threshold)/Resolution/Resolution/100;
}
Blend file attached
EDIT : Here's another rendered result to show the effect of reducing the Threshold for a sharper result. Here I decreased the Threshold to 0.001 and increased the Resolution to 200. I also added a Subsurface modifier to the reflector mesh (set to a factor of 3) and reduced the size of the ripples on the reflector so that the distortion was not so pronounced.
This is now sharp enough to make out the text at the bottom of the image (although it's obviously distorted by the reflection and is mirrored left to right). This obviously took much longer to render. Quality could be further improved by increasing the number of render samples and/or decreasing the Threashold parameter further (but requiring longer render times)