PaintTexturePreview_URP.shader 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. //Mesh Terrain Editor's brush preview shader for URP
  2. //http://u3d.as/oWB
  3. //This is a modified shader from https://github.com/ColinLeung-NiloCat/UnityURPUnlitScreenSpaceDecalShader
  4. Shader "Hidden/MTE/PaintTexturePreview/URP"
  5. {
  6. Properties
  7. {
  8. [Header(MTE Brush Parameters)]
  9. _BrushCenter("Brush Center", Vector) = (0, 0, 0, 0)//the UV of hitpoint of mouse ray on mesh-terain
  10. _NormalizedBrushSize("Normalized Brush Size", float) = 0.1//the normalized brush size against the mesh size of a mesh-terrain
  11. [Normal]_NormalTex ("Normalmap", 2D) = "bump" {}//Splat-texture's normal map
  12. [Header(Basic)]
  13. [MainTexture]_MainTex("Texture", 2D) = "white" {}
  14. _MaskTex ("Mask (RGB) Trans (A)", 2D) = "white" {}
  15. [HDR]_Color("_Color (default = 1,1,1,1)", color) = (1,1,1,1)
  16. [Header(Blending)]
  17. //https://docs.unity3d.com/ScriptReference/Rendering.BlendMode.html
  18. [Enum(UnityEngine.Rendering.BlendMode)]_SrcBlend("_SrcBlend (default = SrcAlpha)", Float) = 5 //5 = SrcAlpha
  19. [Enum(UnityEngine.Rendering.BlendMode)]_DstBlend("_DstBlend (default = OneMinusSrcAlpha)", Float) = 10 //10 = OneMinusSrcAlpha
  20. [Header(Alpha remap(extra alpha control))]
  21. _AlphaRemap("_AlphaRemap (default = 1,0,0,0) _____alpha will first mul x, then add y (zw unused)", vector) = (1,0,0,0)
  22. [Header(Prevent Side Stretching(Compare projection direction with scene normal and Discard if needed))]
  23. [Toggle(_ProjectionAngleDiscardEnable)] _ProjectionAngleDiscardEnable("_ProjectionAngleDiscardEnable (default = off)", float) = 0
  24. _ProjectionAngleDiscardThreshold("_ProjectionAngleDiscardThreshold (default = 0)", range(-1,1)) = 0
  25. [Header(Mul alpha to rgb)]
  26. [Toggle]_MulAlphaToRGB("_MulAlphaToRGB (default = off)", Float) = 0
  27. [Header(Ignore texture wrap mode setting)]
  28. [Toggle(_FracUVEnable)] _FracUVEnable("_FracUVEnable (default = off)", Float) = 0
  29. //====================================== below = usually can ignore in normal use case =====================================================================
  30. [Header(Stencil Masking)]
  31. //https://docs.unity3d.com/ScriptReference/Rendering.CompareFunction.html
  32. _StencilRef("_StencilRef", Float) = 0
  33. [Enum(UnityEngine.Rendering.CompareFunction)]_StencilComp("_StencilComp (default = Disable) _____Set to NotEqual if you want to mask by specific _StencilRef value, else set to Disable", Float) = 0 //0 = disable
  34. [Header(ZTest)]
  35. //https://docs.unity3d.com/ScriptReference/Rendering.CompareFunction.html
  36. //default need to be Disable, because we need to make sure decal render correctly even if camera goes into decal cube volume, although disable ZTest by default will prevent EarlyZ (bad for GPU performance)
  37. [Enum(UnityEngine.Rendering.CompareFunction)]_ZTest("_ZTest (default = Disable) _____to improve GPU performance, Set to LessEqual if camera never goes into cube volume, else set to Disable", Float) = 0 //0 = disable
  38. [Header(Cull)]
  39. //https://docs.unity3d.com/ScriptReference/Rendering.CullMode.html
  40. //default need to be Front, because we need to make sure decal render correctly even if camera goes into decal cube
  41. [Enum(UnityEngine.Rendering.CullMode)]_Cull("_Cull (default = Front) _____to improve GPU performance, Set to Back if camera never goes into cube volume, else set to Front", Float) = 1 //1 = Front
  42. [Header(Unity Fog)]
  43. [Toggle(_UnityFogEnable)] _UnityFogEnable("_UnityFogEnable (default = on)", Float) = 1
  44. }
  45. SubShader
  46. {
  47. //To avoid render order problems, Queue must >= 2501, which enters the transparent queue, in transparent queue Unity will always draw from back to front
  48. //https://github.com/ColinLeung-NiloCat/UnityURPUnlitScreenSpaceDecalShader/issues/6#issuecomment-615940985
  49. /*
  50. //https://docs.unity3d.com/Manual/SL-SubShaderTags.html
  51. Queues up to 2500 (“Geometry+500”) are consided “opaque” and optimize the drawing order of the objects for best performance.
  52. Higher rendering queues are considered for “transparent objects” and sort objects by distance, starting rendering from the furthest ones and ending with the closest ones.
  53. Skyboxes are drawn in between all opaque and all transparent objects.
  54. */
  55. Tags { "RenderType" = "Overlay" "Queue" = "Transparent-499" }
  56. Pass
  57. {
  58. Stencil
  59. {
  60. Ref[_StencilRef]
  61. Comp[_StencilComp]
  62. }
  63. Cull[_Cull]
  64. ZTest[_ZTest]
  65. ZWrite off
  66. Blend[_SrcBlend][_DstBlend]
  67. HLSLPROGRAM
  68. #pragma vertex vert
  69. #pragma fragment frag
  70. // make fog work
  71. #pragma multi_compile_fog
  72. //due to using ddx() & ddy()
  73. #pragma target 3.0
  74. #pragma shader_feature_local _ProjectionAngleDiscardEnable
  75. #pragma shader_feature_local _UnityFogEnable
  76. #pragma shader_feature_local _FracUVEnable
  77. // Required by all Universal Render Pipeline shaders.
  78. // It will include Unity built-in shader variables (except the lighting variables)
  79. // (https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html
  80. // It will also include many utilitary functions.
  81. #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
  82. // for calculating color against the main light and normal map
  83. #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
  84. struct appdata
  85. {
  86. float4 vertex : POSITION;
  87. };
  88. struct v2f
  89. {
  90. float4 vertex : SV_POSITION;
  91. float4 screenUV : TEXCOORD0;
  92. float4 viewRayOS : TEXCOORD1;
  93. float4 cameraPosOSAndFogFactor : TEXCOORD2;
  94. };
  95. sampler2D _MainTex, _MaskTex, _NormalTex;
  96. CBUFFER_START(UnityPerMaterial)
  97. float4 _MainTex_ST;
  98. float2 _BrushCenter;
  99. float _NormalizedBrushSize;
  100. float _ProjectionAngleDiscardThreshold;
  101. half4 _Color;
  102. half2 _AlphaRemap;
  103. half _MulAlphaToRGB;
  104. CBUFFER_END
  105. sampler2D _CameraDepthTexture;
  106. v2f vert(appdata v)
  107. {
  108. v2f o;
  109. //regular MVP
  110. o.vertex = TransformObjectToHClip(v.vertex.xyz);
  111. //regular unity fog
  112. #if _UnityFogEnable
  113. o.cameraPosOSAndFogFactor.a = ComputeFogFactor(o.vertex.z);
  114. #else
  115. o.cameraPosOSAndFogFactor.a = 0;
  116. #endif
  117. //prepare depth texture's screen space UV
  118. o.screenUV = ComputeScreenPos(o.vertex);
  119. //get "camera to vertex" ray in View space
  120. float3 viewRay = TransformWorldToView(TransformObjectToWorld(v.vertex.xyz));
  121. //***WARNING***
  122. //=========================================================
  123. //"viewRay z division" must do in the fragment shader, not vertex shader! (due to rasteriazation varying interpolation's perspective correction)
  124. //We skip the "viewRay z division" in vertex shader for now, and pass the division value to varying o.viewRayOS.w first, we will do the division later when we enter fragment shader
  125. //viewRay /= viewRay.z; //skip the "viewRay z division" in vertex shader for now
  126. o.viewRayOS.w = viewRay.z;//pass the division value to varying o.viewRayOS.w
  127. //=========================================================
  128. viewRay *= -1; //unity's camera space is right hand coord(negativeZ pointing into screen), we want positive z ray in fragment shader, so negate it
  129. //it is ok to write very expensive code in decal's vertex shader, it is just a unity cube(4*6 vertices) per decal only, won't affect GPU performance at all.
  130. float4x4 ViewToObjectMatrix = mul(unity_WorldToObject, UNITY_MATRIX_I_V);
  131. //transform everything to object space(decal space) in vertex shader first, so we can skip all matrix mul() in fragment shader
  132. o.viewRayOS.xyz = mul((float3x3)ViewToObjectMatrix, viewRay);
  133. o.cameraPosOSAndFogFactor.xyz = mul(ViewToObjectMatrix, float4(0,0,0,1)).xyz;
  134. return o;
  135. }
  136. half3 LambertLight(half3 color, half3 normal)
  137. {
  138. Light light = GetMainLight();
  139. half3 c = 0.5f * color + 0.5f * color * max(half3(1, 1, 1), light.color) * dot(
  140. #if UNITY_VERSION >= 202010//TODO normal not work for 2020.1+, to be inspected
  141. half3(0,1,0)
  142. #else
  143. normal
  144. #endif
  145. , light.direction);
  146. return c;
  147. }
  148. half4 frag(v2f i) : SV_Target
  149. {
  150. //***WARNING***
  151. //=========================================================
  152. //now do "viewRay z division" that we skipped in vertex shader earlier.
  153. i.viewRayOS /= i.viewRayOS.w;
  154. //=========================================================
  155. float sceneCameraSpaceDepth = LinearEyeDepth(tex2Dproj(_CameraDepthTexture, i.screenUV).r, _ZBufferParams);
  156. //scene depth in any space = rayStartPos + rayDir * rayLength
  157. //here all data in ObjectSpace(OS) or DecalSpace
  158. float3 decalSpaceScenePos = i.cameraPosOSAndFogFactor.xyz + i.viewRayOS.xyz * sceneCameraSpaceDepth;
  159. //convert unity cube's [-0.5,0.5] vertex pos range to [0,1] uv. Only works if you use unity cube in mesh filter!
  160. float2 decalSpaceUV = decalSpaceScenePos.xy + 0.5;
  161. //discard logic
  162. //===================================================
  163. // discard "out of cube volume" pixels
  164. //2020-4-17: tried fix clip() bug by removing all possible bool
  165. //https://github.com/ColinLeung-NiloCat/UnityURPUnlitScreenSpaceDecalShader/issues/6#issuecomment-614633460
  166. float mask = (abs(decalSpaceScenePos.x) < 0.5 ? 1.0 : 0.0) * (abs(decalSpaceScenePos.y) < 0.5 ? 1.0 : 0.0) * (abs(decalSpaceScenePos.z) < 0.5 ? 1.0 : 0.0);
  167. #if _ProjectionAngleDiscardEnable
  168. // also discard "scene normal not facing decal projector direction" pixels
  169. float3 decalSpaceHardNormal = normalize(cross(ddx(decalSpaceScenePos), ddy(decalSpaceScenePos)));//reconstruct scene hard normal using scene pos ddx&ddy
  170. mask *= decalSpaceHardNormal.z > _ProjectionAngleDiscardThreshold ? 1.0 : 0.0;//compare scene hard normal with decal projector's dir, decalSpaceHardNormal.z equals dot(decalForwardDir,sceneHardNormalDir)
  171. #endif
  172. //call discard
  173. clip(mask - 0.5);//if ZWrite is off, clip() is fast enough on mobile, because it won't write the DepthBuffer, so no pipeline stall(confirmed by ARM staff).
  174. //===================================================
  175. // sample the decal texture
  176. float2 uv = decalSpaceUV.xy
  177. * _MainTex_ST.xy * _NormalizedBrushSize + _MainTex_ST.zw
  178. + (_BrushCenter - 0.5*_NormalizedBrushSize )* _MainTex_ST.xy;//Texture tiling & offset
  179. #if _FracUVEnable
  180. uv = frac(uv);//add frac to ignore texture wrap setting
  181. #endif
  182. half4 col = tex2D(_MainTex, uv);
  183. col.a = saturate(col.a * _AlphaRemap.x + _AlphaRemap.y);//alpha remap MAD
  184. col.a *= tex2D(_MaskTex, decalSpaceUV.xy).a;//apply alpha mask
  185. col.rgb *= lerp(1, col.a, _MulAlphaToRGB);//extra multiply alpha to RGB
  186. // sample the normal map
  187. half3 normal = UnpackNormal(tex2D(_NormalTex, uv));
  188. // lit against the light with normal considered
  189. col.rgb = LambertLight(col.rgb, normal);
  190. #if _UnityFogEnable
  191. // Mix the pixel color with fogColor. You can optionaly use MixFogColor to override the fogColor
  192. // with a custom one.
  193. col.rgb = MixFog(col.rgb, i.cameraPosOSAndFogFactor.a);
  194. #endif
  195. return col;
  196. }
  197. ENDHLSL
  198. }
  199. }
  200. }