I’m going over the cg series here.
In the first entry under the second chapter they throw this exercise to the reader:
f you are familiar with scripting in Unity, you could try this idea: write a script for an object that takes a reference to another sphere object and assigns (using renderer.sharedMaterial.SetMatrix()) the inverse model matrix (renderer.worldToLocalMatrix) of that sphere object to a float4x4 uniform parameter of the shader. In the shader, compute the position of the fragment in world coordinates and apply the inverse model matrix of the other sphere object to the fragment position. Now you have the position of the fragment in the local coordinate system of the other sphere object; here, it is easy to test whether the fragment is inside the sphere or not because in this coordinate system all Unity spheres are centered around the origin with radius 0.5. Discard the fragment if it is inside the other sphere object. The resulting script and shader can cut away points from the surface of any object with the help of a cutting sphere that can be manipulated interactively in the editor like any other sphere.
This is what I did:
using UnityEngine;
[ExecuteInEditMode]
public class CullMe : MonoBehaviour
{
public Transform culler;
void Update()
{
if (!culler)
return;
var renderer = GetComponent<Renderer>();
if (!renderer)
return;
var material = renderer.sharedMaterial;
material.SetMatrix("_CullerSpace", culler.worldToLocalMatrix);
}
}
This is the shader I have:
Shader "CGShader5"
{
SubShader
{
Pass
{
/*Cull Off*/
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
uniform float4x4 _CullerSpace;
struct vout
{
float4 pos : SV_POSITION;
float4 worldpos : TEXCOORD0;
float4 localpos : TEXCOORD1;
float4 cullerpos : TEXCOORD2;
};
vout vert(float4 vertex : POSITION)
{
vout _out;
_out.pos = mul(UNITY_MATRIX_MVP, vertex);
_out.localpos = vertex;
_out.worldpos = mul(_Object2World, vertex);
_out.cullerpos = mul(_CullerSpace, _out.worldpos);
return _out;
}
float4 frag(vout _in) : COLOR
{
/*return float4(_in.cullerpos.x, _in.cullerpos.y, 0, 1);*/
if (_in.cullerpos.x > -0.5 && _in.cullerpos.x < 0.5 &&
_in.cullerpos.y > -0.5 && _in.cullerpos.y < 0.5 &&
_in.cullerpos.z > -0.5 && _in.cullerpos.z < 0.5)
discard;
return float4(0, 1, 0, 1);
}
ENDCG
}
}
}
To use this:
- Create two spheres
- Give one of them a custom material with “CGShader5” as the material’s shader.
- Attach CullMe.cs to the previous sphere, and assign the other sphere as ‘culler’
- Move the two spheres close to each other
You should see something like this:
What I expect however is:
That is, I want to discard the fragments in the left sphere that are intersecting/inside of the right sphere, only those fragments!
The check in the frag function is actually doing what it should, but it’s incorrect. See those non-intersecting frags that are discarded? well according to this check they should be discarded cause their ‘x’ local to the other sphere is less than 0.5 (contained within the radius of the other sphere)
I believe it can be solved just with this inverse model matrix without introducing any other uniform variables (like the position of the other sphere) but I’m just not seeing it…
What am I missing?
Any help is appreciated!