Planar reflections not working

I tried to setup and enable planar reflections, but it behaves exactly the same way as it did without enabling them after I do so.

It reflects the sky just fine, but the environment doesn’t get reflected and SKM doesn’t get properly reflected either.

These are my settings

As for the mirror, it’s just a plane with a reflective material, there’s also a planar reflection actor roughly overlapping the plane.

I was following this guide.

I also noticed that placing the planar reflection actor or box reflection capture over the plane doesn’t seem like it actually does anything.

you didn’t follow the tutorial correctly. you have to set the global illumination to screen space or none. or if you want to use lumen gi you set the reflection method to none for the planar reflection to work.

both of those can be changed at runtime in the ppv for gameplay and visual control.

1 Like

I did that (set GI to SSR), it didn’t have much of an effect, I mean it changed the reflection’s properties a bit but it was still broken, just marginally less so (it rendered more of the SKM but it also had more artifacting, environment still didn’t reflect).

I’ll have to try setting reflection method to none though I didn’t try that way. I’m currently playing with scenecapture2d mirrors, I think I know a way to get them to very closely imitate real mirrors.

okay. dunno what kind of water shader you have there. it should work with basic blockout meshes or any static mesh props. atleast in 5.3 and 5.4 it works. not sure which version you’re on.

on the topic of sc2d mirrors… if you get them to work properly let me and @Barisbkc 's thread know. he’s an entitled feeling whatever. but he needs that stuff too for his lowend archviz project. i couldn’t figure out the projection math. maybe you got more talent or are smarter than me. :slight_smile:

Well it’s just a matter of basic understanding of how light and reflections work really.

I thought of using dot product as an indication for the angle from which the player is looking at the mirror, and rotating the scenecapture2d away from that angle accordingly. E.g. the more the camera is looking at the mirror from an angle, the more the scencapture2d should be looking directly away from the player. (player goes left, camera turns to the players right, player goes up camera turns down, etc)

The answer is already kinda available here Realistic mirror reflection vector with SceneCapture actor?

Just a matter of figuring out how to apply it in bp really.

1 Like

yeh… a reflection vector is easy math. the clipping plane is easy too. the fov aka projection won’t line up tho. i spent a couple hours on it. i’ll have to revisit it at some point, for sure. hmm.

I mean without any FOV shenanigans and before I had the idea of using reflection vector (which is kinda the obvious way but i’m new to this) I managed to get this far using some of my own code in combination with code i got from this thread Creating a mirror using Scene Capture 2D actor? - #2 by Lovecraft_K

300724-195405

But it’s got some problems, mainly the fov thing and how the Z height is currently handled.

It’s kinda the same concept as rotation vector really but instead of just rotating i’m also panning, it is working out pretty decently for the Y axis motion

1 Like

yes. i ran thru all of that too. i have an accurate camera reflection vector.

the issue is you need to peek thru a hole. you cannot calculate the frustum in blueprint. so… you gotta project the whole wall in mirrored screenspace. this renders alot of things you don’t need.

portal used a stencil hack to render this stuff. i’m not there yet tho. this is the next iteration i’ll try. not tonite tho.

2 Likes

Care to share? I managed to get it to a pretty good point using only rotation, though i’ll probably add some of the panning back in:

300724-204934

But I’d love to not have to work as hard for it if you know what I mean :wink:

I don’t mind it rendering a lot of things, I just need a working mirror with acceptable performance.

I made that post but never added the code to update the FOV of the mirror scene capture.

This is what the final mirror Tick() code looked like. Definitely has room to be improved upon. It was good enough though for the app we shipped years ago.

Variables in .h file.

// scene capture component that renders mirrored scene
UPROPERTY()
	ASceneCapture2D* _sceneCapture2d = nullptr;

// min and max fov
// fov changes when player moves toward/away from mirror
float _fovAtCloseDistance = 110.0f;
float _fovAtMaxDistance = 40.0f;
float _closeDistance = 100.0f;
float _farDistance = 900.0f;

// ref to player controller
ABasePlayerController* _playerController = nullptr;

Updating the scene capture in the mirror in Tick() function, removing null checks and other stuff not related to the logic.

// get player viewpoint
FVector playerCamPosition;
FRotator playerCamRotation;
_playerController->GetCameraLocationAndRotation(playerCamPosition, playerCamRotation);

