Advertisement
Guest User

Grass Geometry Shader

a guest
Apr 30th, 2020
1,481
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.24 KB | None | 0 0
  1. // @Cyanilux
  2. // Grass Geometry Shader, Written for Universal RP with help from https://roystan.net/articles/grass-shader.html
  3. // Note, doesn't include Lighting or Tessellation
  4. Shader "Unlit/GeoGrass" {
  5. Properties {
  6. _Color ("Colour", Color) = (1,1,1,1)
  7. _Color2 ("Colour2", Color) = (1,1,1,1)
  8. _Width ("Width", Float) = 1
  9. _RandomWidth ("Random Width", Float) = 1
  10. _Height ("Height", Float) = 1
  11. _RandomHeight ("Random Height", Float) = 1
  12. }
  13. SubShader {
  14. Tags { "RenderType"="Opaque" "RenderPipeline" = "UniversalPipeline" }
  15. LOD 300
  16.  
  17. Cull Off
  18.  
  19. Pass {
  20. Name "ForwardLit"
  21. Tags {"LightMode" = "UniversalForward"}
  22.  
  23. HLSLPROGRAM
  24. // Required to compile gles 2.0 with standard srp library
  25. #pragma prefer_hlslcc gles
  26. #pragma exclude_renderers d3d11_9x gles
  27. #pragma target 4.5
  28.  
  29. #pragma require geometry
  30.  
  31. #pragma vertex vert
  32. #pragma geometry geom
  33. #pragma fragment frag
  34.  
  35. #pragma multi_compile _ _MAIN_LIGHT_SHADOWS
  36. #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
  37. #pragma multi_compile _ _SHADOWS_SOFT
  38.  
  39. // Defines
  40.  
  41. #define BLADE_SEGMENTS 3
  42.  
  43. // Includes
  44.  
  45. #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
  46. #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
  47. #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
  48. #include "grass.hlsl"
  49.  
  50. // Fragment
  51.  
  52. float4 frag (GeometryOutput input) : SV_Target {
  53. #if SHADOWS_SCREEN
  54. float4 clipPos = TransformWorldToHClip(input.positionWS);
  55. float4 shadowCoord = ComputeScreenPos(clipPos);
  56. #else
  57. float4 shadowCoord = TransformWorldToShadowCoord(input.positionWS);
  58. #endif
  59.  
  60. Light mainLight = GetMainLight(shadowCoord);
  61.  
  62. return lerp(_Color, _Color2, input.uv.y) * mainLight.shadowAttenuation;
  63. }
  64.  
  65. ENDHLSL
  66. }
  67.  
  68. // Used for rendering shadowmaps
  69. //UsePass "Universal Render Pipeline/Lit/ShadowCaster"
  70.  
  71. Pass {
  72. Name "ShadowCaster"
  73. Tags {"LightMode" = "ShadowCaster"}
  74.  
  75. ZWrite On
  76. ZTest LEqual
  77.  
  78. HLSLPROGRAM
  79. // Required to compile gles 2.0 with standard srp library
  80. #pragma prefer_hlslcc gles
  81. #pragma exclude_renderers d3d11_9x gles
  82. #pragma target 4.5
  83.  
  84. // -------------------------------------
  85. // Material Keywords
  86. //#pragma shader_feature _ALPHATEST_ON
  87.  
  88. //--------------------------------------
  89. // GPU Instancing
  90. //#pragma multi_compile_instancing
  91. //#pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
  92.  
  93. #pragma require geometry
  94.  
  95. #pragma vertex vert
  96. #pragma geometry geom
  97. #pragma fragment ShadowPassFragment2
  98.  
  99. #define BLADE_SEGMENTS 3
  100. #define SHADOW
  101.  
  102. //#include "Packages/com.unity.render-pipelines.universal/Shaders/ShadowCasterPass.hlsl"
  103.  
  104. #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
  105. #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
  106. #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
  107. #include "grass.hlsl"
  108.  
  109. half4 ShadowPassFragment2(GeometryOutput input) : SV_TARGET{
  110. //Alpha(SampleAlbedoAlpha(input.uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap)).a, _BaseColor, _Cutoff);
  111. return 0;
  112. }
  113.  
  114. ENDHLSL
  115. }
  116.  
  117. // Used for depth prepass
  118. // If shadows cascade are enabled we need to perform a depth prepass.
  119. // We also need to use a depth prepass in some cases camera require depth texture
  120. // (e.g, MSAA is enabled and we can't resolve with Texture2DMS
  121. //UsePass "Universal Render Pipeline/Lit/DepthOnly"
  122.  
  123. // Note, can't UsePass + SRP Batcher due to UnityPerMaterial CBUFFER having incosistent size between subshaders..
  124. // Had to comment this out for now so it doesn't break SRP Batcher.
  125. // Would have to copy the pass in here and use the same UnityPerMaterial buffer the other passes use
  126.  
  127. }
  128. }
  129.  
  130. --------------------------------------------------
  131. (grass.hlsl)
  132. // @Cyanilux
  133. // Grass Geometry Shader, Written for Universal RP with help from https://roystan.net/articles/grass-shader.html
  134. // Note, doesn't include Lighting or Tessellation
  135.  
  136. // Structs
  137.  
  138. struct Attributes {
  139. float4 positionOS : POSITION;
  140. float3 normal : NORMAL;
  141. float4 tangent : TANGENT;
  142. float2 texcoord : TEXCOORD0;
  143. };
  144.  
  145. struct Varyings {
  146. float4 positionOS : SV_POSITION;
  147. float3 positionWS : TEXCOORD1;
  148. float3 normal : NORMAL;
  149. float4 tangent : TANGENT;
  150. float2 texcoord : TEXCOORD0;
  151. };
  152.  
  153. struct GeometryOutput {
  154. float4 positionCS : SV_POSITION;
  155. float3 positionWS : TEXCOORD1;
  156. float2 uv : TEXCOORD0;
  157. };
  158.  
  159. // Methods
  160.  
  161. float rand(float3 seed) {
  162. return frac(sin(dot(seed.xyz, float3(12.9898, 78.233, 53.539))) * 43758.5453);
  163. }
  164.  
  165. // https://gist.github.com/keijiro/ee439d5e7388f3aafc5296005c8c3f33
  166. float3x3 AngleAxis3x3(float angle, float3 axis) {
  167. float c, s;
  168. sincos(angle, s, c);
  169.  
  170. float t = 1 - c;
  171. float x = axis.x;
  172. float y = axis.y;
  173. float z = axis.z;
  174.  
  175. return float3x3(
  176. t * x * x + c, t * x * y - s * z, t * x * z + s * y,
  177. t * x * y + s * z, t * y * y + c, t * y * z - s * x,
  178. t * x * z - s * y, t * y * z + s * x, t * z * z + c
  179. );
  180. }
  181.  
  182. float3 _LightDirection;
  183.  
  184. float4 GetShadowPositionHClip(float3 positionWS, float3 normalWS) {
  185. float4 positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, _LightDirection));
  186.  
  187. #if UNITY_REVERSED_Z
  188. positionCS.z = min(positionCS.z, positionCS.w * UNITY_NEAR_CLIP_VALUE);
  189. #else
  190. positionCS.z = max(positionCS.z, positionCS.w * UNITY_NEAR_CLIP_VALUE);
  191. #endif
  192.  
  193. return positionCS;
  194. }
  195.  
  196. float4 WorldToHClip(float3 positionWS, float3 normalWS){
  197. #ifdef SHADOW
  198. return GetShadowPositionHClip(positionWS, normalWS);
  199. #else
  200. return TransformWorldToHClip(positionWS);
  201. #endif
  202. }
  203.  
  204. // Variables
  205. CBUFFER_START(UnityPerMaterial) // Required to be compatible with SRP Batcher
  206. float4 _Color;
  207. float4 _Color2;
  208. float _Width;
  209. float _RandomWidth;
  210. float _Height;
  211. float _RandomHeight;
  212. CBUFFER_END
  213.  
  214. // Vertex, Geometry & Fragment Shaders
  215.  
  216. Varyings vert (Attributes input) {
  217. Varyings output = (Varyings)0;
  218.  
  219. VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
  220. // Seems like GetVertexPositionInputs doesn't work with SRP Batcher inside geom function?
  221. // Had to move it here, in order to obtain positionWS and pass it through the Varyings output.
  222.  
  223. output.positionOS = input.positionOS; //vertexInput.positionCS; //
  224. output.positionWS = vertexInput.positionWS;
  225. output.normal = input.normal;
  226. output.tangent = input.tangent;
  227. output.texcoord = input.texcoord;
  228. return output;
  229. }
  230.  
  231. [maxvertexcount(BLADE_SEGMENTS * 2 + 1)]
  232. void geom(uint primitiveID : SV_PrimitiveID, triangle Varyings input[3], inout TriangleStream<GeometryOutput> triStream) {
  233. GeometryOutput output = (GeometryOutput) 0;
  234.  
  235. //VertexPositionInputs vertexInput = GetVertexPositionInputs(input[0].positionOS.xyz);
  236. // Note, this works fine without SRP Batcher but seems to break when using it. See vert function above.
  237.  
  238. /* (Normal mesh vertices. Need to add 3 to maxvertexcount if this is uncommented)
  239. output.positionCS = TransformWorldToHClip(input[0].positionWS);
  240. output.uv = input[0].texcoord;
  241. triStream.Append(output);
  242.  
  243. output.positionCS = TransformWorldToHClip(input[1].positionWS);
  244. output.uv = input[1].texcoord;
  245. triStream.Append(output);
  246.  
  247. output.positionCS = TransformWorldToHClip(input[2].positionWS);
  248. output.uv = input[2].texcoord;
  249. triStream.Append(output);
  250.  
  251. triStream.RestartStrip();
  252. */
  253.  
  254. // Construct World -> Tangent Matrix (for aligning grass with mesh normals)
  255. float3 normal = input[0].normal;
  256. float4 tangent = input[0].tangent;
  257. float3 binormal = cross(normal, tangent) * tangent.w;
  258.  
  259. float3x3 tangentToLocal = float3x3(
  260. tangent.x, binormal.x, normal.x,
  261. tangent.y, binormal.y, normal.y,
  262. tangent.z, binormal.z, normal.z
  263. );
  264.  
  265. float3 positionWS = input[0].positionWS;
  266.  
  267. float r = rand(positionWS.xyz);
  268. float3x3 randRotation = AngleAxis3x3(r * TWO_PI, float3(0,0,1));
  269.  
  270. // Wind (based on sin / cos, aka a circular motion, but strength of 0.1 * sine)
  271. float2 wind = float2(sin(_Time.y + positionWS.x * 0.5), cos(_Time.y + positionWS.z * 0.5)) * 0.1 * sin(_Time.y + r);
  272. float3x3 windMatrix = AngleAxis3x3(wind * PI, normalize(float3(wind.x,wind.y,0)));
  273.  
  274. float3x3 transformMatrix = mul(tangentToLocal, randRotation);
  275. float3x3 transformMatrixWithWind = mul(mul(tangentToLocal, windMatrix), randRotation);
  276.  
  277. float bend = rand(positionWS.xyz) - 0.5;
  278. float width = _Width + _RandomWidth * (rand(positionWS.zyx) - 0.5);
  279. float height = _Height + _RandomHeight * (rand(positionWS.yxz) - 0.5);
  280.  
  281. float3 normalWS = mul(transformMatrix, float3(0, 1, 0)); //?
  282.  
  283. // Handle Geometry
  284.  
  285. // Base 2 vertices
  286. output.positionWS = positionWS + mul(transformMatrix, float3(width, 0, 0));
  287. output.positionCS = WorldToHClip(output.positionWS, normalWS);
  288. output.uv = float2(0, 0);
  289. triStream.Append(output);
  290.  
  291. output.positionWS = positionWS + mul(transformMatrix, float3(-width, 0, 0));
  292. output.positionCS = WorldToHClip(output.positionWS, normalWS);
  293. output.uv = float2(0, 0);
  294. triStream.Append(output);
  295.  
  296. // Center (2 vertices per BLADE_SEGMENTS)
  297. for (int i = 1; i < BLADE_SEGMENTS; i++) {
  298. float t = i / (float)BLADE_SEGMENTS;
  299.  
  300. float h = height * t;
  301. float w = width * (1-t);
  302. float b = bend * pow(t, 2);
  303.  
  304. output.positionWS = positionWS + mul(transformMatrixWithWind, float3(w, b, h));
  305. output.positionCS = WorldToHClip(output.positionWS, normalWS);
  306. output.uv = float2(0, t);
  307. triStream.Append(output);
  308.  
  309. output.positionWS = positionWS + mul(transformMatrixWithWind, float3(-w, b, h));
  310. output.positionCS = WorldToHClip(output.positionWS, normalWS);
  311. output.uv = float2(0, t);
  312. triStream.Append(output);
  313. }
  314.  
  315. // Final vertex at top of blade
  316. output.positionWS = positionWS + mul(transformMatrixWithWind, float3(0, bend, height));
  317. output.positionCS = WorldToHClip(output.positionWS, normalWS);
  318.  
  319. output.uv = float2(0, 1);
  320. triStream.Append(output);
  321.  
  322. triStream.RestartStrip();
  323. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement