Created
October 29, 2016 22:11
-
-
Save PopupAsylum/b91d162c95155538b0eb9e72b80f9d4b to your computer and use it in GitHub Desktop.
Behaviour that modifies a renderers position during the render process to prevent it being culled
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Collections; | |
[ExecuteInEditMode] | |
[RequireComponent(typeof(MeshRenderer))] | |
public class RefractedBounds : MonoBehaviour { | |
MeshRenderer renderer; | |
void OnEnable() { | |
renderer = GetComponent<MeshRenderer>(); | |
Camera.onPreCull -= OnCameraPreCull; | |
Camera.onPreRender -= OnCameraPreRender; | |
Camera.onPreCull += OnCameraPreCull; | |
Camera.onPreRender += OnCameraPreRender; | |
} | |
void OnDisable() { | |
Camera.onPreCull -= OnCameraPreCull; | |
Camera.onPreRender -= OnCameraPreRender; | |
} | |
Vector3 positionBeforeOnPreCull; | |
void OnCameraPreCull(Camera camera) { | |
Vector4 surface = renderer.sharedMaterial.GetVector("_Surface"); | |
float refraction = renderer.sharedMaterial.GetFloat("_RefractiveIndex"); | |
positionBeforeOnPreCull = transform.position; | |
transform.position = Refract(transform.position, camera.transform.position, surface, 1 / refraction, Vector3.zero); | |
} | |
void OnCameraPreRender(Camera camera) { | |
transform.position = positionBeforeOnPreCull; | |
} | |
void OnWillRenderObject() { | |
transform.position = positionBeforeOnPreCull; | |
} | |
bool IsBelowSurface(Vector3 position, Vector4 surface) { | |
Vector3 surfaceNorm = new Vector3(surface.x, surface.y, surface.z); | |
return Vector3.Dot(position - surfaceNorm * surface.w, surfaceNorm) < 0; | |
} | |
Vector3 Refract(Vector3 position, Vector3 viewerPosition, Vector4 surface, float refractionIndex, Vector3 surfaceNormalOffset) { | |
//ray end is the vertex's undistorted position | |
Vector3 vertexPosition = position; | |
//get the vector from the camera to the vertex | |
Vector3 worldRay = vertexPosition - viewerPosition; | |
//normalize it for direction | |
Vector3 worldRayDir = worldRay.normalized; | |
//surface is a vector4 that defines a plane | |
Vector3 worldPlaneNormal = new Vector3(surface.x, surface.y, surface.z); | |
//define a known position on the plane | |
Vector3 worldPlaneOrigin = worldPlaneNormal * surface.w; | |
//get the vector result of the worldRay entering the water | |
Vector3 refraction = Refract(worldRayDir, (worldPlaneNormal + surfaceNormalOffset).normalized, refractionIndex); | |
//raycast from the vertex, backwards along the refraction vector | |
float denom = Vector3.Dot(-worldPlaneNormal, -refraction); | |
Vector3 p010 = worldPlaneOrigin - vertexPosition; | |
float t = Vector3.Dot(p010, -worldPlaneNormal) / denom; | |
Vector3 intersection = vertexPosition + refraction * -t; | |
//get the vector from the camera to the intersection, this is the perceived position | |
Vector3 originToIntersection = intersection - viewerPosition; | |
//starting from the camera, move the vector along the perceived position vector by the original ray length | |
return viewerPosition + originToIntersection.normalized * worldRay.magnitude; | |
} | |
Vector3 Refract(Vector3 inDir, Vector3 surface, float indexOfRefraction){ | |
float c1 = -Vector3.Dot(surface, inDir); | |
float c2 = Mathf.Sqrt(1 - indexOfRefraction * indexOfRefraction * (1 - c1 * c1)); | |
Vector3 result; | |
result.x = (indexOfRefraction * inDir.x) + (indexOfRefraction * c1 - c2) * surface.x; | |
result.y = (indexOfRefraction * inDir.y) + (indexOfRefraction * c1 - c2) * surface.y; | |
result.z = (indexOfRefraction * inDir.z) + (indexOfRefraction * c1 - c2) * surface.z; | |
return result; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment