Howto modify the projection matrix

Hi guys,

I’m trying to create a runtime plugin to modify the projection matrix every frame (without touching the engine).

BUT:

  • i can’t override LocalPlayer::CalcSceneView / LocalPlayer::GetProjectionData because they aren’t virtual.
  • i can’t override ISceneViewExtension because seems that this extensions are used just for the HMD device (have i to create a fake IHeadMountedDisplay implementation?)

thanks a lot,

Martin

Have you looked at AActor::CalcCamera() and UCameraComponent::GetCameraView() ?

Hi,

yes, both give me as output a FMinimalViewInfo that doesn’t contains any info about the projection matrix. anyway it’s a read only struct

thanks

It does not look to be possible right now without modifying the engine. GetProjectionData would be the place to do it.

what im trying now is to extend UGameViewportClient

I’ll ovveride the Draw function. this function will be a copy of UGameViewportClient::Draw but after LocalPlayer->CalcSceneView I put my code …

Not very elegant … but seems the only option that I have…

I am trying to set the projection matrix too. Did you manage to do it?

I am trying to set the projection matrix too. Did you manage to do it?

Yes! If you don’t want to recompile the engine, the only solution is to extend UGameViewportClient class, overwrite the method ::Draw … it’s not a proper overwrite because you have to do a 1:1 copy …then after the call to LocalPlayer->CalcSceneView you have the View struct that contains the projection matrix (if you touch the proj matrix be sure to update related matrices …es viewprojmatrix…etc)
When you have done this you have to set your viewport class in Edit->Project Settings->General Settings

Thank you sooooo much :slight_smile:

Thank you very much! (kinda late)
Did you or KennyBobby run into the blur problem mentioned here:
and fixed it?
Also some ups here would be welcome.

It’s driving me insane, I lost a week trying to figure it out. I reached a point where I would pay for a plugin that would accept 2 floats for a basic camera shift.

I think that the ue4 projection matrix has some “requirements” …i have fixed all rendering problems doing these “magic stuffs”:

result.M[2][2] = 0.0f;
result.M[3][0] = 0.0f;
result.M[3][1] = 0.0f;

result *= 1.0f / result.M[0][0];
result.M[3][2] = GNearClippingPlane;

where result is your off-axis proj matrix

Other important point: be sure to update all related matrices:

	View->ProjectionMatrixUnadjustedForRHI = CalculateOffAxisProjectionMatrix(Viewport, location);

	FVector viewOrigin(0.0f, 0.0f, View->ProjectionMatrixUnadjustedForRHI.M[3][3]);

	View->ViewMatrices.ViewMatrix.SetOrigin(View->ViewMatrices.ViewMatrix.GetOrigin() - viewOrigin);

	View->InvViewMatrix = View->ViewMatrices.ViewMatrix.InverseSafe();
	View->ViewMatrices.ViewOrigin += View->InvViewMatrix.TransformPosition(-viewOrigin);
	View->ViewMatrices.PreViewTranslation = -View->ViewMatrices.ViewOrigin;
	View->ViewMatrices.ProjMatrix = _AdjustProjectionMatrixForRHI(View->ProjectionMatrixUnadjustedForRHI);
	View->ViewProjectionMatrix = View->ViewMatrices.GetViewProjMatrix();
	View->InvViewProjectionMatrix = View->ViewMatrices.GetInvProjMatrix() * View->InvViewMatrix;
	FMatrix TranslatedViewMatrix = FTranslationMatrix(-View->ViewMatrices.PreViewTranslation) * View->ViewMatrices.ViewMatrix;
	View->ViewMatrices.TranslatedViewProjectionMatrix = TranslatedViewMatrix * View->ViewMatrices.ProjMatrix;
	View->ViewMatrices.InvTranslatedViewProjectionMatrix = View->ViewMatrices.TranslatedViewProjectionMatrix.InverseSafe();
	View->ShadowViewMatrices = View->ViewMatrices;

	//View->InvDeviceZToWorldZTransform = CreateInvDeviceZToWorldZTransform(View->ProjectionMatrixUnadjustedForRHI);

	GetViewFrustumBounds(View->ViewFrustum, View->ViewProjectionMatrix, false);

Thank you so much for going through all of the trouble of figuring this out!

Any chance you could put up the complete code solution so all of us lazy googlers can just rip your work off whole sale and take all of the credit?

It would be much appreciated!

Thank you for all your help.
Unfortunately, as showed here, I’m still getting the motion blur.

I used all your suggestions with the following exceptions:
I had some errors with _AdjustProjectionMatrixForRHI so I copy-pasted it with the default values and also I used the non-safe inverts.

The only things that could be the cause of what I am seeing is maybe because I commented out SCOPE_CYCLE_COUNTER because I couldn’t get rid of some related errors (STAT_HudTime, STAT_UIDrawingTime)
Or maybe you inserted the corrections elsewhere?
I put it as mentioned in the NumViews for, right after FSceneView* View = LocalPlayer->CalcSceneView(&ViewFamily,…

On another note I got some limited success by modifying the engine (scenecapture2d). It’s a virtual function so I tried extending and overriding but since it’s FScene I’m supposed to extend and replace I can’t solve all the dependencies.

Try to use the code attached here

Some parts are marked with (@TODO) and there you have to make some changes…

good luck!

see my reply below

Thank you, thank you, thank you, thank you!
I now have everything I need, I have to make it to work.
Thank you once again!

Thanks man! I ended up figuring it out and I did something similar. Since it’ll cover some of your todo’s I’ll add it here:

What I ended up doing was creating a SetOffAxisMatrix blueprint option so that I could dynamically update it.

I also made a special function which I’ll attach below: (forgive the c# code, I’m not to good with C++)

The function takes 3 parameters: The height of the screen, the width of the screen, and the position of the eye relative to the center of the screen, all in MM. (so 0,0,0 would mean you’re in the middle of the tv, 0,0,500 would mean your head is 500mm back from the center of the screen and -20,0,500 would mean your head is back and slightly to the left of the center of the scren) I think the math reduces away the differences between mm,cm or Unreal world units so it doesn’t matter what you use so long as its all the same scale.

Thanks again man, I couldn’t have done this without your help!

	public static Matrix4 UpdateFrustrum(float ScreenWidth,float ScreenHeight,Vector3 eyePosition)
	{
		float headX = eyePosition.X / ScreenHeight;
		float headY = eyePosition.Y / ScreenHeight;
		float headDist = eyePosition.Z / ScreenHeight;
		float screenAspect = ScreenWidth / ScreenHeight;

		float nearPlane = 1f;
		Matrix4 resultM = Matrix4.CreatePerspectiveOffCenter(    nearPlane*(-.5f * screenAspect - headX)/headDist, 
			nearPlane*(.5f * screenAspect - headX)/headDist, 
			nearPlane*(-.5f + headY)/headDist, 
			nearPlane*(.5f + headY)/headDist, 
			nearPlane, 1000);


		return MagicFix (resultM);
	}

	private static Matrix4 MagicFix(Matrix4 matrix){
		float[,] result = Matrix2Float (matrix);
		//result [1, 2] = 0f;
		result[2,3] = 1f;
		result[2,2] = 0.0f;
		result[3,0] = 0.0f;
		result[3,1] = 0.0f;

		result = ScaleMatrix(result, (1.0f / result [0, 0]));
		result[3,2] = nearClipPlane;
		return Float2Matrix (result);
	}

I was able to get it to work with everything except I had to turn anti-aliasing down to 2 or off.

there’s also some wierd lightmap issues, but since its just a prototype I’m not to worried about it. (I mostly just disabled fog)

Also, if you don’t have a problem with modifying the source code, than its worth saying that I was able to fix all of the render issues (minus the anti-aliasing) by modifying the code in localplayer getprojectiondata instead.

void ULocalPlayer::SetOffAxis(const FMatrix& offAxisMatrix){
EnableOffAxis = true;
OffAxisProjectionMatrix = offAxisMatrix;
}

In the function ULocalPlayer::GetProjectionData I added:
if (EnableOffAxis)
{
ProjectionData.ProjectionMatrix = OffAxisProjectionMatrix;//
// calculate the out rect
ProjectionData.SetViewRectangle(FIntRect(X, Y, X + SizeX, Y + SizeY));
}
else if( !bNeedStereo )

Can You please describe how to use those files for dummy? I know how to use blueprints, etc. Some little more specific instructions of what to do to achieve projection mapping on material or postprocess.
Thanks!