// calculate reflection vector: r = d-2(dot(d,n))n
// n = mirror forward vector
// d = player vector to mirror
// r = reflection vector
FVector n = GetActorForwardVector();
n.Normalize();
FVector d = GetActorLocation() - playerCamPosition;
d.Normalize();
FVector r = d - 2 * (FVector::DotProduct(d, n)) * n;

// update FOV based on how far the player is from the mirror
// far away from mirror = narrow fov
// closer to mirror = wider fov
// lerp fov between min distance and max distance
float distanceToPlayer = (GetActorLocation() - playerCamPosition).Size();
float clampedDistanceToPlayer = FMath::Clamp(distanceToPlayer, _closeDistance, _farDistance);
float fovLerpAlpha = (clampedDistanceToPlayer - _closeDistance) / (_farDistance - _closeDistance);
float newFovAngle = FMath::InterpExpoOut(_fovAtCloseDistance, _fovAtMaxDistance, fovLerpAlpha);

// set scene capture camera angle and fov
_sceneCapture2d->SetActorRelativeRotation(r.Rotation());
_sceneCapture2d->GetCaptureComponent2D()->FOVAngle = newFovAngle;

// draw debug angle test code
// uncomment to visualize reflection angle in game
FVector lineStart = GetActorLocation();
FVector lineEnd = GetActorLocation() + r * 20.0f;
DrawDebugLine(GetWorld(), lineStart, lineEnd, FColor::Red);

Some possible improvements we never got around to:

  • adjust FOV based on angle to mirror surface
  • adjust FOV based on width of mirror surface
1 Like

Biggest performance improvements we found, turn off the mirror scene capture when:

  • player is far from the mirror
  • player is behind the mirror
  • player is not looking at the mirror
1 Like

well now… i’ve got it. it’s surprisingly simple. but has caveats. the render target gotta be the same apect ratio as the viewport. and it’s lagging a frame or 2 behind. and… yes… it is rendering the whole mirror view, but only needs the mirror plane information, which should be rendered reverse stenciled in a pre pass and skip everything that is not part of the mirror surface. that is the optimization step todo.

viewport reprojection.

and the blueprint. ignore the debug mess. lol

sharing is caring. :slight_smile: it’s not perfectly usable anyway. shruge

2 Likes

I’m trying to translate ur code to BP but it’s not working, i’m not entirely sure what I’m doing wrong

It looks like it should be exactly the same as

FVector n = GetActorForwardVector();
n.Normalize();
FVector d = GetActorLocation() - playerCamPosition;
d.Normalize();
FVector r = d - 2 * (FVector::DotProduct(d, n)) * n;
_sceneCapture2d->SetActorRelativeRotation(r.Rotation());

I would think, but the camera doesn’t move, the rotation doesn’t change and the output of r is usually in the range of 0-3 on a specific axis (x or y), got any ideas what I’m doing wrong? if I try the debug method you used at the end of the code, the line trace draws about 20 units in front or behind the camera (it can tell if i’m in front or behind the camera and by how much), but it doesn’t go in any other direction

also thanks for this, and for the optimization tips.

I’m curious how you guys handle scaling up the mirrors though, if you make the mirror bigger the image from the rendertarget is stretched to fit it, is there a way to correct for this?

@glitchered thanks! that is a pretty decent way of doing it, and I can see why you say it’s not prefectly usable, it works fine when you’re far away from the mirror but when you’re near it the camera tends to be too far away. :thinking:

Sorry I’m not much help with blueprints. :frowning_face:

As for the mirror mesh surface vs the render target, we just made sure the mesh surface has proper lined up 0,0 >> 1,1 UVs. The render target we just adjusted until it looked good enough without hurting performance too much. Nothing special really.

1 Like

wdym too far away? third person issue or what’s wrong? have you matched the player camera and the capture camera’s fov? they gotta be the same. in first person i can “perfectly” line it up wiith a lumen distance field mirror. to me it is 95% correct. just the lag and the render penalty is bad.

1 Like

I do think i got ur code right.

But the camera always seems to be too far away from the mirror plane
310724-171729

I had to use different values for the camera forward vector for some reason but other than that it’s the same I think. Also the FOV of the scene capture and the player camera are both the same.

