Skip to content

Instantly share code, notes, and snippets.

@bgolus
Created July 6, 2020 00:07
Show Gist options
  • Save bgolus/df9fd6dfb11312e68592afe373b153f8 to your computer and use it in GitHub Desktop.
Save bgolus/df9fd6dfb11312e68592afe373b153f8 to your computer and use it in GitHub Desktop.
A "single triangle" cube shader. Really a shader that orients a flat mesh towards the camera and uses a box ray intersection to draw a cube in the interior.
Shader "Unlit/CameraFacingBoxRaycast"
{
Properties
{
_Cube ("Texture", Cube) = "" {}
_MeshScale ("Mesh Scale", Float) = 1.0
[Toggle] _DisableCameraFacing ("Disable Camera Facing", Float) = 0.0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float4 pos : SV_POSITION;
float3 ray : TEXCOORD0;
};
samplerCUBE _Cube;
// scale up the mesh if it's too small to see the entire box
// for example, the default Unity quad is too small and needs a scale of ~1.8
float _MeshScale;
// see what the raycasted box looks like without the mesh being oriented towards the camera
bool _DisableCameraFacing;
v2f vert (appdata v)
{
v2f o;
// calculate a camera facing rotation matrix
float3 worldSpaceObjectPos = unity_ObjectToWorld._m03_m13_m23;
float3 viewOffset = _WorldSpaceCameraPos.xyz - worldSpaceObjectPos;
float3 forward = normalize(viewOffset);
float3 right = normalize(cross(forward, float3(0,1,0)));
float3 up = cross(right, forward);
float3x3 rotMat = float3x3(right, up, forward);
// get the max object scale to ensure camera facing mesh is scaled large enough to cover
float3 scale = float3(
length(unity_ObjectToWorld._m00_m10_m20),
length(unity_ObjectToWorld._m01_m11_m21),
length(unity_ObjectToWorld._m02_m12_m22)
);
float maxScale = max(abs(scale.x), max(abs(scale.y), abs(scale.z)));
// calculate world space position for mesh
float3 worldPos = mul(v.vertex.xyz * maxScale * _MeshScale + float3(0,0,maxScale * sqrt(0.5 * 0.5 * 3.0)), rotMat) + worldSpaceObjectPos;
// calculate and output object space view ray for interpolation
o.ray = mul(unity_WorldToObject, float4(worldPos - _WorldSpaceCameraPos.xyz, 0.0));
o.pos = UnityWorldToClipPos(worldPos);
if (_DisableCameraFacing)
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
// from iq's excellent site
// https://www.iquilezles.org/www/articles/boxfunctions/boxfunctions.htm
float2 boxIntersection( in float3 ro, in float3 rd, in float3 rad, out float3 oN )
{
float3 m = 1.0/rd;
float3 n = m*ro;
float3 k = abs(m)*rad;
float3 t1 = -n - k;
float3 t2 = -n + k;
float tN = max( max( t1.x, t1.y ), t1.z );
float tF = min( min( t2.x, t2.y ), t2.z );
if( tN>tF || tF<0.0) return float2(-1.0, -1.0); // no intersection
oN = -sign(rd)*step(t1.yzx,t1.xyz)*step(t1.zxy,t1.xyz);
return float2( tN, tF );
}
fixed4 frag (v2f i) : SV_Target
{
// ray origin
float3 objectSpaceCameraPos = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos.xyz, 1.0)).xyz;
// ray box intersection
float3 faceNormal; // unused
float2 rayHits = boxIntersection(objectSpaceCameraPos, i.ray, float3(.5,.5,.5), faceNormal);
// above function returns float2(-1,-1) if there's no intersection
clip(rayHits.x);
// calculate object space position from ray, front hit ray length, and ray origin
float3 boxPos = i.ray * rayHits.x + objectSpaceCameraPos;
// sample cube map using object space position
fixed4 col = texCUBE(_Cube, boxPos);
return col;
}
ENDCG
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment