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.
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.
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)
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
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
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.
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:
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.
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.
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.
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.
But the camera always seems to be too far away from the mirror plane
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
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.
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.
(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)
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.
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