EDIT: I just figured out the cause, i was doing the material different from you, instead of screen position viewport UV i was just using texcoord, changing that fixed the issue. That is a clever trick man! It also solves the issue of the image stretching somehow (probably all that camera distance)

also I just figured there’s a reflection vector node in bp :man_facepalming:

310724-170851

I’m not using it entirely correctly, it looks good for the Y axis rotation, not as good on the Z (dosn’t rotate enough in that direction) and if I get too far away it turns all the way around so i think some clamping is needed somewhere but yeah… Epic already implemented this logic for us.

1 Like

this is not how you use the reflection vector node. you don’t need it for the setup i proposed, anyway.

you for sure used the wrong material setup.

i’m working on directionality now. hmm

1 Like

I improved a little bit on your code

I converted it to use relative position and rotation instead of world, and I’m doing it with fewer nodes so it’s more efficient.

This system can be very eccentric about the render target resolution. I’m using some code to try to dynamically create it

And it’s very hit or miss, sometimes it gives me good results quite close to what you had in your previous screenshot where you posted a mirror next to a distance field mirror, other times it gives me bad results where things get stretched somehow so things become vertically smaller and throw all the calculations off.

When I fullscreen I get perfect results

(the further mirror is a normal material mirror, it’d actually be pretty awesome if it wouldn’t only render skeletal meshes from weird angles like this. I believe it’s only able to render the parts of the skm that are in frame for the player camera if I understand it correctly, and then uses distance fields to render the static meshes that aren’t visible)

1 Like

i had that too yesternite. it’s a mismatch between viewport and rendertarget aspect ratio. messes up the projection. i guess the projection matrix has to be updated too when you resize the rendertarget.

it’s only correct if the viewport and intial rendertarget aspect ratio matches, or if you play in a new pie window. with all of it being 16:9.

also… i hope you create the render target on begin play. this doesn’t belong in the tick function. a simple resize would do the same trick. still only on begin play. i’ve done that with a scale before the break. for some reason it doesn’t update the scale in a new pie window. it uses the last used viewport scale. weird bug, i guess.

anyway… i don’t understand rotator math. i tired, but… directionality will not be coded by me. i think i’m done here. this is where the blueprint code ends. the optimization gotta be done in c++.

i defo love this feature of this mirror technique.

not immersion breaking at all. huh…

2 Likes

Yeah it’s interesting how that only happens on rotation :thinking: it seems like the further you are from the mirror the worse it gets too.

And yes I do the rendertarget on beginplay, of course, I’m not a complete imbecile.

This is my complete current iteration:
Material: https://d3kjluh73b9h9o.cloudfront.net/original/4X/a/d/2/ad28622c3a874549009bb310536f2167f0d7e866.jpeg






Problems:

  • When the player camera is rotated (doesn’t happen on panning) the reflection spazzes out
  • Despite everything I’ve done to keep the render target aspect ratio consistent with the viewport resolution, the reflection is only 1:1 accurate compared to ‘proper’ mirrors when in fullscreen or in an aspect ratio that matches fullscreen. This seems to be an engine bug, and I can’t figure out a way around it.
  • I am using the near clipping plane setting to hide objects between the scene capture and the mirror’s surface, however I couldn’t get the math for it right and eventually settled on configuring it to the highest value that doesn’t result in parts of the reflection being hidden (so a good value for the most extreme viewing angle); but because it needs to be adjusted based on viewing angle and can’t just be the actual distance between the scene capture and the closest part of the mirror, it can never actually be perfect. The closer you can get that multiply node to 1 without encountering problems, the better your results will be for this, but still this is kinda the #1 thing that limits the use cases for this type of mirror, since objects rendered between the scene capture and the mirror would show up in the reflection if they’re not within the near clipping plane’s range. The problem really is that the near clipping plane is facing the same way as the scene capture component and not the mirror surface plane. A solution to this (which glitchered is using) is to use global clip planes, but it comes at a hefty cost:

    Another way would be to use a box collision in conjunction with hide actor components node to hide everything behind the mirror’s surface from the scene capture.

Edit: Apparently in the original post the mirror wouldn’t work from every angle, i’ve fixed it but it complicated the set relative location and rotation a little bit