diff --git a/.editorconfig b/.editorconfig index 9933dd0..00be4e1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,6 +8,8 @@ root = true indent_style = tab trim_trailing_whitespace = true insert_final_newline = true +charset = utf-8-bom +end_of_line = crlf # (Please don't specify an indent_size here; that has too many unintended consequences.) # Code files diff --git a/CHANGELOG.md b/CHANGELOG.md index b1728fd..ccf5497 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,25 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/); this project adheres to [Semantic Versioning](http://semver.org/). +## [0.8.1] - 2020.09.21 + +Alpha test support, bugfixes and misc improvements. + +### Added +- Added support for alpha-testing ([#10](https://github.com/Arvtesh/UnityFx.Outline/issues/10)). +- Added support for merging outline layer objects ([#12](https://github.com/Arvtesh/UnityFx.Outline/issues/12)). +- Added `RemoveGameObject` helper methof to `OutlineEffect` ([#15](https://github.com/Arvtesh/UnityFx.Outline/issues/15)). +- Added ability to customize render event in `OutlineBehaviour`. +- Added ability to render outlines to the specified camera only for `OutlineBehaviour`. +- Added warning for unsupported render pipelines for `OutlineBehaviour` and `OutlineEffect`. + +### Changed +- Misc inspector improvements. +- Changed default render event to `AfterSkybox`. + +### Fixed +- Fixed incorrect condition for selection of render method, which sometimes caused problems with outline rendering on mobiles ([#14](https://github.com/Arvtesh/UnityFx.Outline/issues/14)). + ## [0.8.0] - 2020.05.30 [URP](https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@8.1/manual/index.html) support, core refactoring and bugfixes. diff --git a/Outline.Core/Assets/Examples/SimplePerCamera/Cutout.mat b/Outline.Core/Assets/Examples/SimplePerCamera/Cutout.mat new file mode 100644 index 0000000..483d94b --- /dev/null +++ b/Outline.Core/Assets/Examples/SimplePerCamera/Cutout.mat @@ -0,0 +1,78 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Cutout + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: _ALPHATEST_ON + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: 2450 + stringTagMap: + RenderType: TransparentCutout + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 2800000, guid: 0ff6a32c354a4bd40a270e2d1e39335f, type: 3} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 1 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/Outline.Core/Assets/Examples/SimplePerCamera/Cutout.mat.meta b/Outline.Core/Assets/Examples/SimplePerCamera/Cutout.mat.meta new file mode 100644 index 0000000..bb419d5 --- /dev/null +++ b/Outline.Core/Assets/Examples/SimplePerCamera/Cutout.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d11ad24ddc889ee4c9067711d2b431a4 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Outline.Core/Assets/Examples/SimplePerCamera/Outline.unity b/Outline.Core/Assets/Examples/SimplePerCamera/Outline.unity index 5a17553..2dd9f75 100644 --- a/Outline.Core/Assets/Examples/SimplePerCamera/Outline.unity +++ b/Outline.Core/Assets/Examples/SimplePerCamera/Outline.unity @@ -79,7 +79,7 @@ LightmapSettings: m_PVRFilterTypeDirect: 0 m_PVRFilterTypeIndirect: 0 m_PVRFilterTypeAO: 0 - m_PVRFilteringMode: 1 + m_PVRFilteringMode: 2 m_PVRCulling: 1 m_PVRFilteringGaussRadiusDirect: 1 m_PVRFilteringGaussRadiusIndirect: 5 @@ -229,8 +229,12 @@ MonoBehaviour: _content: - Go: {fileID: 1579373802} LayerIndex: 0 + - Go: {fileID: 1244590621} + LayerIndex: 0 - Go: {fileID: 748173439} LayerIndex: 1 + - Go: {fileID: 1150463983} + LayerIndex: 2 --- !u!20 &692811815 Camera: m_ObjectHideFlags: 0 @@ -287,6 +291,8 @@ Transform: - {fileID: 1579373806} - {fileID: 748173443} - {fileID: 1789341921} + - {fileID: 1150463984} + - {fileID: 1244590625} m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -304,7 +310,7 @@ MonoBehaviour: m_EditorClassIdentifier: _outlineResources: {fileID: 11400000, guid: d28e70f030b1a634db9a6a6d5478ef19, type: 2} _outlineLayers: {fileID: 11400000, guid: 3a6c3b3c5f6e3ad4ab8e09fc219865bd, type: 2} - _cameraEvent: 18 + _cameraEvent: 15 --- !u!1 &748173439 GameObject: m_ObjectHideFlags: 0 @@ -396,6 +402,175 @@ Transform: m_Father: {fileID: 692811816} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1150463983 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1150463984} + - component: {fileID: 1150463987} + - component: {fileID: 1150463986} + m_Layer: 0 + m_Name: Quad + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1150463984 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1150463983} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -2.14, y: 0, z: 3.84} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 692811816} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &1150463986 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1150463983} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: d11ad24ddc889ee4c9067711d2b431a4, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &1150463987 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1150463983} + m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1244590621 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1244590625} + - component: {fileID: 1244590624} + - component: {fileID: 1244590623} + - component: {fileID: 1244590622} + m_Layer: 0 + m_Name: Capsule + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!136 &1244590622 +CapsuleCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1244590621} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + m_Radius: 0.5 + m_Height: 2 + m_Direction: 1 + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1244590623 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1244590621} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &1244590624 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1244590621} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1244590625 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1244590621} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0.63, y: 0, z: 4.6} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 692811816} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1579373802 GameObject: m_ObjectHideFlags: 0 @@ -432,7 +607,7 @@ MeshRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 2100000, guid: fba6bd852efa3814e9fa0e4788cc192d, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -539,8 +714,11 @@ MonoBehaviour: _outlineColor: {r: 0, g: 1, b: 0, a: 1} _outlineWidth: 15 _outlineIntensity: 2 + _outlineAlphaCutoff: 0.9 _outlineMode: 1 _layerMask: 0 + _cameraEvent: 15 + _targetCamera: {fileID: 0} _updateRenderers: 0 --- !u!23 &1789341923 MeshRenderer: diff --git a/Outline.Core/Assets/Examples/SimplePerCamera/TestImage.png b/Outline.Core/Assets/Examples/SimplePerCamera/TestImage.png new file mode 100644 index 0000000..b8bfe95 Binary files /dev/null and b/Outline.Core/Assets/Examples/SimplePerCamera/TestImage.png differ diff --git a/Outline.Core/Assets/Examples/SimplePerCamera/TestImage.png.meta b/Outline.Core/Assets/Examples/SimplePerCamera/TestImage.png.meta new file mode 100644 index 0000000..4f4ccee --- /dev/null +++ b/Outline.Core/Assets/Examples/SimplePerCamera/TestImage.png.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 0ff6a32c354a4bd40a270e2d1e39335f +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + wrapU: -1 + wrapV: -1 + wrapW: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - serializedVersion: 2 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Outline.Core/Assets/Examples/SimplePerCamera/TestOutlineLayers.asset b/Outline.Core/Assets/Examples/SimplePerCamera/TestOutlineLayers.asset index 0c2023b..3707e2e 100644 --- a/Outline.Core/Assets/Examples/SimplePerCamera/TestOutlineLayers.asset +++ b/Outline.Core/Assets/Examples/SimplePerCamera/TestOutlineLayers.asset @@ -35,7 +35,7 @@ MonoBehaviour: _outlineColor: {r: 1, g: 0, b: 1, a: 1} _outlineWidth: 4 _outlineIntensity: 2 - _outlineMode: 0 + _outlineMode: 4 _name: The best layer _enabled: 1 _layerMask: 2 diff --git a/Outline.Core/Assets/Examples/SimplePerCamera/Transparent.mat b/Outline.Core/Assets/Examples/SimplePerCamera/Transparent.mat new file mode 100644 index 0000000..0bfc481 --- /dev/null +++ b/Outline.Core/Assets/Examples/SimplePerCamera/Transparent.mat @@ -0,0 +1,78 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Transparent + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: _ALPHAPREMULTIPLY_ON + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: 3000 + stringTagMap: + RenderType: Transparent + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 10 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 3 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 0 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 0.5176471} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/Outline.Core/Assets/Examples/SimplePerCamera/Transparent.mat.meta b/Outline.Core/Assets/Examples/SimplePerCamera/Transparent.mat.meta new file mode 100644 index 0000000..6bf95c8 --- /dev/null +++ b/Outline.Core/Assets/Examples/SimplePerCamera/Transparent.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fba6bd852efa3814e9fa0e4788cc192d +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Outline.Core/Assets/Examples/SimplePerObject/TestOutlineSettings.asset b/Outline.Core/Assets/Examples/SimplePerObject/TestOutlineSettings.asset index a32e433..503a07e 100644 --- a/Outline.Core/Assets/Examples/SimplePerObject/TestOutlineSettings.asset +++ b/Outline.Core/Assets/Examples/SimplePerObject/TestOutlineSettings.asset @@ -15,4 +15,5 @@ MonoBehaviour: _outlineColor: {r: 1, g: 0, b: 0, a: 1} _outlineWidth: 5 _outlineIntensity: 1 + _outlineAlphaCutoff: 0.9 _outlineMode: 0 diff --git a/Outline.Core/Packages/UnityFx.Outline/CHANGELOG.md b/Outline.Core/Packages/UnityFx.Outline/CHANGELOG.md index 0460817..7254ca7 100644 --- a/Outline.Core/Packages/UnityFx.Outline/CHANGELOG.md +++ b/Outline.Core/Packages/UnityFx.Outline/CHANGELOG.md @@ -3,6 +3,25 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/); this project adheres to [Semantic Versioning](http://semver.org/). +## [0.8.1] - 2020.09.21 + +Alpha test support, bugfixes and misc improvements. + +### Added +- Added support for alpha-testing ([#10](https://github.com/Arvtesh/UnityFx.Outline/issues/10)). +- Added support for merging outline layer objects ([#12](https://github.com/Arvtesh/UnityFx.Outline/issues/12)). +- Added `RemoveGameObject` helper methof to `OutlineEffect` ([#15](https://github.com/Arvtesh/UnityFx.Outline/issues/15)). +- Added ability to customize render event in `OutlineBehaviour`. +- Added ability to render outlines to the specified camera only for `OutlineBehaviour`. +- Added warning for unsupported render pipelines for `OutlineBehaviour` and `OutlineEffect`. + +### Changed +- Misc inspector improvements. +- Changed default render event to `AfterSkybox`. + +### Fixed +- Fixed incorrect condition for selection of render method, which sometimes caused problems with outline rendering on mobiles ([#14](https://github.com/Arvtesh/UnityFx.Outline/issues/14)). + ## [0.8.0] - 2020.05.30 Major refactoring and bugfixes. diff --git a/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineBehaviourEditor.cs b/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineBehaviourEditor.cs index 17b32c6..9733658 100644 --- a/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineBehaviourEditor.cs +++ b/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineBehaviourEditor.cs @@ -6,6 +6,7 @@ using UnityEditorInternal; using UnityEditor.SceneManagement; using UnityEngine; +using UnityEngine.Rendering; namespace UnityFx.Outline { @@ -13,6 +14,7 @@ namespace UnityFx.Outline public class OutlineBehaviourEditor : Editor { private OutlineBehaviour _effect; + private SerializedProperty _settingsProp; private bool _debugOpened; private bool _renderersOpened; private bool _camerasOpened; @@ -20,6 +22,7 @@ public class OutlineBehaviourEditor : Editor private void OnEnable() { _effect = (OutlineBehaviour)target; + _settingsProp = serializedObject.FindProperty("_outlineSettings"); } public override void OnInspectorGUI() @@ -37,34 +40,25 @@ public override void OnInspectorGUI() _effect.IgnoreLayerMask = mask; } - var obj = (OutlineSettings)EditorGUILayout.ObjectField("Outline Settings", _effect.OutlineSettings, typeof(OutlineSettings), true); + var e = (CameraEvent)EditorGUILayout.EnumPopup("Render Event", _effect.RenderEvent); - if (_effect.OutlineSettings != obj) + if (e != _effect.RenderEvent) { - Undo.RecordObject(_effect, "Set Settings"); - _effect.OutlineSettings = obj; + Undo.RecordObject(_effect, "Set Render Event"); + _effect.RenderEvent = e; } - if (obj) - { - EditorGUI.BeginDisabledGroup(true); - EditorGUI.indentLevel += 1; + var c = (Camera)EditorGUILayout.ObjectField("Target Camera", _effect.Camera, typeof(Camera), true); - OutlineEditorUtility.Render(_effect, _effect); - - EditorGUILayout.HelpBox(string.Format("Outline settings are overriden with values from {0}.", obj.name), MessageType.Info, true); - EditorGUI.indentLevel -= 1; - EditorGUI.EndDisabledGroup(); - } - else + if (c != _effect.Camera) { - EditorGUI.indentLevel += 1; - - OutlineEditorUtility.Render(_effect, _effect); - - EditorGUI.indentLevel -= 1; + Undo.RecordObject(_effect, "Set Target Camera"); + _effect.Camera = c; } + EditorGUILayout.PropertyField(_settingsProp); + serializedObject.ApplyModifiedProperties(); + if (EditorGUI.EndChangeCheck()) { EditorUtility.SetDirty(_effect.gameObject); diff --git a/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineEditorUtility.cs b/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineEditorUtility.cs index 2b8bf4c..004f2ec 100644 --- a/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineEditorUtility.cs +++ b/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineEditorUtility.cs @@ -11,56 +11,6 @@ namespace UnityFx.Outline { public static class OutlineEditorUtility { - public static void RenderDivider(Color color, int thickness = 1, int padding = 5) - { - var r = EditorGUILayout.GetControlRect(GUILayout.Height(padding + thickness)); - - r.height = thickness; - r.y += padding / 2; - r.x -= 2; - - EditorGUI.DrawRect(r, color); - } - - public static void Render(IOutlineSettings settings, UnityEngine.Object undoContext) - { - var color = EditorGUILayout.ColorField("Color", settings.OutlineColor); - - if (settings.OutlineColor != color) - { - Undo.RecordObject(undoContext, "Color"); - settings.OutlineColor = color; - } - - var width = EditorGUILayout.IntSlider("Width", settings.OutlineWidth, OutlineResources.MinWidth, OutlineResources.MaxWidth); - - if (settings.OutlineWidth != width) - { - Undo.RecordObject(undoContext, "Width"); - settings.OutlineWidth = width; - } - - var prevRenderMode = settings.OutlineRenderMode; - var renderMode = (OutlineRenderFlags)EditorGUILayout.EnumFlagsField("Render Flags", prevRenderMode); - - if (renderMode != prevRenderMode) - { - Undo.RecordObject(undoContext, "Render Flags"); - settings.OutlineRenderMode = renderMode; - } - - if ((renderMode & OutlineRenderFlags.Blurred) != 0) - { - var i = EditorGUILayout.Slider("Blur Intensity", settings.OutlineIntensity, OutlineResources.MinIntensity, OutlineResources.MaxIntensity); - - if (!Mathf.Approximately(settings.OutlineIntensity, i)) - { - Undo.RecordObject(undoContext, "Blur Intensity"); - settings.OutlineIntensity = i; - } - } - } - public static void RenderPreview(OutlineLayer layer, int layerIndex, bool showObjects) { if (layer != null) diff --git a/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineLayerCollectionEditor.cs b/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineLayerCollectionEditor.cs index faa90bf..207abc6 100644 --- a/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineLayerCollectionEditor.cs +++ b/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineLayerCollectionEditor.cs @@ -38,11 +38,13 @@ public override void OnInspectorGUI() EditorGUI.BeginChangeCheck(); var mask = EditorGUILayout.MaskField("Ignore layers", _layers.IgnoreLayerMask, InternalEditorUtility.layers); + var merge = EditorGUILayout.Toggle("Merge Layer Objects", _layers.MergeLayerObjects); if (EditorGUI.EndChangeCheck()) { - Undo.RecordObject(_layers, "Set Layers"); + Undo.RecordObject(_layers, "Change layer collection"); _layers.IgnoreLayerMask = mask; + _layers.MergeLayerObjects = merge; } EditorGUILayout.Space(); @@ -93,6 +95,7 @@ private void OnDrawLayer(Rect rect, int index, bool isActive, bool isFocused) var width = layer.OutlineWidth; var renderMode = layer.OutlineRenderMode; var blurIntensity = layer.OutlineIntensity; + var alphaCutoff = layer.OutlineAlphaCutoff; EditorGUI.BeginChangeCheck(); @@ -135,6 +138,12 @@ private void OnDrawLayer(Rect rect, int index, bool isActive, bool isFocused) if ((renderMode & OutlineRenderFlags.Blurred) != 0) { blurIntensity = EditorGUI.Slider(new Rect(rect.x, y, rect.width, lineHeight), "Blur Intensity", blurIntensity, OutlineResources.MinIntensity, OutlineResources.MaxIntensity); + y += lineOffset; + } + + if ((renderMode & OutlineRenderFlags.EnableAlphaTesting) != 0) + { + alphaCutoff = EditorGUI.Slider(new Rect(rect.x, y, rect.width, lineHeight), "Alpha Cutoff", alphaCutoff, OutlineResources.MinAlphaCutoff, OutlineResources.MaxAlphaCutoff); } EditorGUI.EndDisabledGroup(); @@ -152,6 +161,7 @@ private void OnDrawLayer(Rect rect, int index, bool isActive, bool isFocused) layer.OutlineColor = color; layer.OutlineRenderMode = renderMode; layer.OutlineIntensity = blurIntensity; + layer.OutlineAlphaCutoff = alphaCutoff; } } @@ -169,6 +179,11 @@ private float OnGetElementHeight(int index) ++numberOfLines; } + if ((_layers[index].OutlineRenderMode & OutlineRenderFlags.EnableAlphaTesting) != 0) + { + ++numberOfLines; + } + return (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing) * numberOfLines + EditorGUIUtility.standardVerticalSpacing; } diff --git a/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineSettingsEditor.cs b/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineSettingsEditor.cs index efa43c4..2ccb069 100644 --- a/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineSettingsEditor.cs +++ b/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineSettingsEditor.cs @@ -1,33 +1,177 @@ -// Copyright (C) 2019-2020 Alexander Bogarsukov. All rights reserved. -// See the LICENSE.md file in the project root for more information. - -using System; -using System.Collections; -using System.Collections.Generic; -using UnityEditor; -using UnityEngine; - -namespace UnityFx.Outline -{ - [CustomEditor(typeof(OutlineSettings))] - public class OutlineSettingsEditor : Editor - { - private OutlineSettings _settings; - - public override void OnInspectorGUI() - { - EditorGUI.BeginChangeCheck(); - OutlineEditorUtility.Render(_settings, _settings); - - if (EditorGUI.EndChangeCheck()) - { - EditorUtility.SetDirty(_settings); - } - } - - private void OnEnable() - { - _settings = (OutlineSettings)target; - } - } -} +// Copyright (C) 2019-2020 Alexander Bogarsukov. All rights reserved. +// See the LICENSE.md file in the project root for more information. + +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace UnityFx.Outline +{ + [CustomEditor(typeof(OutlineSettings))] + public class OutlineSettingsEditor : Editor + { + private const string _layerMaskPropName = "_outlineLayerMask"; + private const string _settingsPropName = "_outlineSettings"; + private const string _colorPropName = "_outlineColor"; + private const string _widthPropName = "_outlineWidth"; + private const string _intensityPropName = "_outlineIntensity"; + private const string _cutoffPropName = "_outlineAlphaCutoff"; + private const string _renderModePropName = "_outlineMode"; + + private static readonly GUIContent _layerMaskContent = new GUIContent("Outline Layer Mask", OutlineResources.OutlineLayerMaskTooltip); + private static readonly GUIContent _colorContent = new GUIContent("Color", "Outline color."); + private static readonly GUIContent _widthContent = new GUIContent("Width", "Outline width in pixels."); + private static readonly GUIContent _renderModeContent = new GUIContent("Render Flags", "Outline render flags. Multiple values can be selected at the same time."); + private static readonly GUIContent _intensityContent = new GUIContent("Blur Intensity", "Outline intensity value. It is only usable for blurred outlines."); + private static readonly GUIContent _cutoffContent = new GUIContent("Alpha Cutoff", "Outline alpha cutoff value. It is only usable when alpha testing is enabled and the material doesn't have _Cutoff property."); + + public override void OnInspectorGUI() + { + base.OnInspectorGUI(); + + var colorProp = serializedObject.FindProperty(_colorPropName); + var widthProp = serializedObject.FindProperty(_widthPropName); + var intensityProp = serializedObject.FindProperty(_intensityPropName); + var cutoffProp = serializedObject.FindProperty(_cutoffPropName); + var renderModeProp = serializedObject.FindProperty(_renderModePropName); + var renderMode = (OutlineRenderFlags)renderModeProp.intValue; + + EditorGUILayout.PropertyField(colorProp, _colorContent); + EditorGUILayout.PropertyField(widthProp, _widthContent); + + //EditorGUILayout.PropertyField(renderModeProp, _renderModeContent); + renderModeProp.intValue = (int)(OutlineRenderFlags)EditorGUILayout.EnumFlagsField(_renderModeContent, renderMode); + + if ((renderMode & OutlineRenderFlags.Blurred) != 0) + { + EditorGUILayout.PropertyField(intensityProp, _intensityContent); + } + + if ((renderMode & OutlineRenderFlags.EnableAlphaTesting) != 0) + { + EditorGUILayout.PropertyField(cutoffProp, _cutoffContent); + } + + serializedObject.ApplyModifiedProperties(); + } + + internal static float GetSettingsInstanceHeight(SerializedProperty property) + { + var lineCy = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + var renderModeProp = property.FindPropertyRelative(_renderModePropName); + var renderMode = (OutlineRenderFlags)renderModeProp.intValue; + var result = lineCy * 4; + + if ((renderMode & OutlineRenderFlags.Blurred) != 0) + { + result += lineCy; + } + + if ((renderMode & OutlineRenderFlags.EnableAlphaTesting) != 0) + { + result += lineCy; + } + + return result; + } + + internal static float GetSettingsWithMaskHeight(SerializedProperty property) + { + var lineCy = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + var layerMaskProp = property.FindPropertyRelative(_layerMaskPropName); + + if (layerMaskProp.intValue != 0) + { + var renderModeProp = property.FindPropertyRelative(_renderModePropName); + var renderMode = (OutlineRenderFlags)renderModeProp.intValue; + var result = lineCy * 5; + + if ((renderMode & OutlineRenderFlags.Blurred) != 0) + { + result += lineCy; + } + + if ((renderMode & OutlineRenderFlags.EnableAlphaTesting) != 0) + { + result += lineCy; + } + + return result; + } + + return lineCy; + } + + internal static void DrawSettingsInstance(Rect rc, SerializedProperty property) + { + var settingsProp = property.FindPropertyRelative(_settingsPropName); + + EditorGUI.PropertyField(new Rect(rc.x, rc.y, rc.width, EditorGUIUtility.singleLineHeight), settingsProp); + EditorGUI.indentLevel += 1; + + if (settingsProp.objectReferenceValue) + { + var obj = new SerializedObject(settingsProp.objectReferenceValue); + var colorProp = obj.FindProperty(_colorPropName); + var widthProp = obj.FindProperty(_widthPropName); + var intensityProp = obj.FindProperty(_intensityPropName); + var cutoffProp = obj.FindProperty(_cutoffPropName); + var renderModeProp = obj.FindProperty(_renderModePropName); + + EditorGUI.BeginDisabledGroup(true); + DrawSettingsInternal(rc, colorProp, widthProp, intensityProp, cutoffProp, renderModeProp); + EditorGUI.EndDisabledGroup(); + } + else + { + var colorProp = property.FindPropertyRelative(_colorPropName); + var widthProp = property.FindPropertyRelative(_widthPropName); + var intensityProp = property.FindPropertyRelative(_intensityPropName); + var cutoffProp = property.FindPropertyRelative(_cutoffPropName); + var renderModeProp = property.FindPropertyRelative(_renderModePropName); + + DrawSettingsInternal(rc, colorProp, widthProp, intensityProp, cutoffProp, renderModeProp); + } + + EditorGUI.indentLevel -= 1; + } + + internal static void DrawSettingsWithMask(Rect rc, SerializedProperty property) + { + var layerMaskProp = property.FindPropertyRelative(_layerMaskPropName); + + EditorGUI.PropertyField(new Rect(rc.x, rc.y, rc.width, EditorGUIUtility.singleLineHeight), layerMaskProp, _layerMaskContent); + + if (layerMaskProp.intValue != 0) + { + var lineCy = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + DrawSettingsInstance(new Rect(rc.x, rc.y + lineCy, rc.width, rc.height - lineCy), property); + } + } + + private static void DrawSettingsInternal(Rect rc, SerializedProperty colorProp, SerializedProperty widthProp, SerializedProperty intensityProp, SerializedProperty cutoffProp, SerializedProperty renderModeProp) + { + var renderMode = (OutlineRenderFlags)renderModeProp.intValue; + var lineCy = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + var n = 4; + + EditorGUI.PropertyField(new Rect(rc.x, rc.y + 1 * lineCy, rc.width, EditorGUIUtility.singleLineHeight), colorProp, _colorContent); + EditorGUI.PropertyField(new Rect(rc.x, rc.y + 2 * lineCy, rc.width, EditorGUIUtility.singleLineHeight), widthProp, _widthContent); + + // NOTE: EditorGUI.PropertyField doesn't allow multi-selection, have to use EnumFlagsField explixitly. + renderModeProp.intValue = (int)(OutlineRenderFlags)EditorGUI.EnumFlagsField(new Rect(rc.x, rc.y + 3 * lineCy, rc.width, EditorGUIUtility.singleLineHeight), _renderModeContent, renderMode); + + if ((renderMode & OutlineRenderFlags.Blurred) != 0) + { + EditorGUI.PropertyField(new Rect(rc.x, rc.y + n++ * lineCy, rc.width, EditorGUIUtility.singleLineHeight), intensityProp, _intensityContent); + } + + if ((renderMode & OutlineRenderFlags.EnableAlphaTesting) != 0) + { + EditorGUI.PropertyField(new Rect(rc.x, rc.y + n * lineCy, rc.width, EditorGUIUtility.singleLineHeight), cutoffProp, _cutoffContent); + } + } + } +} diff --git a/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineSettingsInstanceDrawer.cs b/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineSettingsInstanceDrawer.cs new file mode 100644 index 0000000..a6d6992 --- /dev/null +++ b/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineSettingsInstanceDrawer.cs @@ -0,0 +1,27 @@ +// Copyright (C) 2019-2020 Alexander Bogarsukov. All rights reserved. +// See the LICENSE.md file in the project root for more information. + +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace UnityFx.Outline +{ + [CustomPropertyDrawer(typeof(OutlineSettingsInstance))] + public class OutlineSettingsInstanceDrawer : PropertyDrawer + { + public override void OnGUI(Rect rc, SerializedProperty property, GUIContent label) + { + EditorGUI.BeginProperty(rc, label, property); + OutlineSettingsEditor.DrawSettingsInstance(rc, property); + EditorGUI.EndProperty(); + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + return OutlineSettingsEditor.GetSettingsInstanceHeight(property); + } + } +} diff --git a/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineSettingsInstanceDrawer.cs.meta b/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineSettingsInstanceDrawer.cs.meta new file mode 100644 index 0000000..99ae287 --- /dev/null +++ b/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineSettingsInstanceDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9a7d070135166b04783f98841111c742 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineSettingsWithLayerMaskDrawer.cs b/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineSettingsWithLayerMaskDrawer.cs new file mode 100644 index 0000000..31cea7e --- /dev/null +++ b/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineSettingsWithLayerMaskDrawer.cs @@ -0,0 +1,27 @@ +// Copyright (C) 2019-2020 Alexander Bogarsukov. All rights reserved. +// See the LICENSE.md file in the project root for more information. + +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace UnityFx.Outline +{ + [CustomPropertyDrawer(typeof(OutlineSettingsWithLayerMask))] + public class OutlineSettingsWithLayerMaskDrawer : PropertyDrawer + { + public override void OnGUI(Rect rc, SerializedProperty property, GUIContent label) + { + EditorGUI.BeginProperty(rc, label, property); + OutlineSettingsEditor.DrawSettingsWithMask(rc, property); + EditorGUI.EndProperty(); + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) + { + return OutlineSettingsEditor.GetSettingsWithMaskHeight(property); + } + } +} diff --git a/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineSettingsWithLayerMaskDrawer.cs.meta b/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineSettingsWithLayerMaskDrawer.cs.meta new file mode 100644 index 0000000..358c115 --- /dev/null +++ b/Outline.Core/Packages/UnityFx.Outline/Editor/Scripts/OutlineSettingsWithLayerMaskDrawer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5558cd049bcb613438115d59a4376272 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Outline.Core/Packages/UnityFx.Outline/Runtime/Prefabs/OutlineResources.asset b/Outline.Core/Packages/UnityFx.Outline/Runtime/Prefabs/OutlineResources.asset index 285880e..b00ab91 100644 --- a/Outline.Core/Packages/UnityFx.Outline/Runtime/Prefabs/OutlineResources.asset +++ b/Outline.Core/Packages/UnityFx.Outline/Runtime/Prefabs/OutlineResources.asset @@ -12,5 +12,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: b503341e0a514e3489c4851727e68257, type: 3} m_Name: OutlineResources m_EditorClassIdentifier: - RenderShader: {fileID: 4800000, guid: ac20fbf75bafe454aba5ef3c098349df, type: 3} - OutlineShader: {fileID: 4800000, guid: 41c9acbf41c8245498ac9beab378de12, type: 3} + _renderShader: {fileID: 4800000, guid: ac20fbf75bafe454aba5ef3c098349df, type: 3} + _outlineShader: {fileID: 4800000, guid: 41c9acbf41c8245498ac9beab378de12, type: 3} + _enableInstancing: 0 diff --git a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/IOutlineSettings.cs b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/IOutlineSettings.cs index c822837..e2f517d 100644 --- a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/IOutlineSettings.cs +++ b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/IOutlineSettings.cs @@ -27,13 +27,19 @@ public interface IOutlineSettings : IEquatable /// /// Gets or sets outline intensity value. Allowed range is [, ]. - /// This is used for blurred oulines only (i.e. is set to ). + /// This is used for blurred oulines only (i.e. has flag). /// /// /// /// float OutlineIntensity { get; set; } + /// + /// Gets or sets alpha cutoff value. Allowed range is [0, 1]. This is used only when has flag. + /// + /// + float OutlineAlphaCutoff { get; set; } + /// /// Gets or sets outline render mode. /// diff --git a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineBehaviour.cs b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineBehaviour.cs index 96daed1..8a1622e 100644 --- a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineBehaviour.cs +++ b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineBehaviour.cs @@ -27,6 +27,10 @@ public sealed class OutlineBehaviour : MonoBehaviour, IOutlineSettings private OutlineSettingsInstance _outlineSettings; [SerializeField, HideInInspector] private int _layerMask; + [SerializeField, HideInInspector] + private CameraEvent _cameraEvent = OutlineRenderer.RenderEvent; + [SerializeField, HideInInspector] + private Camera _targetCamera; [SerializeField, Tooltip("If set, list of object renderers is updated on each frame. Enable if the object has child renderers which are enabled/disabled frequently.")] private bool _updateRenderers; @@ -107,6 +111,33 @@ public int IgnoreLayerMask } } + /// + /// Gets or sets used to render the outlines. + /// + public CameraEvent RenderEvent + { + get + { + return _cameraEvent; + } + set + { + if (_cameraEvent != value) + { + foreach (var kvp in _cameraMap) + { + if (kvp.Key) + { + kvp.Key.RemoveCommandBuffer(_cameraEvent, kvp.Value); + kvp.Key.AddCommandBuffer(value, kvp.Value); + } + } + + _cameraEvent = value; + } + } + } + /// /// Gets outline renderers. By default all child components are used for outlining. /// @@ -120,9 +151,50 @@ public ICollection OutlineRenderers } } + /// + /// Gets or sets camera to render outlines to. If not set, outlines are rendered to all active cameras. + /// + /// + public Camera Camera + { + get + { + return _targetCamera; + } + set + { + if (_targetCamera != value) + { + if (value) + { + _camerasToRemove.Clear(); + + foreach (var kvp in _cameraMap) + { + if (kvp.Key && kvp.Key != value) + { + kvp.Key.RemoveCommandBuffer(_cameraEvent, kvp.Value); + kvp.Value.Dispose(); + + _camerasToRemove.Add(kvp.Key); + } + } + + foreach (var camera in _camerasToRemove) + { + _cameraMap.Remove(camera); + } + } + + _targetCamera = value; + } + } + } + /// /// Gets all cameras outline data is rendered to. /// + /// public ICollection Cameras => _cameraMap.Keys; /// @@ -140,6 +212,15 @@ public void UpdateRenderers() private void Awake() { + if (GraphicsSettings.renderPipelineAsset) + { + Debug.LogWarningFormat(this, OutlineResources.SrpNotSupported, GetType().Name); + } + +#if UNITY_POST_PROCESSING_STACK_V2 + Debug.LogWarningFormat(this, OutlineResources.PpNotSupported, GetType().Name); +#endif + CreateRenderersIfNeeded(); CreateSettingsIfNeeded(); } @@ -157,7 +238,7 @@ private void OnDisable() { if (kvp.Key) { - kvp.Key.RemoveCommandBuffer(OutlineRenderer.RenderEvent, kvp.Value); + kvp.Key.RemoveCommandBuffer(_cameraEvent, kvp.Value); } kvp.Value.Dispose(); @@ -275,6 +356,21 @@ public float OutlineIntensity } } + /// + public float OutlineAlphaCutoff + { + get + { + CreateSettingsIfNeeded(); + return _outlineSettings.OutlineAlphaCutoff; + } + set + { + CreateSettingsIfNeeded(); + _outlineSettings.OutlineAlphaCutoff = value; + } + } + /// public OutlineRenderFlags OutlineRenderMode { @@ -306,7 +402,7 @@ public bool Equals(IOutlineSettings other) private void OnCameraPreRender(Camera camera) { - if (camera) + if (camera && (!_targetCamera || _targetCamera == camera)) { if (_outlineSettings.RequiresCameraDepth) { @@ -317,7 +413,7 @@ private void OnCameraPreRender(Camera camera) { var cmdBuf = new CommandBuffer(); cmdBuf.name = string.Format("{0} - {1}", GetType().Name, name); - camera.AddCommandBuffer(OutlineRenderer.RenderEvent, cmdBuf); + camera.AddCommandBuffer(_cameraEvent, cmdBuf); _cameraMap.Add(camera, cmdBuf); } diff --git a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineEffect.cs b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineEffect.cs index 0150be3..7de6c04 100644 --- a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineEffect.cs +++ b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineEffect.cs @@ -136,6 +136,18 @@ public void AddGameObject(GameObject go, int layerIndex) _outlineLayers[layerIndex].Add(go); } + /// + /// Removes the specified from . + /// + /// A to remove. + public void RemoveGameObject(GameObject go) + { + if (_outlineLayers) + { + _outlineLayers.Remove(go); + } + } + /// /// Shares with another instance. /// @@ -156,6 +168,14 @@ public void ShareLayersWith(OutlineEffect other) private void Awake() { + if (GraphicsSettings.renderPipelineAsset) + { + Debug.LogWarningFormat(this, OutlineResources.SrpNotSupported, GetType().Name); + } + +#if UNITY_POST_PROCESSING_STACK_V2 + Debug.LogWarningFormat(this, OutlineResources.PpNotSupported, GetType().Name); +#endif } private void OnEnable() diff --git a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineLayer.cs b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineLayer.cs index d3d71da..ea9264c 100644 --- a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineLayer.cs +++ b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineLayer.cs @@ -29,6 +29,7 @@ public sealed class OutlineLayer : ICollection, IReadOnlyCollection< private OutlineLayerCollection _parentCollection; private Dictionary _outlineObjects = new Dictionary(); + private List _mergedRenderers; #endregion @@ -183,12 +184,49 @@ public void GetRenderObjects(IList renderObjects) if (go && go.activeInHierarchy) { - renderObjects.Add(new OutlineRenderObject(go, kvp.Value.GetList(), _settings)); + renderObjects.Add(new OutlineRenderObject(kvp.Value.GetList(), _settings, go.name)); } } } } + /// + /// Gets all layer renderers. + /// + public IReadOnlyList GetRenderers() + { + if (_enabled) + { + if (_mergedRenderers != null) + { + _mergedRenderers.Clear(); + } + else + { + _mergedRenderers = new List(); + } + + foreach (var kvp in _outlineObjects) + { + var go = kvp.Key; + + if (go && go.activeInHierarchy) + { + var rl = kvp.Value.GetList(); + + for (var i = 0; i < rl.Count; i++) + { + _mergedRenderers.Add(rl[i]); + } + } + } + + return _mergedRenderers; + } + + return Array.Empty(); + } + #endregion #region internals @@ -275,6 +313,19 @@ public float OutlineIntensity } } + /// + public float OutlineAlphaCutoff + { + get + { + return _settings.OutlineAlphaCutoff; + } + set + { + _settings.OutlineAlphaCutoff = value; + } + } + /// public OutlineRenderFlags OutlineRenderMode { diff --git a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineLayerCollection.cs b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineLayerCollection.cs index a701654..160bb83 100644 --- a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineLayerCollection.cs +++ b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineLayerCollection.cs @@ -23,6 +23,8 @@ public sealed class OutlineLayerCollection : ScriptableObject, IList _layers = new List(); [SerializeField, HideInInspector] private int _layerMask; + [SerializeField, HideInInspector] + private bool _mergeLayerObjects; #endregion @@ -51,6 +53,21 @@ public int IgnoreLayerMask } } + /// + /// Gets or sets a value indicating whether layer game objects should be trated as one. + /// + public bool MergeLayerObjects + { + get + { + return _mergeLayerObjects; + } + set + { + _mergeLayerObjects = value; + } + } + /// /// Gets number of game objects in the layers. /// @@ -101,10 +118,35 @@ public OutlineLayer AddLayer() /// Gets the objects for rendering. /// public void GetRenderObjects(IList renderObjects) + { + if (_mergeLayerObjects) + { + foreach (var layer in _layers) + { + renderObjects.Add(new OutlineRenderObject(layer.GetRenderers(), layer, layer.Name)); + } + } + else + { + foreach (var layer in _layers) + { + layer.GetRenderObjects(renderObjects); + } + } + } + + /// + /// Removes the specified from layers. + /// + /// A to remove. + public void Remove(GameObject go) { foreach (var layer in _layers) { - layer.GetRenderObjects(renderObjects); + if (layer.Remove(go)) + { + break; + } } } diff --git a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineRenderFlags.cs b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineRenderFlags.cs index d180f46..2f94e9b 100644 --- a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineRenderFlags.cs +++ b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineRenderFlags.cs @@ -22,8 +22,13 @@ public enum OutlineRenderFlags Blurred = 1, /// - /// Enabled depth testing when rendering object outlines. Only visible parts of objects are outlined. + /// Enables depth testing when rendering object outlines. Only visible parts of objects are outlined. /// - EnableDepthTesting = 2 + EnableDepthTesting = 2, + + /// + /// Enabled alpha testing when rendering outlines. + /// + EnableAlphaTesting = 4 } } diff --git a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineRenderObject.cs b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineRenderObject.cs index fe7469a..6c5ab02 100644 --- a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineRenderObject.cs +++ b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineRenderObject.cs @@ -16,7 +16,7 @@ namespace UnityFx.Outline { #region data - private readonly GameObject _go; + private readonly string _tag; private readonly IReadOnlyList _renderers; private readonly IOutlineSettings _outlineSettings; @@ -25,9 +25,9 @@ namespace UnityFx.Outline #region interface /// - /// Gets the . + /// Gets the object tag name. /// - public GameObject Go => _go; + public string Tag => _tag; /// /// Gets renderers for the object. @@ -42,19 +42,11 @@ namespace UnityFx.Outline /// /// Initializes a new instance of the struct. /// - public OutlineRenderObject(GameObject go, IReadOnlyList renderers, IOutlineSettings outlineSettings) + public OutlineRenderObject(IReadOnlyList renderers, IOutlineSettings outlineSettings, string tag = null) { - _go = go; _renderers = renderers; _outlineSettings = outlineSettings; - } - - /// - /// Implicit convertino to . - /// - public static implicit operator GameObject(OutlineRenderObject o) - { - return o._go; + _tag = tag; } #endregion @@ -64,7 +56,7 @@ public static implicit operator GameObject(OutlineRenderObject o) /// public bool Equals(OutlineRenderObject other) { - return _go == other._go && _renderers == other._renderers && _outlineSettings == other._outlineSettings; + return string.CompareOrdinal(_tag, other._tag) == 0 && _renderers == other._renderers && _outlineSettings == other._outlineSettings; } #endregion diff --git a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineRenderer.cs b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineRenderer.cs index 6ac7e84..32d2510 100644 --- a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineRenderer.cs +++ b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineRenderer.cs @@ -36,8 +36,10 @@ namespace UnityFx.Outline { #region data - private const int _hPassId = 0; - private const int _vPassId = 1; + private const int _defaultRenderPassId = 0; + private const int _alphaTestRenderPassId = 1; + private const int _outlineHPassId = 0; + private const int _outlineVPassId = 1; private const RenderTextureFormat _rtFormat = RenderTextureFormat.R8; private static readonly int _maskRtId = Shader.PropertyToID("_MaskTex"); @@ -55,7 +57,7 @@ namespace UnityFx.Outline /// /// A default outline rendering should be assosiated with. /// - public const CameraEvent RenderEvent = CameraEvent.BeforeImageEffects; + public const CameraEvent RenderEvent = CameraEvent.AfterSkybox; /// /// Initializes a new instance of the struct. @@ -201,7 +203,7 @@ public OutlineRenderer(CommandBuffer cmd, OutlineResources resources, RenderTarg /// public void Render(OutlineRenderObject obj) { - Render(obj.Renderers, obj.OutlineSettings, obj.Go.name); + Render(obj.Renderers, obj.OutlineSettings, obj.Tag); } /// @@ -233,8 +235,10 @@ public void Render(IReadOnlyList renderers, IOutlineSettings settings, } _commandBuffer.BeginSample(sampleName); - RenderObject(settings, renderers); - RenderOutline(settings); + { + RenderObject(settings, renderers); + RenderOutline(settings); + } _commandBuffer.EndSample(sampleName); } } @@ -266,8 +270,10 @@ public void Render(Renderer renderer, IOutlineSettings settings, string sampleNa } _commandBuffer.BeginSample(sampleName); - RenderObject(settings, renderer); - RenderOutline(settings); + { + RenderObject(settings, renderer); + RenderOutline(settings); + } _commandBuffer.EndSample(sampleName); } @@ -319,45 +325,62 @@ private void RenderObjectClear(OutlineRenderFlags flags) _commandBuffer.ClearRenderTarget(false, true, Color.clear); } - private void RenderObject(IOutlineSettings settings, IReadOnlyList renderers) + private void DrawRenderer(Renderer renderer, IOutlineSettings settings) { - RenderObjectClear(settings.OutlineRenderMode); - - for (var i = 0; i < renderers.Count; ++i) + if (renderer && renderer.enabled && renderer.isVisible && renderer.gameObject.activeInHierarchy) { - var r = renderers[i]; + var alphaTest = (settings.OutlineRenderMode & OutlineRenderFlags.EnableAlphaTesting) != 0; - if (r && r.enabled && r.isVisible && r.gameObject.activeInHierarchy) - { - // NOTE: Accessing Renderer.sharedMaterials triggers GC.Alloc. That's why we use a temporary - // list of materials, cached with the outline resources. - r.GetSharedMaterials(_resources.TmpMaterials); + // NOTE: Accessing Renderer.sharedMaterials triggers GC.Alloc. That's why we use a temporary + // list of materials, cached with the outline resources. + renderer.GetSharedMaterials(_resources.TmpMaterials); - for (var j = 0; j < _resources.TmpMaterials.Count; ++j) + if (alphaTest) + { + for (var i = 0; i < _resources.TmpMaterials.Count; ++i) + { + var mat = _resources.TmpMaterials[i]; + + // Use material cutoff value if available. + if (mat.HasProperty(_resources.AlphaCutoffId)) + { + _commandBuffer.SetGlobalFloat(_resources.AlphaCutoffId, mat.GetFloat(_resources.AlphaCutoffId)); + } + else + { + _commandBuffer.SetGlobalFloat(_resources.AlphaCutoffId, settings.OutlineAlphaCutoff); + } + + _commandBuffer.SetGlobalTexture(_resources.MainTexId, _resources.TmpMaterials[i].mainTexture); + _commandBuffer.DrawRenderer(renderer, _resources.RenderMaterial, i, _alphaTestRenderPassId); + } + } + else + { + for (var i = 0; i < _resources.TmpMaterials.Count; ++i) { - _commandBuffer.DrawRenderer(r, _resources.RenderMaterial, j); + _commandBuffer.DrawRenderer(renderer, _resources.RenderMaterial, i, _defaultRenderPassId); } } } } - private void RenderObject(IOutlineSettings settings, Renderer renderer) + private void RenderObject(IOutlineSettings settings, IReadOnlyList renderers) { RenderObjectClear(settings.OutlineRenderMode); - if (renderer && renderer.enabled && renderer.isVisible && renderer.gameObject.activeInHierarchy) + for (var i = 0; i < renderers.Count; ++i) { - // NOTE: Accessing Renderer.sharedMaterials triggers GC.Alloc. That's why we use a temporary - // list of materials, cached with the outline resources. - renderer.GetSharedMaterials(_resources.TmpMaterials); - - for (var i = 0; i < _resources.TmpMaterials.Count; ++i) - { - _commandBuffer.DrawRenderer(renderer, _resources.RenderMaterial, i); - } + DrawRenderer(renderers[i], settings); } } + private void RenderObject(IOutlineSettings settings, Renderer renderer) + { + RenderObjectClear(settings.OutlineRenderMode); + DrawRenderer(renderer, settings); + } + private void RenderOutline(IOutlineSettings settings) { var mat = _resources.OutlineMaterial; @@ -367,11 +390,11 @@ private void RenderOutline(IOutlineSettings settings) // HPass _commandBuffer.SetRenderTarget(_hPassRtId, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store); - Blit(_commandBuffer, _maskRtId, _resources, _hPassId, mat, props); + Blit(_commandBuffer, _maskRtId, _resources, _outlineHPassId, mat, props); // VPassBlend _commandBuffer.SetRenderTarget(_rt, RenderBufferLoadAction.Load, RenderBufferStoreAction.Store); - Blit(_commandBuffer, _hPassRtId, _resources, _vPassId, mat, props); + Blit(_commandBuffer, _hPassRtId, _resources, _outlineVPassId, mat, props); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -380,13 +403,15 @@ private static void Blit(CommandBuffer cmdBuffer, RenderTargetIdentifier src, Ou // Set source texture as _MainTex to match Blit behavior. cmdBuffer.SetGlobalTexture(resources.MainTexId, src); - if (SystemInfo.graphicsShaderLevel < 35 || resources.UseFullscreenTriangleMesh) + // NOTE: SystemInfo.graphicsShaderLevel check is not enough sometimes (esp. on mobiles), so there is SystemInfo.supportsInstancing + // check and a flag for forcing DrawMesh. + if (SystemInfo.graphicsShaderLevel >= 35 && SystemInfo.supportsInstancing && !resources.UseFullscreenTriangleMesh) { - cmdBuffer.DrawMesh(resources.FullscreenTriangleMesh, Matrix4x4.identity, mat, 0, shaderPass, props); + cmdBuffer.DrawProcedural(Matrix4x4.identity, mat, shaderPass, MeshTopology.Triangles, 3, 1, props); } else { - cmdBuffer.DrawProcedural(Matrix4x4.identity, mat, shaderPass, MeshTopology.Triangles, 3, 1, props); + cmdBuffer.DrawMesh(resources.FullscreenTriangleMesh, Matrix4x4.identity, mat, 0, shaderPass, props); } } diff --git a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineResources.cs b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineResources.cs index 3431e1f..cc39ab0 100644 --- a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineResources.cs +++ b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineResources.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using UnityEditor; using UnityEngine; namespace UnityFx.Outline @@ -16,6 +17,13 @@ public sealed class OutlineResources : ScriptableObject { #region data + [SerializeField] + private Shader _renderShader; + [SerializeField] + private Shader _outlineShader; + [SerializeField] + private bool _enableInstancing; + private Material _renderMaterial; private Material _outlineMaterial; private MaterialPropertyBlock _props; @@ -60,6 +68,18 @@ public sealed class OutlineResources : ScriptableObject /// public const int SolidIntensity = 100; + /// + /// Minimum value of outline alpha cutoff parameter. + /// + /// + public const float MinAlphaCutoff = 0; + + /// + /// Maximum value of outline alpha cutoff parameter. + /// + /// + public const float MaxAlphaCutoff = 1; + /// /// Name of _MainTex shader parameter. /// @@ -80,6 +100,11 @@ public sealed class OutlineResources : ScriptableObject /// public const string IntensityName = "_Intensity"; + /// + /// Name of _Cutoff shader parameter. + /// + public const string AlphaCutoffName = "_Cutoff"; + /// /// Name of _GaussSamples shader parameter. /// @@ -105,6 +130,21 @@ public sealed class OutlineResources : ScriptableObject /// public const string OutlineLayerCollectionTooltip = "Collection of outline layers to use. This can be used to share outline settings between multiple cameras."; + /// + /// Tooltip text for outline field. + /// + public const string OutlineLayerMaskTooltip = "Layer mask for outined objects."; + + /// + /// SRP not supported message. + /// + internal const string SrpNotSupported = "{0} works with built-in render pipeline only. It does not support SRP (including URP and HDRP)."; + + /// + /// Post-processing not supported message. + /// + internal const string PpNotSupported = "{0} does not support Unity Post-processing stack v2. It might not work as expected."; + /// /// Hashed name of _MainTex shader parameter. /// @@ -125,6 +165,11 @@ public sealed class OutlineResources : ScriptableObject /// public readonly int IntensityId = Shader.PropertyToID(IntensityName); + /// + /// Hashed name of _Cutoff shader parameter. + /// + public readonly int AlphaCutoffId = Shader.PropertyToID(AlphaCutoffName); + /// /// Hashed name of _GaussSamples shader parameter. /// @@ -138,12 +183,24 @@ public sealed class OutlineResources : ScriptableObject /// /// Gets or sets a that renders objects outlined with a solid while color. /// - public Shader RenderShader; + public Shader RenderShader + { + get + { + return _renderShader; + } + } /// /// Gets or sets a that renders outline around the mask, that was generated with . /// - public Shader OutlineShader; + public Shader OutlineShader + { + get + { + return _outlineShader; + } + } /// /// Gets a -based material. @@ -157,7 +214,8 @@ public Material RenderMaterial _renderMaterial = new Material(RenderShader) { name = "Outline - RenderColor", - hideFlags = HideFlags.HideAndDontSave + hideFlags = HideFlags.HideAndDontSave, + enableInstancing = _enableInstancing }; } @@ -177,7 +235,8 @@ public Material OutlineMaterial _outlineMaterial = new Material(OutlineShader) { name = "Outline - Main", - hideFlags = HideFlags.HideAndDontSave + hideFlags = HideFlags.HideAndDontSave, + enableInstancing = _enableInstancing }; if (_useDrawMesh) @@ -270,6 +329,31 @@ public bool UseFullscreenTriangleMesh } } + /// + /// Gets or sets a value indicating whether instancing is enabled. + /// + public bool EnableInstancing + { + get + { + return _enableInstancing; + } + set + { + _enableInstancing = value; + + if (_renderMaterial) + { + _renderMaterial.enableInstancing = value; + } + + if (_outlineMaterial) + { + _outlineMaterial.enableInstancing = value; + } + } + } + /// /// Gets a value indicating whether the instance is in valid state. /// @@ -325,8 +409,8 @@ public float[] GetGaussSamples(int width) /// public void ResetToDefaults() { - RenderShader = Shader.Find("Hidden/UnityFx/OutlineColor"); - OutlineShader = Shader.Find("Hidden/UnityFx/Outline"); + _renderShader = Shader.Find("Hidden/UnityFx/OutlineColor"); + _outlineShader = Shader.Find("Hidden/UnityFx/Outline"); } /// diff --git a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineResources.cs.meta b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineResources.cs.meta index 0f1aca3..ce5153a 100644 --- a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineResources.cs.meta +++ b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineResources.cs.meta @@ -4,9 +4,8 @@ MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: - - RenderShader: {fileID: 4800000, guid: ac20fbf75bafe454aba5ef3c098349df, type: 3} - - HPassShader: {fileID: 4800000, guid: 41c9acbf41c8245498ac9beab378de12, type: 3} - - VPassBlendShader: {fileID: 4800000, guid: 1df0cb1700e142f4ca3b28297d3957da, type: 3} + - _renderShader: {fileID: 4800000, guid: ac20fbf75bafe454aba5ef3c098349df, type: 3} + - _outlineShader: {fileID: 4800000, guid: 41c9acbf41c8245498ac9beab378de12, type: 3} executionOrder: 0 icon: {instanceID: 0} userData: diff --git a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineSettings.cs b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineSettings.cs index 1b6fe4b..6e4c4b1 100644 --- a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineSettings.cs +++ b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineSettings.cs @@ -17,10 +17,12 @@ public sealed class OutlineSettings : ScriptableObject, IOutlineSettings // NOTE: There is a custom editor for OutlineSettings, so no need to show these in default inspector. [SerializeField, HideInInspector] private Color _outlineColor = Color.red; - [SerializeField, HideInInspector] + [SerializeField, HideInInspector, Range(OutlineResources.MinWidth, OutlineResources.MaxWidth)] private int _outlineWidth = 4; - [SerializeField, HideInInspector] + [SerializeField, HideInInspector, Range(OutlineResources.MinIntensity, OutlineResources.MaxIntensity)] private float _outlineIntensity = 2; + [SerializeField, HideInInspector, Range(OutlineResources.MinAlphaCutoff, OutlineResources.MaxAlphaCutoff)] + private float _outlineAlphaCutoff = 0.9f; [SerializeField, HideInInspector] private OutlineRenderFlags _outlineMode; @@ -38,7 +40,8 @@ public static bool Equals(IOutlineSettings lhs, IOutlineSettings rhs) return lhs.OutlineColor == rhs.OutlineColor && lhs.OutlineWidth == rhs.OutlineWidth && lhs.OutlineRenderMode == rhs.OutlineRenderMode && - Mathf.Approximately(lhs.OutlineIntensity, rhs.OutlineIntensity); + Mathf.Approximately(lhs.OutlineIntensity, rhs.OutlineIntensity) && + Mathf.Approximately(lhs.OutlineAlphaCutoff, rhs.OutlineAlphaCutoff); } #endregion @@ -84,6 +87,19 @@ public float OutlineIntensity } } + /// + public float OutlineAlphaCutoff + { + get + { + return _outlineAlphaCutoff; + } + set + { + _outlineAlphaCutoff = Mathf.Clamp(value, 0, 1); + } + } + /// public OutlineRenderFlags OutlineRenderMode { diff --git a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineSettingsInstance.cs b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineSettingsInstance.cs index b887cdd..656faef 100644 --- a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineSettingsInstance.cs +++ b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineSettingsInstance.cs @@ -18,10 +18,12 @@ internal class OutlineSettingsInstance : IOutlineSettings private OutlineSettings _outlineSettings; [SerializeField, HideInInspector] private Color _outlineColor = Color.red; - [SerializeField, HideInInspector] + [SerializeField, HideInInspector, Range(OutlineResources.MinWidth, OutlineResources.MaxWidth)] private int _outlineWidth = 4; - [SerializeField, HideInInspector] + [SerializeField, HideInInspector, Range(OutlineResources.MinIntensity, OutlineResources.MaxIntensity)] private float _outlineIntensity = 2; + [SerializeField, HideInInspector, Range(OutlineResources.MinAlphaCutoff, OutlineResources.MaxAlphaCutoff)] + private float _outlineAlphaCutoff = 0.9f; [SerializeField, HideInInspector] private OutlineRenderFlags _outlineMode; @@ -94,6 +96,19 @@ public float OutlineIntensity } } + /// + public float OutlineAlphaCutoff + { + get + { + return _outlineSettings is null ? _outlineAlphaCutoff : _outlineSettings.OutlineAlphaCutoff; + } + set + { + _outlineAlphaCutoff = Mathf.Clamp(value, 0, 1); + } + } + /// public OutlineRenderFlags OutlineRenderMode { diff --git a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineSettingsWithLayerMask.cs b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineSettingsWithLayerMask.cs new file mode 100644 index 0000000..1336ffc --- /dev/null +++ b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineSettingsWithLayerMask.cs @@ -0,0 +1,33 @@ +// Copyright (C) 2019-2020 Alexander Bogarsukov. All rights reserved. +// See the LICENSE.md file in the project root for more information. + +using System; +using UnityEngine; + +namespace UnityFx.Outline +{ + [Serializable] + internal class OutlineSettingsWithLayerMask : OutlineSettingsInstance + { + #region data + +#pragma warning disable 0649 + + // NOTE: There are custom editors for public components, so no need to show these in default inspector. + [SerializeField, HideInInspector] + private LayerMask _outlineLayerMask; + +#pragma warning restore 0649 + + #endregion + + #region interface + + public int OutlineLayerMask => _outlineLayerMask; + + #endregion + + #region implementation + #endregion + } +} diff --git a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineSettingsWithLayerMask.cs.meta b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineSettingsWithLayerMask.cs.meta new file mode 100644 index 0000000..86fb5b9 --- /dev/null +++ b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/OutlineSettingsWithLayerMask.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9f6645ede9c6d2346b6aee185f8261d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/Properties/AssemblyInfo.cs b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/Properties/AssemblyInfo.cs index d7b9f2c..78ea24b 100644 --- a/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/Properties/AssemblyInfo.cs +++ b/Outline.Core/Packages/UnityFx.Outline/Runtime/Scripts/Properties/AssemblyInfo.cs @@ -27,3 +27,4 @@ // Make internals visible to the editor assembly. [assembly: InternalsVisibleTo("UnityFx.Outline.Editor")] +[assembly: InternalsVisibleTo("UnityFx.Outline.URP")] diff --git a/Outline.Core/Packages/UnityFx.Outline/Runtime/Shaders/Outline.shader b/Outline.Core/Packages/UnityFx.Outline/Runtime/Shaders/Outline.shader index ec2be87..3862749 100644 --- a/Outline.Core/Packages/UnityFx.Outline/Runtime/Shaders/Outline.shader +++ b/Outline.Core/Packages/UnityFx.Outline/Runtime/Shaders/Outline.shader @@ -5,13 +5,6 @@ // Modified version of 'Custom/Post Outline' shader taken from https://willweissman.wordpress.com/tutorials/shaders/unity-shaderlab-object-outlines/. Shader "Hidden/UnityFx/Outline" { - Properties - { - _Width("Outline thickness (in pixels)", Range(1, 32)) = 5 - _Intensity("Outline intensity", Range(0.1, 100)) = 2 - _Color("Outline color", Color) = (1, 0, 0, 1) - } - HLSLINCLUDE #include "UnityCG.cginc" @@ -32,8 +25,9 @@ Shader "Hidden/UnityFx/Outline" v2f_img vert(appdata_img v) { v2f_img o; - UNITY_INITIALIZE_OUTPUT(v2f_img, o); UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_OUTPUT(v2f_img, o); + UNITY_TRANSFER_INSTANCE_ID(v, o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); o.pos = float4(v.vertex.xy, UNITY_NEAR_CLIP_VALUE, 1); @@ -50,7 +44,7 @@ Shader "Hidden/UnityFx/Outline" UNITY_VERTEX_INPUT_INSTANCE_ID }; - float4 GetFullScreenTriangleVertexPosition(uint vertexID, float z) + float4 GetFullScreenTriangleVertexPosition(uint vertexID, float z = UNITY_NEAR_CLIP_VALUE) { // Generates a triangle in homogeneous clip space, s.t. // v0 = (-1, -1, 1), v1 = (3, -1, 1), v2 = (-1, 3, 1). @@ -61,11 +55,12 @@ Shader "Hidden/UnityFx/Outline" v2f_img vert(appdata_vid v) { v2f_img o; - UNITY_INITIALIZE_OUTPUT(v2f_img, o); UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_OUTPUT(v2f_img, o); + UNITY_TRANSFER_INSTANCE_ID(v, o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); - o.pos = GetFullScreenTriangleVertexPosition(v.vertexID, UNITY_NEAR_CLIP_VALUE); + o.pos = GetFullScreenTriangleVertexPosition(v.vertexID); o.uv = ComputeScreenPos(o.pos); return o; @@ -89,12 +84,16 @@ Shader "Hidden/UnityFx/Outline" float4 frag_h(v2f_img i) : SV_Target { + UNITY_SETUP_INSTANCE_ID(i); + float intensity = CalcIntensity(i.uv, float2(_MainTex_TexelSize.x, 0)); return float4(intensity, intensity, intensity, 1); } float4 frag_v(v2f_img i) : SV_Target { + UNITY_SETUP_INSTANCE_ID(i); + if (UNITY_SAMPLE_SCREENSPACE_TEXTURE(_MaskTex, i.uv).r > 0) { // TODO: Avoid discard/clip to improve performance on mobiles. @@ -124,6 +123,7 @@ Shader "Hidden/UnityFx/Outline" HLSLPROGRAM #pragma target 3.5 + #pragma multi_compile_instancing #pragma shader_feature_local _USE_DRAWMESH #pragma vertex vert #pragma fragment frag_h @@ -140,6 +140,7 @@ Shader "Hidden/UnityFx/Outline" HLSLPROGRAM #pragma target 3.5 + #pragma multi_compile_instancing #pragma shader_feature_local _USE_DRAWMESH #pragma vertex vert #pragma fragment frag_v diff --git a/Outline.Core/Packages/UnityFx.Outline/Runtime/Shaders/OutlineColor.shader b/Outline.Core/Packages/UnityFx.Outline/Runtime/Shaders/OutlineColor.shader index bca1a57..d44c92b 100644 --- a/Outline.Core/Packages/UnityFx.Outline/Runtime/Shaders/OutlineColor.shader +++ b/Outline.Core/Packages/UnityFx.Outline/Runtime/Shaders/OutlineColor.shader @@ -9,11 +9,36 @@ Shader "Hidden/UnityFx/OutlineColor" #include "UnityCG.cginc" + UNITY_DECLARE_SCREENSPACE_TEXTURE(_MainTex); + float _Cutoff; + + v2f_img vert(appdata_img v) + { + v2f_img o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_OUTPUT(v2f_img, o); + UNITY_TRANSFER_INSTANCE_ID(v, o); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.texcoord; + return o; + } + half4 frag() : SV_Target { return 1; } + half4 frag_clip(v2f_img i) : SV_Target + { + UNITY_SETUP_INSTANCE_ID(i); + + half4 c = UNITY_SAMPLE_SCREENSPACE_TEXTURE(_MainTex, i.uv); + clip(c.a - _Cutoff); + return 1; + } + ENDHLSL SubShader @@ -27,10 +52,22 @@ Shader "Hidden/UnityFx/OutlineColor" { HLSLPROGRAM - #pragma vertex vert_img + #pragma multi_compile_instancing + #pragma vertex vert #pragma fragment frag ENDHLSL } + + Pass + { + HLSLPROGRAM + + #pragma multi_compile_instancing + #pragma vertex vert + #pragma fragment frag_clip + + ENDHLSL + } } } diff --git a/Outline.Core/Packages/UnityFx.Outline/package.json b/Outline.Core/Packages/UnityFx.Outline/package.json index 6dc340f..9f478fc 100644 --- a/Outline.Core/Packages/UnityFx.Outline/package.json +++ b/Outline.Core/Packages/UnityFx.Outline/package.json @@ -1,6 +1,6 @@ { "name": "com.unityfx.outline", - "version": "0.8.0", + "version": "0.8.1", "displayName": "Outline toolkit", "description": "This package contains configurable per-object and per-camera outline effect implementation for built-in render pipeline. Both solid and blurred outline modes are supported (Gauss blur), as well as depth testing. Reusable and extensible API.", "unity": "2018.4", diff --git a/Outline.Core/ProjectSettings/ProjectVersion.txt b/Outline.Core/ProjectSettings/ProjectVersion.txt index 31a1862..895cee8 100644 --- a/Outline.Core/ProjectSettings/ProjectVersion.txt +++ b/Outline.Core/ProjectSettings/ProjectVersion.txt @@ -1 +1 @@ -m_EditorVersion: 2018.4.11f1 +m_EditorVersion: 2018.4.13f1 diff --git a/Outline.PostProcessing/Packages/UnityFx.Outline.PostProcessing/Runtime/Scripts/Outline.cs b/Outline.PostProcessing/Packages/UnityFx.Outline.PostProcessing/Runtime/Scripts/Outline.cs index b43431e..2094d8b 100644 --- a/Outline.PostProcessing/Packages/UnityFx.Outline.PostProcessing/Runtime/Scripts/Outline.cs +++ b/Outline.PostProcessing/Packages/UnityFx.Outline.PostProcessing/Runtime/Scripts/Outline.cs @@ -25,7 +25,7 @@ public class OutlineResourcesParameter : ParameterOverride protected override void OnEnable() { - if (value == null) + if (value is null) { value = Settings._defaultResources; } diff --git a/Outline.URP/Packages/UnityFx.Outline.URP/Runtime/Scripts/OutlineFeature.cs b/Outline.URP/Packages/UnityFx.Outline.URP/Runtime/Scripts/OutlineFeature.cs index ad6c82e..4e2d956 100644 --- a/Outline.URP/Packages/UnityFx.Outline.URP/Runtime/Scripts/OutlineFeature.cs +++ b/Outline.URP/Packages/UnityFx.Outline.URP/Runtime/Scripts/OutlineFeature.cs @@ -54,7 +54,7 @@ public override void Create() { _outlinePass = new OutlinePass(this) { - renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing + renderPassEvent = RenderPassEvent.AfterRenderingSkybox }; } diff --git a/README.md b/README.md index 9fff0c8..0c4244c 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,8 @@ Supported outline parameters are: - Width (in pixels); - Type (solid or blurred); - Intensity (for blurred outlines); -- Depth testing. +- Depth testing; +- Alpha testing. Supported platforms: - Windows/Mac standalone; @@ -66,7 +67,7 @@ Npm core package is available at [npmjs.com](https://www.npmjs.com/package/com.u } ], "dependencies": { - "com.unityfx.outline": "0.8.0", + "com.unityfx.outline": "0.8.1", "com.unityfx.outline.urp": "0.1.0", } } @@ -147,6 +148,12 @@ outlineSettings.OutlineWidth = 2; outlineSettings.OutlineRenderMode = OutlineRenderFlags.Blurred | OutlineRenderFlags.EnableDepthTesting; ``` +### Alpha testing +By default alpha testing is disabled when rendering outlines. This behaviour can be overriden by setting `EnableAlphaTesting` flag of `Rander Flags` (either via scripting API or with editor). +```csharp +outlineSettings.OutlineRenderMode = OutlineRenderFlags.EnableAlphaTesting; +``` + ### Ignore layers When adding a `GameObject` to outline collection it is often desirable to ignore child renderers in specific layers (for instance, `TransparentFX`). This can be achieved by settings the `IgnoreLayers` mask in outline settings (or through corresponding API). ```csharp