您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
339 行
14 KiB
339 行
14 KiB
using UnityEngine;
|
|
using Unity.DemoTeam.Attributes;
|
|
|
|
namespace Unity.DemoTeam.DigitalHuman
|
|
{
|
|
[ExecuteAlways]
|
|
[RequireComponent(typeof(Renderer))]
|
|
public class EyeRenderer : MonoBehaviour
|
|
{
|
|
private Renderer rnd;
|
|
private MaterialPropertyBlock rndProps;
|
|
|
|
public float eyeRadius = 0.014265f;
|
|
|
|
[Space]
|
|
[Tooltip("The Z offset from the eye origin of the iris plane")]
|
|
public float eyeCorneaRadiusStart = 0.01325f;
|
|
[Tooltip("The Z offset from the cornea that contains limbus darkening (the dark ring around the iris)")]
|
|
public float eyeCorneaLimbusDarkeningOffset = 0.00075f;// was 0.001
|
|
[Range(1.0f, 1.4f), Tooltip("The index of refraction of a human cornea is 1.376, but our geometry works well with a value of 1.2. Affects magnitude of iris distortion from cornea lens.")]
|
|
public float eyeCorneaIndexOfRefraction = 1.3f;// was 1.2
|
|
[Range(0.0f, 1.0f)]
|
|
public float eyeCorneaSSSScale = 0.0f;
|
|
[Range(0.0f, 1.0f)]
|
|
public float eyeCorneaSmoothness = 0.917f;
|
|
public bool eyeCorneaDebug = false;
|
|
|
|
[Space]
|
|
public bool eyeIrisBentLightingDebug = false;
|
|
public bool eyeIrisBentLighting = true;
|
|
public float eyeIrisPlaneOffset = 0.002f;// was 0
|
|
[Range(-1.0f, 1.0f)]
|
|
public float eyeIrisConcavity = 0.0f;
|
|
|
|
[Space]
|
|
[Tooltip("Texture space pupil offset")]
|
|
public Vector2 eyePupilOffset = new Vector2(0.002f, 0.016f);
|
|
[Tooltip("Texture space pupil diameter")]
|
|
public float eyePupilDiameter = 0.095f;
|
|
[Tooltip("Texture space pupil falloff")]
|
|
public float eyePupilFalloff = 0.015f;
|
|
[Tooltip("Texture space pupil scale")]
|
|
public float eyePupilScale = 1.0f;
|
|
|
|
[Space]
|
|
public bool eyeWrappedLighting = false;
|
|
[Range(0.0f, 1.0f)]
|
|
public float eyeWrappedLightingCosine = 0.5f;
|
|
[Range(1.0f, 4.0f)]
|
|
public float eyeWrappedLightingPower = 4.0f;
|
|
|
|
// http://hyperphysics.phy-astr.gsu.edu/hbase/vision/eyescal.html
|
|
const float IOR_HUMAN_AQUEOUS_HUMOR = 1.336f;
|
|
const float IOR_HUMAN_CORNEA = 1.376f;
|
|
const float IOR_HUMAN_LENS = 1.406f;
|
|
|
|
// https://journals.lww.com/optvissci/Abstract/1995/10000/Refractive_Index_and_Osmolality_of_Human_Tears_.4.aspx
|
|
const float IOR_HUMAN_TEARS = 1.33698f;
|
|
|
|
[Space]
|
|
[Range(1.0f, 2.0f)]
|
|
public float eyeLitIORCornea = IOR_HUMAN_CORNEA;
|
|
[Range(1.0f, 2.0f)]
|
|
public float eyeLitIORSclera = IOR_HUMAN_TEARS;
|
|
|
|
[Space]
|
|
public Transform eyePolygonContainer;
|
|
private const int eyePolygonSize = 4;// should match size in EyeLitForward.shader
|
|
private Vector4[] eyePolygonOS = new Vector4[eyePolygonSize];
|
|
private Vector3 eyeAsgOriginOS = new Vector3(0.0f, 0.0f, 0.0f);
|
|
private Vector3 eyeAsgMeanOS = new Vector3(0.0f, 0.0f, 1.0f);
|
|
private Vector3 eyeAsgTangentOS = new Vector3(1.0f, 0.0f, 0.0f);
|
|
private Vector3 eyeAsgBitangentOS = new Vector3(0.0f, 1.0f, 0.0f);
|
|
private Vector2 eyeAsgSharpness = new Vector2(1.25f, 9.0f);
|
|
[Range(1e-1f, 128.0f)]
|
|
public float eyeAsgPower = 16.0f;
|
|
[Range(1e-7f, 1e-1f)]
|
|
public float eyeAsgThreshold = 1e-1f;
|
|
private Vector2 eyeAsgThresholdScaleBias = new Vector2(1.0f, 0.0f);
|
|
[Range(0.0f, 1.0f)]
|
|
public float eyeAsgModulateAlbedo = 0.5f;
|
|
|
|
[Space]
|
|
public ConeMapping coneMapping = ConeMapping.ClosingAxisSpaceSplit;
|
|
public enum ConeMapping
|
|
{
|
|
ObjectSpaceMean,
|
|
ClosingAxisSpaceSplit,
|
|
}
|
|
[VisibleIf("coneMapping", ConeMapping.ClosingAxisSpaceSplit)]
|
|
public float coneOriginOffset = 1.0f;
|
|
[VisibleIf("coneMapping", ConeMapping.ClosingAxisSpaceSplit)]
|
|
public Vector2 coneScale = Vector2.one;
|
|
[VisibleIf("coneMapping", ConeMapping.ClosingAxisSpaceSplit)]
|
|
public Vector3 coneBias = Vector3.zero;
|
|
public bool coneDebug = false;
|
|
|
|
void Awake()
|
|
{
|
|
rnd = GetComponent<Renderer>();
|
|
rndProps = new MaterialPropertyBlock();
|
|
}
|
|
|
|
// amplitude * e^(pow(sharpness * (cosTheta - 1), power)) = epsilon
|
|
// e^(pow(sharpness * (cosTheta - 1), power)) = epsilon / amplitude
|
|
// pow(sharpness * (cosTheta - 1), power) = log(epsilon / amplitude)
|
|
// pow(sharpness * (cosTheta - 1), power) = log(epsilon) - log(amplitude)
|
|
// sharpness * (cosTheta - 1) = pow(log(epsilon) - log(amplitude), 1.0 / power)
|
|
// sharpness = pow(log(epsilon) - log(amplitude), 1.0 / power) / (cosTheta - 1)
|
|
|
|
// https://mynameismjp.wordpress.com/2016/10/09/sg-series-part-2-spherical-gaussians-101/
|
|
float AsgSharpnessFromThreshold(float epsilon, float amplitude, float power, float cosTheta)
|
|
{
|
|
return 0.5f * Mathf.Pow(-Mathf.Log(epsilon) - Mathf.Log(amplitude), 1.0f / power) / -(cosTheta - 1.0f);
|
|
}
|
|
|
|
void LateUpdate()
|
|
{
|
|
var eyePolygonComplete = (eyePolygonContainer != null) && (eyePolygonContainer.childCount == eyePolygonSize);
|
|
if (eyePolygonComplete)
|
|
{
|
|
for (int i = 0; i != eyePolygonSize; i++)
|
|
{
|
|
eyePolygonOS[i] = this.transform.InverseTransformPoint(eyePolygonContainer.GetChild(i).position);
|
|
if (coneDebug)
|
|
{
|
|
DrawRayLocal(Vector3.zero, eyePolygonOS[i], Color.white);
|
|
}
|
|
}
|
|
|
|
float cosThetaTangent = 0.0f;
|
|
float cosThetaBitangent = 0.0f;
|
|
|
|
switch (coneMapping)
|
|
{
|
|
case ConeMapping.ObjectSpaceMean:
|
|
{
|
|
eyeAsgOriginOS = Vector3.zero;
|
|
eyeAsgMeanOS = Vector3.Normalize(
|
|
Vector3.Normalize(eyePolygonOS[0])
|
|
+ Vector3.Normalize(eyePolygonOS[1])
|
|
+ Vector3.Normalize(eyePolygonOS[2])
|
|
+ Vector3.Normalize(eyePolygonOS[3])
|
|
);
|
|
eyeAsgBitangentOS = Vector3.Normalize(
|
|
Vector3.Cross(eyeAsgMeanOS, Vector3.Normalize(eyePolygonOS[2] - eyePolygonOS[0]))
|
|
);
|
|
eyeAsgTangentOS = Vector3.Cross(eyeAsgBitangentOS, eyeAsgMeanOS);
|
|
|
|
float cosThetaMeanToLeft = Vector3.Dot(Vector3.Normalize(eyePolygonOS[0]), eyeAsgMeanOS);
|
|
float cosThetaMeanToRight = Vector3.Dot(Vector3.Normalize(eyePolygonOS[2]), eyeAsgMeanOS);
|
|
float cosThetaMeanToTop = Vector3.Dot(Vector3.Normalize(eyePolygonOS[1]), eyeAsgMeanOS);
|
|
float cosThetaMeanToBottom = Vector3.Dot(Vector3.Normalize(eyePolygonOS[3]), eyeAsgMeanOS);
|
|
|
|
cosThetaTangent = (cosThetaMeanToLeft + cosThetaMeanToRight) * 0.5f;
|
|
cosThetaBitangent = (cosThetaMeanToTop + cosThetaMeanToBottom) * 0.5f;
|
|
}
|
|
break;
|
|
case ConeMapping.ClosingAxisSpaceSplit:
|
|
{
|
|
var eyePolygonRot = Quaternion.Euler(coneBias.y, coneBias.x, coneBias.z);
|
|
|
|
for (int i = 0; i != eyePolygonSize; i++)
|
|
{
|
|
eyePolygonOS[i] = eyePolygonRot * eyePolygonOS[i];
|
|
}
|
|
|
|
var closingPlaneNormal = Vector3.Normalize(eyePolygonOS[2] - eyePolygonOS[0]);
|
|
var closingPlaneOrigin = Vector3.ProjectOnPlane(eyePolygonOS[0], closingPlaneNormal);
|
|
|
|
var closingPlanePosTop = Vector3.ProjectOnPlane(eyePolygonOS[1], closingPlaneNormal) - closingPlaneOrigin;
|
|
var closingPlanePosBottom = Vector3.ProjectOnPlane(eyePolygonOS[3], closingPlaneNormal) - closingPlaneOrigin;
|
|
var closingPlaneDirTop = Vector3.Normalize(closingPlanePosTop);
|
|
var closingPlaneDirBottom = Vector3.Normalize(closingPlanePosBottom);
|
|
|
|
var closingPlaneForward = Vector3.Normalize(closingPlaneDirTop + closingPlaneDirBottom);
|
|
{
|
|
closingPlaneOrigin -= closingPlaneForward * (0.01f * coneOriginOffset);
|
|
//TODO pick an origin that sends the resulting forward vector through the original origin in the closing plane
|
|
|
|
closingPlanePosTop = Vector3.ProjectOnPlane(eyePolygonOS[1], closingPlaneNormal) - closingPlaneOrigin;
|
|
closingPlanePosBottom = Vector3.ProjectOnPlane(eyePolygonOS[3], closingPlaneNormal) - closingPlaneOrigin;
|
|
closingPlaneDirTop = Vector3.Normalize(closingPlanePosTop);
|
|
closingPlaneDirBottom = Vector3.Normalize(closingPlanePosBottom);
|
|
|
|
closingPlaneForward = Vector3.Normalize(closingPlaneDirTop + closingPlaneDirBottom);
|
|
}
|
|
|
|
var openingPosLeft = (Vector3)eyePolygonOS[0] - closingPlaneOrigin;
|
|
var openingPosRight = (Vector3)eyePolygonOS[2] - closingPlaneOrigin;
|
|
var openingDirLeft = Vector3.Normalize(openingPosLeft);
|
|
var openingDirRight = Vector3.Normalize(openingPosRight);
|
|
|
|
var closingPlaneAltitude = coneScale.y * 0.5f * Mathf.Deg2Rad * Vector3.Angle(closingPlaneDirTop, closingPlaneDirBottom);
|
|
var closingPlaneAzimuth = coneScale.x * 0.5f * Mathf.Deg2Rad * Vector3.Angle(openingDirLeft, openingDirRight);
|
|
|
|
if (coneDebug)
|
|
{
|
|
DrawRayLocal(closingPlaneOrigin, closingPlanePosTop, Color.yellow);
|
|
DrawRayLocal(closingPlaneOrigin, closingPlanePosBottom, Color.yellow);
|
|
DrawRayLocal(closingPlaneOrigin, openingPosLeft, Color.yellow);
|
|
DrawRayLocal(closingPlaneOrigin, openingPosRight, Color.yellow);
|
|
}
|
|
|
|
eyeAsgOriginOS = closingPlaneOrigin;
|
|
eyeAsgMeanOS = closingPlaneForward;
|
|
eyeAsgTangentOS = closingPlaneNormal;
|
|
eyeAsgBitangentOS = Vector3.Normalize(Vector3.Cross(eyeAsgMeanOS, eyeAsgTangentOS));
|
|
|
|
cosThetaTangent = Mathf.Cos(closingPlaneAzimuth);
|
|
cosThetaBitangent = Mathf.Cos(closingPlaneAltitude);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (coneDebug)
|
|
{
|
|
DrawRayLocal(eyeAsgOriginOS, eyeAsgMeanOS, Color.red);
|
|
DrawRayLocal(eyeAsgOriginOS, eyeAsgBitangentOS, Color.green);
|
|
DrawRayLocal(eyeAsgOriginOS, eyeAsgTangentOS, Color.blue);
|
|
}
|
|
|
|
eyeAsgSharpness.x = AsgSharpnessFromThreshold(eyeAsgThreshold, 1.0f, eyeAsgPower, cosThetaTangent);
|
|
eyeAsgSharpness.y = AsgSharpnessFromThreshold(eyeAsgThreshold, 1.0f, eyeAsgPower, cosThetaBitangent);
|
|
|
|
eyeAsgThresholdScaleBias.x = 1.0f / (1.0f - eyeAsgThreshold);
|
|
eyeAsgThresholdScaleBias.y = -eyeAsgThreshold / (1.0f - eyeAsgThreshold);
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i != eyePolygonSize; i++)
|
|
{
|
|
eyePolygonOS[i] = Vector4.zero;
|
|
}
|
|
}
|
|
|
|
if (eyeCorneaDebug)
|
|
{
|
|
var pos = this.transform.position;
|
|
var rot = this.transform.rotation;
|
|
|
|
var dirBF = rot * Vector3.forward;
|
|
var dirBT = rot * Vector3.up;
|
|
var dirLR = rot * Vector3.right;
|
|
|
|
var rot45 = rot * Quaternion.AngleAxis(45.0f, Vector3.forward);
|
|
|
|
var dirBT45 = rot45 * dirBT;
|
|
var dirLR45 = rot45 * dirLR;
|
|
|
|
var posIris = pos + dirBF * eyeCorneaRadiusStart;
|
|
var posFade = pos + dirBF * (eyeCorneaRadiusStart + eyeCorneaLimbusDarkeningOffset);
|
|
var posSide = pos + dirLR * eyeRadius;
|
|
|
|
var lineIris = eyeRadius * 0.6f;
|
|
{
|
|
Debug.DrawLine(posIris - dirBT * lineIris, posIris + dirBT * lineIris, Color.red);
|
|
Debug.DrawLine(posIris - dirLR * lineIris, posIris + dirLR * lineIris, Color.red);
|
|
|
|
Debug.DrawLine(posIris - dirBT45 * lineIris, posIris + dirBT45 * lineIris, Color.red);
|
|
Debug.DrawLine(posIris - dirLR45 * lineIris, posIris + dirLR45 * lineIris, Color.red);
|
|
}
|
|
|
|
var lineFade = eyeRadius * 0.5f;
|
|
{
|
|
Debug.DrawLine(posFade - dirBT45 * lineFade, posFade + dirBT45 * lineFade, Color.magenta);
|
|
Debug.DrawLine(posFade - dirLR45 * lineFade, posFade + dirLR45 * lineFade, Color.magenta);
|
|
|
|
Debug.DrawLine(posFade - dirBT45 * lineFade, posIris - dirBT45 * lineFade, Color.magenta);
|
|
Debug.DrawLine(posFade + dirBT45 * lineFade, posIris + dirBT45 * lineFade, Color.magenta);
|
|
|
|
Debug.DrawLine(posFade - dirLR45 * lineFade, posIris - dirLR45 * lineFade, Color.magenta);
|
|
Debug.DrawLine(posFade + dirLR45 * lineFade, posIris + dirLR45 * lineFade, Color.magenta);
|
|
}
|
|
|
|
var lineSide = eyeRadius * 0.25f;
|
|
{
|
|
Debug.DrawLine(posSide - dirBT * lineSide, posSide + dirBT * lineSide, Color.blue);
|
|
Debug.DrawLine(posSide - dirBF * lineSide, posSide + dirBF * lineSide, Color.blue);
|
|
}
|
|
}
|
|
|
|
if (rndProps == null)
|
|
rndProps = new MaterialPropertyBlock();
|
|
|
|
rnd.GetPropertyBlock(rndProps);
|
|
{
|
|
rndProps.SetFloat("_EyeRadius", eyeRadius);
|
|
rndProps.SetFloat("_EyeCorneaRadiusStart", eyeCorneaRadiusStart);
|
|
rndProps.SetFloat("_EyeCorneaLimbusDarkeningOffset", eyeCorneaLimbusDarkeningOffset);
|
|
rndProps.SetFloat("_EyeCorneaIndexOfRefraction", eyeCorneaIndexOfRefraction);
|
|
rndProps.SetFloat("_EyeCorneaIndexOfRefractionRatio", 1.0f / eyeCorneaIndexOfRefraction);
|
|
rndProps.SetFloat("_EyeCorneaSSS", eyeCorneaSSSScale);
|
|
rndProps.SetFloat("_EyeCorneaSmoothness", eyeCorneaSmoothness);
|
|
|
|
rndProps.SetFloat("_EyeIrisBentLightingDebug", eyeIrisBentLightingDebug ? 1 : 0);
|
|
rndProps.SetFloat("_EyeIrisBentLighting", eyeIrisBentLighting ? 1 : 0);
|
|
rndProps.SetFloat("_EyeIrisPlaneOffset", eyeIrisPlaneOffset);
|
|
rndProps.SetFloat("_EyeIrisConcavity", eyeIrisConcavity);
|
|
|
|
rndProps.SetVector("_EyePupilOffset", eyePupilOffset);
|
|
rndProps.SetFloat("_EyePupilDiameter", eyePupilDiameter);
|
|
rndProps.SetFloat("_EyePupilFalloff", eyePupilFalloff);
|
|
rndProps.SetFloat("_EyePupilScale", eyePupilScale);
|
|
|
|
if (eyeWrappedLighting)
|
|
{
|
|
rndProps.SetFloat("_EyeWrappedLightingCosine", eyeWrappedLightingCosine);
|
|
rndProps.SetFloat("_EyeWrappedLightingPower", eyeWrappedLightingPower);
|
|
}
|
|
else
|
|
{
|
|
rndProps.SetFloat("_EyeWrappedLightingCosine", 0.0f);
|
|
rndProps.SetFloat("_EyeWrappedLightingPower", 1.0f);
|
|
}
|
|
|
|
rndProps.SetFloat("_EyeLitIORCornea", eyeLitIORCornea);
|
|
rndProps.SetFloat("_EyeLitIORSclera", eyeLitIORSclera);
|
|
|
|
rndProps.SetVector("_EyeAsgOriginOS", eyeAsgOriginOS);
|
|
rndProps.SetVector("_EyeAsgMeanOS", eyeAsgMeanOS);
|
|
rndProps.SetVector("_EyeAsgTangentOS", eyeAsgTangentOS);
|
|
rndProps.SetVector("_EyeAsgBitangentOS", eyeAsgBitangentOS);
|
|
rndProps.SetVector("_EyeAsgSharpness", eyeAsgSharpness);
|
|
rndProps.SetFloat("_EyeAsgPower", eyeAsgPower);
|
|
rndProps.SetVector("_EyeAsgThresholdScaleBias", eyeAsgThresholdScaleBias);
|
|
rndProps.SetFloat("_EyeAsgModulateAlbedo", eyeAsgModulateAlbedo);
|
|
}
|
|
rnd.SetPropertyBlock(rndProps);
|
|
}
|
|
|
|
void DrawRayLocal(Vector3 localPosition, Vector3 localVector, Color color)
|
|
{
|
|
Vector3 worldPosition = this.transform.TransformPoint(localPosition);
|
|
Vector3 worldVector = this.transform.TransformVector(localVector);
|
|
Debug.DrawRay(worldPosition, worldVector, color);
|
|
}
|
|
}
|
|
}
|