Get world position in Vertex shader (HLSL)

I’m using Temaran’s UE4 Shader Plugin demo to write a custom Shader in HLSL purely from within C++ / engine code. (but I guess the question should also apply to the custom shader node)

When I try to multiply my vertex position with Primitive.LocalToWorld the result is always a zero vector.

Am I doing it wrong? How do you go from one space to another in the vertex and/or pixel shader in HLSL in UE4?

OutWorldPos= mul(float4(InPosition.xyz,1), Primitive.LocalToWorld);

I read this into the pixel shader and set it as the output color, and the result is pure 0 (black). If I set OutWorldPos = float4(0,0.5,0,1); that works and the pixel shader gets green.


Note: I am using the shader by rendering a quad: DrawPrimitiveUP(RHICmdList, PT_TriangleStrip, 2, Vertices, sizeof(Vertices[0]));

So I guess I have to manually create a world matrix in c++ and a float4x4 attribute on my shader and set it every frame?

I’m amazed how far Unreal has managed to get with such a cumbersome and undocumented graphics programming system.

I figured something out. I’m not accepting my answer because I haven’t figured out how/when any of the built in matrices work.

As everyone probably knows, UE4 is really hard to customize. UE4 Graphics programming using the engine code, is not only harder, it’s also undocumented.

I tried all matrices both on the HLSL usf side and on the c++ side to no avail. So I made my own matrix from scratch every frame and uploaded it to the shader as a 4x4 FMatrix to a shader via the UE4 DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER() macro system.

I render the camera to a quad. This means that in my Vertex shader I can treat it as if it is already in View space (instead of object space aka local space) to make it be exactly the size of the camera. So to get a ray in worldspace from that, I have to multiply by the Inverse Transpose View matrix.

Here’s how to construct an Inverse Transpose View matrix in UE4 in c++:

First get the Viewport of the client (the game running on your machine):

    APlayerCameraManager* Manager = World->GetFirstPlayerController()->PlayerCameraManager;
	ULocalPlayer* LocalPlayer = World->GetFirstLocalPlayerFromController();

	FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(
		LocalPlayer->ViewportClient->Viewport,
		World->Scene,
		LocalPlayer->ViewportClient->EngineShowFlags)
		.SetRealtimeUpdate(true));
	FVector ViewLocation;
	FRotator ViewRotation;
	const FSceneView* SceneView = LocalPlayer->CalcSceneView(&ViewFamily, /*out*/ ViewLocation, /*out*/ ViewRotation, LocalPlayer->ViewportClient->Viewport);

We need the ViewLocation (the current player’s rendering camera’s position in world space) and the ViewRotation.Vector() which is the camera’s forward vector. You can get these directly from the camera manager without CalcSceneView, but I added the SceneView code for reference because it provides a lot of matrix resources (none of which I could get working).

Now just make a simple reverse View Matrix:

FMatrix FPixelShaderUsageClass::GetViewMatrix(FVector eye, FVector target, FVector up) 
{
	FVector forwardX = eye - target; 
forwardX.Normalize();
FVector rightY = FVector::CrossProduct(forwardX, up); 
rightY.Normalize();
//upZ is already  normalized because it's a cross product of two normalized vectors
FVector upZ = FVector::CrossProduct(forwardX, rightY);

//The inverse and transpose of the View matrix. 
return *(new FMatrix(
	*(new FPlane(forwardX.X, rightY.X, upZ.X, eye.X)),
	*(new FPlane(forwardX.Y, rightY.Y, upZ.Y, eye.Y)),
	*(new FPlane(forwardX.Z, rightY.Z, upZ.Z, eye.Z)),
	*(new FPlane(0, 0, 0, 1))
	));
}

From this you can get a float4x4 matrix in your hlsl shader which you can mul with your vertex pos to go from “viewspace” to world space.