Reflections.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. /// <summary>
  2. /// Shiny SSRR - Screen Space Reflections for URP - (c) 2021 Kronnect
  3. /// </summary>
  4. using System;
  5. using System.Collections.Generic;
  6. using UnityEngine;
  7. namespace ShinySSRR {
  8. public enum Scope {
  9. OnlyThisObject = 0,
  10. IncludeChildren = 10
  11. }
  12. [Serializable]
  13. public struct SubMeshSettingsData {
  14. [Range(0, 1)]
  15. public float smoothness;
  16. }
  17. [ExecuteInEditMode]
  18. public partial class Reflections : MonoBehaviour {
  19. [Tooltip("Prevent this object from receiving reflections")]
  20. public bool ignore;
  21. [Tooltip("On which objects should reflections apply")]
  22. public Scope scope;
  23. [Tooltip("Include only objects matching this layer mask")]
  24. public LayerMask layerMask = -1;
  25. [Tooltip("Include only objects including this text")]
  26. public string nameFilter;
  27. [Tooltip("Optionally specify which submeshes can receive reflections. Set this to 0 to apply reflections to all submeshes.")]
  28. public int subMeshMask;
  29. [Header("Material Features")]
  30. [Tooltip("Use smoothness from object material (ignored if material doesn't have a smoothness property)")]
  31. public bool useMaterialSmoothness = true;
  32. [Tooltip("Custom material property name for the smoothness map")]
  33. public string materialSmoothnessMapPropertyName = "_MetallicGlossMap";
  34. [Tooltip("Custom material property name for the smoothness intensity")]
  35. public string materialSmoothnessIntensityPropertyName = "_Smoothness";
  36. [Tooltip("Smoothness value used if material has no smoothness property.")]
  37. [Range(0, 1)]
  38. public float smoothness = 0.75f;
  39. [Tooltip("Apply a different smoothness multiplier per submesh")]
  40. public bool perSubMeshSmoothness;
  41. [Tooltip("Smoothness values per each submesh")]
  42. public SubMeshSettingsData[] subMeshSettings;
  43. [Tooltip("Use normal map from object material (ignored if material doesn't have a smoothness property)")]
  44. public bool useMaterialNormalMap = true;
  45. [Tooltip("Custom material property name for the normal map")]
  46. public string materialNormalMapPropertyName = "_BumpMap";
  47. [Header("Raytracing Settings")]
  48. public bool overrideGlobalSettings;
  49. [Tooltip("Max number of samples used during the raymarch loop")]
  50. [Range(4, 128)] public int sampleCount = 16;
  51. [HideInInspector]
  52. public float stepSize; // no longer used; kept for backward compatibility during upgrade
  53. [Tooltip("Maximum reflection distance")]
  54. public float maxRayLength;
  55. [Tooltip("Assumed thickness of geometry in the depth buffer before binary search")]
  56. public float thickness = 0.2f;
  57. [Tooltip("Number of refinements steps when a reflection hit is found")]
  58. [Range(0, 16)] public int binarySearchIterations = 6;
  59. [Tooltip("Increase accuracy of reflection hit after binary search by discarding points further than a reduced thickness.")]
  60. public bool refineThickness;
  61. [Tooltip("Assumed thickness of geometry in the depth buffer after binary search")]
  62. [Range(0.005f, 1f)]
  63. public float thicknessFine = 0.05f;
  64. [Tooltip("Reflection decay with distance to reflective point")]
  65. public float decay = 2f;
  66. [Range(0, 1)]
  67. [Tooltip("Reduces reflection based on view angle")]
  68. public float fresnel = 0.75f;
  69. [Min(0)]
  70. [Tooltip("Ray dispersion with distance")]
  71. public float fuzzyness;
  72. [Tooltip("Makes sharpen reflections near objects")]
  73. public float contactHardening;
  74. [Tooltip("Jitter helps smoothing edges")]
  75. [Range(0, 1f)] public float jitter = 0.3f;
  76. [NonSerialized]
  77. public readonly static List<Reflections> instances = new List<Reflections>();
  78. [NonSerialized]
  79. public readonly List<SSR_Renderer> ssrRenderers = new List<SSR_Renderer>();
  80. [NonSerialized]
  81. public readonly List<Renderer> renderers = new List<Renderer>();
  82. void OnEnable() {
  83. if (maxRayLength == 0) {
  84. maxRayLength = Mathf.Max(0.1f, stepSize * sampleCount);
  85. }
  86. Refresh();
  87. instances.Add(this);
  88. }
  89. void OnDisable() {
  90. if (instances != null) {
  91. int k = instances.IndexOf(this);
  92. if (k >= 0) {
  93. instances.RemoveAt(k);
  94. }
  95. }
  96. }
  97. private void OnValidate() {
  98. fuzzyness = Mathf.Max(0, fuzzyness);
  99. thickness = Mathf.Max(0.01f, thickness);
  100. thicknessFine = Mathf.Max(0.01f, thicknessFine);
  101. contactHardening = Mathf.Max(0, contactHardening);
  102. decay = Mathf.Max(1f, decay);
  103. if (maxRayLength == 0) {
  104. maxRayLength = stepSize * sampleCount;
  105. }
  106. maxRayLength = Mathf.Max(0.1f, maxRayLength);
  107. Refresh();
  108. }
  109. private void OnDestroy() {
  110. if (ssrRenderers == null) return;
  111. for (int k = 0; k < ssrRenderers.Count; k++) {
  112. Material[] mats = ssrRenderers[k].ssrMaterials;
  113. if (mats != null) {
  114. for (int j = 0; j < mats.Length; j++) {
  115. if (mats[j] != null) DestroyImmediate(mats[j]);
  116. }
  117. }
  118. }
  119. }
  120. /// <summary>
  121. /// Updates the list of included renderers to receive reflections
  122. /// </summary>
  123. public void Refresh() {
  124. switch (scope) {
  125. case Scope.OnlyThisObject: {
  126. renderers.Clear();
  127. Renderer r = GetComponent<Renderer>();
  128. if (r != null) {
  129. renderers.Add(r);
  130. }
  131. }
  132. break;
  133. case Scope.IncludeChildren:
  134. GetComponentsInChildren(true, renderers);
  135. // Check if some children has an exclusive reflections component
  136. int rCount = renderers.Count;
  137. bool usesNameFilter = !string.IsNullOrEmpty(nameFilter);
  138. for (int k = 0; k < rCount; k++) {
  139. Renderer r = renderers[k];
  140. if (((1 << r.gameObject.layer) & layerMask) == 0) {
  141. renderers[k] = null;
  142. continue;
  143. }
  144. if (usesNameFilter) {
  145. if (!r.name.Contains(nameFilter)) {
  146. renderers[k] = null;
  147. continue;
  148. }
  149. }
  150. Reflections refl = r.GetComponent<Reflections>();
  151. if (refl != null && refl != this) {
  152. renderers[k] = null;
  153. }
  154. }
  155. break;
  156. }
  157. // Prepare required ssr materials slots
  158. int renderersCount = renderers.Count;
  159. ssrRenderers.Clear();
  160. if (ignore) return;
  161. for (int k = 0; k < renderersCount; k++) {
  162. Renderer r = renderers[k];
  163. if (r == null) continue;
  164. Reflections refl = r.GetComponent<Reflections>();
  165. if (refl != null && refl.ignore) {
  166. continue;
  167. }
  168. SSR_Renderer ssr = new SSR_Renderer {
  169. renderer = r
  170. };
  171. ssrRenderers.Add(ssr);
  172. }
  173. }
  174. public class SSR_Renderer {
  175. public Renderer renderer;
  176. public Material[] ssrMaterials;
  177. public List<Material> originalMaterials;
  178. public bool isInitialized;
  179. public bool exclude;
  180. public Collider collider;
  181. public bool hasStaticBounds;
  182. public Bounds staticBounds;
  183. public void Init(Material ssrMat) {
  184. isInitialized = true;
  185. originalMaterials = new List<Material>();
  186. renderer.GetSharedMaterials(originalMaterials);
  187. collider = renderer.GetComponent<Collider>();
  188. hasStaticBounds = false;
  189. if (renderer.isPartOfStaticBatch && collider == null) {
  190. MeshFilter mf = renderer.GetComponent<MeshFilter>();
  191. if (mf != null) {
  192. Mesh mesh = mf.sharedMesh;
  193. if (mesh != null) {
  194. int subMeshStartIndex = ((MeshRenderer)renderer).subMeshStartIndex;
  195. staticBounds = mesh.GetSubMesh(subMeshStartIndex).bounds;
  196. int subMeshesCount = originalMaterials.Count;
  197. for (int k = 1; k < subMeshesCount; k++) {
  198. staticBounds.Encapsulate(mesh.GetSubMesh(subMeshStartIndex + k).bounds);
  199. }
  200. hasStaticBounds = true;
  201. }
  202. }
  203. }
  204. ssrMaterials = new Material[originalMaterials.Count];
  205. for (int k = 0; k < ssrMaterials.Length; k++) {
  206. ssrMaterials[k] = Instantiate(ssrMat);
  207. }
  208. }
  209. public void UpdateMaterialProperties(Reflections go, ShinySSRR globalSettings) {
  210. if (go.subMeshSettings == null) {
  211. go.subMeshSettings = new SubMeshSettingsData[0];
  212. }
  213. float totalSmoothness = 0;
  214. for (int s = 0; s < ssrMaterials.Length; s++) {
  215. Material ssrMat = ssrMaterials[s];
  216. Material originalMaterial = originalMaterials[s];
  217. float smoothness = go.smoothness;
  218. if (go.perSubMeshSmoothness) {
  219. if (s < go.subMeshSettings.Length) {
  220. smoothness = go.subMeshSettings[s].smoothness;
  221. }
  222. }
  223. bool hasSmoothnessMap = false;
  224. bool hasNormalMap = false;
  225. if (originalMaterial != null) {
  226. if (go.useMaterialSmoothness) {
  227. if (!string.IsNullOrEmpty(go.materialSmoothnessIntensityPropertyName) && originalMaterial.HasProperty(go.materialSmoothnessIntensityPropertyName)) {
  228. smoothness = originalMaterial.GetFloat(go.materialSmoothnessIntensityPropertyName);
  229. } else if (originalMaterial.HasProperty(ShaderParams.Smoothness)) {
  230. smoothness = originalMaterial.GetFloat(ShaderParams.Smoothness);
  231. }
  232. Texture metallicGlossMap = null;
  233. if (!string.IsNullOrEmpty(go.materialSmoothnessMapPropertyName) && originalMaterial.HasProperty(go.materialSmoothnessMapPropertyName)) {
  234. metallicGlossMap = originalMaterial.GetTexture(go.materialSmoothnessMapPropertyName);
  235. } else if (originalMaterial.HasProperty(ShaderParams.MetallicGlossMap)) {
  236. metallicGlossMap = originalMaterial.GetTexture(ShaderParams.MetallicGlossMap);
  237. }
  238. if (metallicGlossMap != null) {
  239. ssrMat.SetTexture(ShaderParams.SmoothnessMap, metallicGlossMap);
  240. ssrMat.EnableKeyword(ShaderParams.SKW_SMOOTHNESSMAP);
  241. hasSmoothnessMap = true;
  242. }
  243. }
  244. if (originalMaterial.HasProperty(ShaderParams.Color) || originalMaterial.HasProperty(ShaderParams.BaseColor)) {
  245. smoothness *= originalMaterial.color.a;
  246. }
  247. if (go.useMaterialNormalMap) {
  248. Texture bumpMap = null;
  249. if (!string.IsNullOrEmpty(go.materialNormalMapPropertyName) && originalMaterial.HasProperty(go.materialNormalMapPropertyName)) {
  250. bumpMap = originalMaterial.GetTexture(go.materialNormalMapPropertyName);
  251. } else if (originalMaterial.HasProperty(ShaderParams.BumpMap)) {
  252. bumpMap = originalMaterial.GetTexture(ShaderParams.BumpMap);
  253. }
  254. if (bumpMap != null) {
  255. ssrMat.SetTexture(ShaderParams.BumpMap, bumpMap);
  256. ssrMat.SetVector(ShaderParams.BumpMap_ST, originalMaterial.GetVector(ShaderParams.BaseMap_ST));
  257. ssrMat.EnableKeyword(ShaderParams.SKW_NORMALMAP);
  258. hasNormalMap = true;
  259. }
  260. }
  261. }
  262. if (!hasSmoothnessMap) {
  263. ssrMat.DisableKeyword(ShaderParams.SKW_SMOOTHNESSMAP);
  264. }
  265. if (!hasNormalMap) {
  266. ssrMat.DisableKeyword(ShaderParams.SKW_NORMALMAP);
  267. }
  268. totalSmoothness += smoothness;
  269. if (go.overrideGlobalSettings) {
  270. if (go.jitter > 0) {
  271. ssrMat.EnableKeyword(ShaderParams.SKW_JITTER);
  272. } else {
  273. ssrMat.DisableKeyword(ShaderParams.SKW_JITTER);
  274. }
  275. if (go.refineThickness) {
  276. ssrMat.EnableKeyword(ShaderParams.SKW_REFINE_THICKNESS);
  277. ssrMat.SetFloat(ShaderParams.SSRSettings5, go.thicknessFine * go.thickness);
  278. } else {
  279. ssrMat.DisableKeyword(ShaderParams.SKW_REFINE_THICKNESS);
  280. }
  281. ssrMat.SetVector(ShaderParams.SSRSettings2, new Vector4(go.jitter, go.contactHardening, globalSettings.reflectionsMultiplier, globalSettings.vignetteSize));
  282. ssrMat.SetVector(ShaderParams.SSRSettings, new Vector4(go.thickness, go.sampleCount, go.binarySearchIterations, go.maxRayLength));
  283. ssrMat.SetVector(ShaderParams.MaterialData, new Vector4(smoothness, go.fresnel, go.fuzzyness, go.decay));
  284. } else {
  285. if (globalSettings.jitter > 0) {
  286. ssrMat.EnableKeyword(ShaderParams.SKW_JITTER);
  287. } else {
  288. ssrMat.DisableKeyword(ShaderParams.SKW_JITTER);
  289. }
  290. if (globalSettings.refineThickness) {
  291. ssrMat.EnableKeyword(ShaderParams.SKW_REFINE_THICKNESS);
  292. ssrMat.SetFloat(ShaderParams.SSRSettings5, globalSettings.thicknessFine * globalSettings.thickness);
  293. } else {
  294. ssrMat.DisableKeyword(ShaderParams.SKW_REFINE_THICKNESS);
  295. }
  296. ssrMat.SetVector(ShaderParams.SSRSettings2, new Vector4(globalSettings.jitter, globalSettings.contactHardening, globalSettings.reflectionsMultiplier, globalSettings.vignetteSize));
  297. ssrMat.SetVector(ShaderParams.SSRSettings, new Vector4(globalSettings.thickness, globalSettings.sampleCount, globalSettings.binarySearchIterations, globalSettings.maxRayLength));
  298. ssrMat.SetVector (ShaderParams.MaterialData, new Vector4(smoothness, globalSettings.fresnel, globalSettings.fuzzyness, globalSettings.decay));
  299. }
  300. }
  301. exclude = (totalSmoothness == 0);
  302. }
  303. }
  304. private void OnDrawGizmos() {
  305. for (int k = 0; k < ssrRenderers.Count; k++) {
  306. Bounds bounds = ssrRenderers[k].staticBounds;
  307. Gizmos.DrawWireCube(bounds.center, bounds.size);
  308. }
  309. }
  310. public void ApplyRaytracingPreset(RaytracingPreset preset) {
  311. switch (preset) {
  312. case RaytracingPreset.Fast:
  313. sampleCount = 16;
  314. maxRayLength = 6;
  315. binarySearchIterations = 4;
  316. thickness = 0.5f;
  317. refineThickness = false;
  318. jitter = 0.3f;
  319. break;
  320. case RaytracingPreset.Medium:
  321. sampleCount = 24;
  322. maxRayLength = 12;
  323. binarySearchIterations = 5;
  324. refineThickness = false;
  325. break;
  326. case RaytracingPreset.High:
  327. sampleCount = 48;
  328. maxRayLength = 24;
  329. binarySearchIterations = 6;
  330. refineThickness = false;
  331. thicknessFine = 0.05f;
  332. break;
  333. case RaytracingPreset.Superb:
  334. sampleCount = 88;
  335. maxRayLength = 48;
  336. binarySearchIterations = 7;
  337. refineThickness = true;
  338. thicknessFine = 0.02f;
  339. break;
  340. case RaytracingPreset.Ultra:
  341. sampleCount = 128;
  342. maxRayLength = 64;
  343. binarySearchIterations = 8;
  344. refineThickness = true;
  345. thicknessFine = 0.02f;
  346. break;
  347. }
  348. }
  349. }
  350. }