public static Fov DistortTanAngles(Fov tanAngles, Distortion distortion) { return(new Fov ( distortion.Distort(tanAngles.Left), distortion.Distort(tanAngles.Right), distortion.Distort(tanAngles.Bottom), distortion.Distort(tanAngles.Top) )); }
private void UpdateUndistortionTex(Distortion distortion, float maxDistortedValue) { const int n = 16; float[] distortedValues = new float[n]; float distortedValue = 0f; float distortedValueStep = maxDistortedValue / (n - 1); // Генерируется n точек for (int i = 0; i < n; i++) { distortedValues[i] = distortedValue; distortedValue += distortedValueStep; } // Составляется кубический сплайн по n точкам. Сплайн используется для интерполяции функции, обратной к дисторсии CubicHermiteSpline spline = new CubicHermiteSpline(distortion, distortedValues); distortedValue = 0f; distortedValueStep = maxDistortedValue / (texWidth - 1); // Точно известно, что функция дисторсии принимает значения больше своего аргумента // Следовательно обратная функция будет всегда меньше аргумента. Это значит, что значение обратной ф-ции можно нормировать. // Для каждой точки текстуры вычисляется значение сплайна в точке, после чего делится на значение самой точки. Получается число от [0..1], оно записывается в цвет. for (int i = 0; i < _undistortionTex.width; ++i) { float eyeTanAngleNormalized = 1f; if (i > 0) { float undistortedValue = spline.GetValue(distortedValue); // Страховка от отклонений сплайна if (undistortedValue < distortedValue) { eyeTanAngleNormalized = undistortedValue / distortedValue; } } _undistortionTex.SetPixel(i, 0, new Color(eyeTanAngleNormalized, 0f, 0f)); distortedValue += distortedValueStep; } _undistortionTex.Apply(); }
void UpdateView(HmdParameters hmd, DisplayParameters display) { Distortion distortion = new Distortion(hmd.DistortionK1, hmd.DistortionK2); float zNear = _camWorldLeft.nearClipPlane; float zFar = _camWorldLeft.farClipPlane; Fov displayDistancesLeft = Calculator.GetFovDistancesLeft(display, hmd); // То, как должен видеть левый глаз свой кусок экрана. Без линзы. C учётом только размеров дисплея Fov fovDisplayTanAngles = displayDistancesLeft / hmd.ScreenToLensDist; // FoV шлема Fov hmdMaxFovTanAngles = Fov.AnglesToTanAngles(hmd.MaxFovAngles); // То, как должен видеть левый глаз свой кусок экрана. Без линзы. C учётом размеров дисплея и FoV шлема Fov fovEyeTanAglesLeft = Fov.Min(fovDisplayTanAngles, hmdMaxFovTanAngles); // То, как должен видеть левый глаз. Мнимое изображение (после увеличения идеальной линзой без искажений). С широким углом. Именно так надо снять сцену Fov fovWorldTanAnglesLeft = Calculator.DistortTanAngles(fovEyeTanAglesLeft, distortion); Matrix4x4 projWorldLeft; Matrix4x4 projWorldRight; Calculator.ComposeProjectionMatricesFromFovTanAngles(fovWorldTanAnglesLeft, zNear, zFar, out projWorldLeft, out projWorldRight); Matrix4x4 projEyeLeft; Matrix4x4 projEyeRight; Calculator.ComposeProjectionMatricesFromFovTanAngles(fovDisplayTanAngles, zNear, zFar, out projEyeLeft, out projEyeRight); _camWorldLeft.transform.localPosition = 0.5f * Vector3.left * hmd.InterlensDistance; _camWorldRight.transform.localPosition = 0.5f * Vector3.right * hmd.InterlensDistance; _camWorldLeft.projectionMatrix = projWorldLeft; _camWorldRight.projectionMatrix = projWorldRight; float maxWorldFovTanAngle = GetMaxDiagonalValue(fovWorldTanAnglesLeft); UpdateUndistortionTex(distortion, maxWorldFovTanAngle); EyeMaterial.SetFloat("_MaxWorldFovTanAngle", maxWorldFovTanAngle); EyeMaterial.SetTexture("_UndistortionTex", _undistortionTex); EyeMaterial.SetVector("_ProjectionWorldLeft", ComposeProjectionVector(projWorldLeft)); EyeMaterial.SetVector("_ProjectionEyeLeft", ComposeProjectionVector(projEyeLeft)); }