Skip to content

Instantly share code, notes, and snippets.

@slembcke
Last active April 29, 2019 18:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save slembcke/c905cc4f563ab9d9147dbcc2ac85acb1 to your computer and use it in GitHub Desktop.
Save slembcke/c905cc4f563ab9d9147dbcc2ac85acb1 to your computer and use it in GitHub Desktop.
Calculate a bounded mvp matrix for an object.
using UnityEngine;
[ExecuteInEditMode]
public class ProjectedBounds : MonoBehaviour {
private Transform _transform;
private Renderer _renderer;
private Mesh _mesh;
private MaterialPropertyBlock _block;
private void Start(){
_transform = this.transform;
_renderer = GetComponent<Renderer>();
_mesh = GetComponent<MeshFilter>().sharedMesh;
_block = new MaterialPropertyBlock();
}
// Get the component wise absolute value of the extents dotted with the matrix column.
private static float ExtentAxis(Vector3 e, Vector3 c){
return Mathf.Abs(e.x*c.x) + Mathf.Abs(e.y*c.y) + Mathf.Abs(e.z*c.z);
}
private void OnWillRenderObject(){
// Calculate the usual view-projection matrix.
var cam = Camera.current;
var view = cam.worldToCameraMatrix;
var projection = cam.projectionMatrix;
var vp_matrix = projection*view;
// Project the object's bounds into screen space using the model-view-projection matrix.
var mvp = vp_matrix*_transform.localToWorldMatrix;
var bounds = _mesh.bounds;
// Cast center to a homogenous point.
Vector4 center = bounds.center; center.w = 1;
// Project to clip coords.
center = mvp*center;
center.x /= center.w;
center.y /= center.w;
// Calculate the extents.
var ex = ExtentAxis(bounds.extents, mvp.GetRow(0))/center.w;
var ey = ExtentAxis(bounds.extents, mvp.GetRow(1))/center.w;
var ortho = Matrix4x4.Ortho(center.x - ex, center.x + ex, center.y - ey, center.y + ey, -1, 1);
_block.SetMatrix("_VP_Matrix", ortho*vp_matrix);
_renderer.SetPropertyBlock(_block);
}
}
Shader "ProjectedBounds" {
Properties {
[NoScaleOffset] _MainTex ("Texture", 2D) = "white" {}
}
SubShader {
Tags {"Queue" = "Geometry"}
Pass {
CGPROGRAM
#pragma vertex VShader
#pragma fragment FShader
#include "UnityCG.cginc"
struct VertInput {
float4 position : POSITION;
float2 uv : TEXCOORD0;
};
struct FragInput {
float4 position : SV_POSITION;
float4 screen_uv : TEXCOORD0;
};
sampler2D _MainTex;
float4x4 _VP_Matrix;
void VShader(in VertInput IN, out FragInput OUT) {
// This is what UnityObjectToClipPos() does,
// but we want to reuse the world space pos.
float4 world_pos = mul(UNITY_MATRIX_M, IN.position);
OUT.position = mul(UNITY_MATRIX_VP, world_pos);
// Calculate the screen pos with our own view-projection matrix.
float4 screen_pos = mul(_VP_Matrix, world_pos);
// Change from clip coords [-1, 1] to UV coords [0, 1].
OUT.screen_uv = screen_pos + screen_pos.w;
}
half4 FShader(in FragInput IN) : SV_Target {
// Need to use projective texturing or the triangles will be distorted.
return tex2Dproj(_MainTex, IN.screen_uv);
}
ENDCG
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment