I need to make a shader which freezes UNITY_MATRIX_MVP. So I figured I can calculate it in C# and then use SetMatrix() to freeze it. However, I can’t seem to get the correct MVP matrix. If I could fetch the UNITY_MATRIX_MVP matrix from the shader, store it in C#, then set it again in the shader, that would work as well, but I can’t use GetMatrix() before SetMatrix(). So that won’t work either.
This is the code I tried:
Matrix4x4 mat = Matrix4x4.identity;
mat[2,2] *= -1.0f;
Matrix4x4 frozenMatrixMVP = mat * (Camera.main.transform.worldToLocalMatrix * vidTexPatch.transform.localToWorldMatrix);
and
Matrix4x4 p = camera.projectionMatrix;
p[2,2] = -p[2,2];
p[3,2] = -p[3,2];
Matrix4x4 frozenMatrixMVP = p * Camera.main.transform.worldToLocalMatrix * vidTexPatch.transform.localToWorldMatrix;
and
bool d3d = SystemInfo.graphicsDeviceVersion.IndexOf("Direct3D") > -1;
Matrix4x4 M = vidTexPatch.transform.localToWorldMatrix;
Matrix4x4 V = Camera.main.worldToCameraMatrix;
Matrix4x4 P = Camera.main.projectionMatrix;
if (d3d) {
// Invert Y for rendering to a render texture
for (int i = 0; i < 4; i++) {
P[1,i] = -P[1,i];
}
// Scale and bias from OpenGL -> D3D depth range
for (int i = 0; i < 4; i++) {
P[2,i] = P[2,i]*0.5f + P[3,i]*0.5f;
}
}
Matrix4x4 frozenMatrixMVP = P*V*M;
What do you mean, fetch it from the shader? The shader’s original source for the MVP is from code, the video card doesn’t know anything, change anything, nor stores anything from the MVP, it gets it from code.
I’m also unsure what you mean with “freeze” the MVP, do you mean stop the screen from moving, etc… What is the effect you want to accomplish with this freezing and why does it need to adjust the MVP to achieve this effect?
Edit: forgot to add that the third example should be the correct way to set an MVP up in code in Unity.
Fetch it from the shader would be something like:
Matrix4x4 frozenMatrixMVP = vidTexPatch.renderer.material.GetMatrix(“FROZEN_MATRIX_MVP”);
I am sure the shader actually does know about the MVP, because “FROZEN_MATRIX_MVP” is build in.
With freezing I mean that the values of that matrix don’t change anymore, even if the camera and or the object transform changes. This is for an augmented reality video texture special effect.
Thanks, I will try to get the 3rd example to work again.
Camera cam = Camera.main;
Matrix4x4 P = GL.GetGPUProjectionMatrix(cam.projectionMatrix, false);
Matrix4x4 V = cam.worldToCameraMatrix;
Matrix4x4 M = renderer.localToWorldMatrix;
Matrix4x4 MVP = P * V * M;
I have wasted time figuring this out before aswell. In the end I found a gitHub page from Aras that led me to the solution.
Shader "Custom/Test" {
Properties {
_MainTex("Texture", 2D) = "white" { }
}
SubShader{
Pass{
Cull Back
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float4x4 _MATRIX_MVP;
struct v2f{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert(appdata_base v){
v2f o;
float2 screenSpacePos;
float4 clipPos;
//Convert position from world space to clip space.
//Only the UV coordinates should be frozen, so use a different matrix
clipPos = mul(_MATRIX_MVP, v.vertex);
//Convert position from clip space to screen space.
//Screen space has range x=-1 to x=1
screenSpacePos.x = clipPos.x / clipPos.w;
screenSpacePos.y = clipPos.y / clipPos.w;
//the screen space range (-1 to 1) has to be converted to
//the UV range 0 to 1
o.uv.x = (0.5f*screenSpacePos.x) + 0.5f;
o.uv.y = (0.5f*screenSpacePos.y) + 0.5f;
//The position of the vertex should not be frozen, so use
//the standard UNITY_MATRIX_MVP matrix
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
return o;
}
half4 frag(v2f i) : COLOR{
half4 texcol = tex2D(_MainTex, i.uv);
return texcol;
}
ENDCG
}
}
}
From Unity I call:
Matrix4x4 P = GL.GetGPUProjectionMatrix(Camera.main.projectionMatrix, false);
Matrix4x4 V = Camera.main.worldToCameraMatrix;
Matrix4x4 M = debugObject.renderer.localToWorldMatrix;
Matrix4x4 MVP = P * V * M;
debugObject.renderer.material.SetMatrix("_MATRIX_MVP", MVP);
Not sure. I changed many things along the way and I lost track
Anyway, it is working now.
Edit:
Ok, I found out what I was doing wrong. Besides some silly mistakes like adding the wrong shader to my object and using the wrong game object reference, the main thing was that the scale of the plane should be the same in x, y, and z. I had a scale of 0.02, 1, 0.02 and that wasn’t working. When I changed the scale to 0.02, 0.02, 0.02, it suddenly worked. Not sure why that is though…
I don’t think so because my shader is specifically designed for modifying and freezing a video texture for AR. Not sure if that is possible in a shader at all…