// Copyright (c) 2023 Nico de Poel // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; using System.Collections; using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering; namespace FidelityFX { /// /// /// [RequireComponent(typeof(Camera))] public class Fsr3UpscalerCameraHelper : MonoBehaviour { [Tooltip("Standard scaling ratio presets.")] public Fsr3Upscaler.QualityMode qualityMode = Fsr3Upscaler.QualityMode.Quality; private Vector2Int _maxRenderSize; private Vector2Int _displaySize; private Camera _renderCamera; private DepthTextureMode _originalDepthTextureMode; private Rect _originalRect; private void OnEnable() { // Set up the original camera to output all of the required FSR3 Upscaler input resources at the desired resolution _renderCamera = GetComponent(); _originalDepthTextureMode = _renderCamera.depthTextureMode; _renderCamera.depthTextureMode = _originalDepthTextureMode | DepthTextureMode.Depth | DepthTextureMode.MotionVectors; } private void OnDisable() { // Restore the camera's original state _renderCamera.depthTextureMode = _originalDepthTextureMode; } private void LateUpdate() { // Determine the desired rendering and display resolutions _displaySize = GetDisplaySize(); Fsr3Upscaler.GetRenderResolutionFromQualityMode(out var maxRenderWidth, out var maxRenderHeight, _displaySize.x, _displaySize.y, qualityMode); _maxRenderSize = new Vector2Int(maxRenderWidth, maxRenderHeight); if (_maxRenderSize.x == 0 || _maxRenderSize.y == 0) { Debug.LogError($"FSR3 Upscaler render size is invalid: {_maxRenderSize.x}x{_maxRenderSize.y}. Please check your screen resolution and camera viewport parameters."); enabled = false; } // Remember the original camera viewport before we modify it in OnPreCull _originalRect = _renderCamera.rect; } private void OnPreCull() { // Render to a smaller portion of the screen by manipulating the camera's viewport rect _renderCamera.aspect = (_displaySize.x * _originalRect.width) / (_displaySize.y * _originalRect.height); _renderCamera.rect = new Rect(0, 0, _originalRect.width * _maxRenderSize.x / _renderCamera.pixelWidth, _originalRect.height * _maxRenderSize.y / _renderCamera.pixelHeight); ApplyJitter(); } private void ApplyJitter() { var scaledRenderSize = GetScaledRenderSize(); // Perform custom jittering of the camera's projection matrix according to FSR3's recipe int jitterPhaseCount = Fsr3Upscaler.GetJitterPhaseCount(scaledRenderSize.x, _displaySize.x); Fsr3Upscaler.GetJitterOffset(out float jitterX, out float jitterY, Time.frameCount, jitterPhaseCount); jitterX = 2.0f * jitterX / scaledRenderSize.x; jitterY = 2.0f * jitterY / scaledRenderSize.y; var jitterTranslationMatrix = Matrix4x4.Translate(new Vector3(jitterX, jitterY, 0)); _renderCamera.nonJitteredProjectionMatrix = _renderCamera.projectionMatrix; _renderCamera.projectionMatrix = jitterTranslationMatrix * _renderCamera.nonJitteredProjectionMatrix; _renderCamera.useJitteredProjectionMatrixForTransparentRendering = true; } private void OnRenderImage(RenderTexture src, RenderTexture dest) { // Restore the camera's viewport rect so we can output at full resolution _renderCamera.rect = _originalRect; _renderCamera.ResetProjectionMatrix(); Graphics.Blit(src, dest); } private Vector2Int GetDisplaySize() { var targetTexture = _renderCamera.targetTexture; if (targetTexture != null) return new Vector2Int(targetTexture.width, targetTexture.height); return new Vector2Int(_renderCamera.pixelWidth, _renderCamera.pixelHeight); } private bool UsingDynamicResolution() { var targetTexture = _renderCamera.targetTexture; return _renderCamera.allowDynamicResolution || (targetTexture != null && targetTexture.useDynamicScale); } private Vector2Int GetScaledRenderSize() { if (UsingDynamicResolution()) return new Vector2Int(Mathf.CeilToInt(_maxRenderSize.x * ScalableBufferManager.widthScaleFactor), Mathf.CeilToInt(_maxRenderSize.y * ScalableBufferManager.heightScaleFactor)); return _maxRenderSize; } } }