Created
May 25, 2020 14:11
-
-
Save niuage/1cc88222a5b947620c439003caff76ac to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Shader "Unlit/GeoGrass" { | |
Properties { | |
_Color ("Colour", Color) = (1,1,1,1) | |
_Color2 ("Colour2", Color) = (1,1,1,1) | |
_Width ("Width", Float) = 1 | |
_RandomWidth ("Random Width", Float) = 1 | |
_Height ("Height", Float) = 1 | |
_RandomHeight ("Random Height", Float) = 1 | |
} | |
SubShader { | |
Tags { "RenderType"="Opaque" "RenderPipeline" = "UniversalPipeline" } | |
LOD 300 | |
Cull Off | |
Pass { | |
Name "ForwardLit" | |
Tags {"LightMode" = "UniversalForward"} | |
HLSLPROGRAM | |
// Required to compile gles 2.0 with standard srp library | |
#pragma prefer_hlslcc gles | |
#pragma exclude_renderers d3d11_9x gles | |
#pragma target 4.5 | |
#pragma require geometry | |
#pragma vertex vert | |
#pragma geometry geom | |
#pragma fragment frag | |
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS | |
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE | |
#pragma multi_compile _ _SHADOWS_SOFT | |
// Defines | |
#define BLADE_SEGMENTS 3 | |
// Includes | |
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" | |
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" | |
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl" | |
#include "grass.hlsl" | |
// Fragment | |
float4 frag (GeometryOutput input) : SV_Target { | |
#if SHADOWS_SCREEN | |
float4 clipPos = TransformWorldToHClip(input.positionWS); | |
float4 shadowCoord = ComputeScreenPos(clipPos); | |
#else | |
float4 shadowCoord = TransformWorldToShadowCoord(input.positionWS); | |
#endif | |
Light mainLight = GetMainLight(shadowCoord); | |
return lerp(_Color, _Color2, input.uv.y) * mainLight.shadowAttenuation; | |
} | |
ENDHLSL | |
} | |
// Used for rendering shadowmaps | |
//UsePass "Universal Render Pipeline/Lit/ShadowCaster" | |
Pass { | |
Name "ShadowCaster" | |
Tags {"LightMode" = "ShadowCaster"} | |
ZWrite On | |
ZTest LEqual | |
HLSLPROGRAM | |
// Required to compile gles 2.0 with standard srp library | |
#pragma prefer_hlslcc gles | |
#pragma exclude_renderers d3d11_9x gles | |
#pragma target 4.5 | |
// ------------------------------------- | |
// Material Keywords | |
//#pragma shader_feature _ALPHATEST_ON | |
//-------------------------------------- | |
// GPU Instancing | |
//#pragma multi_compile_instancing | |
//#pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A | |
#pragma require geometry | |
#pragma vertex vert | |
#pragma geometry geom | |
#pragma fragment ShadowPassFragment2 | |
#define BLADE_SEGMENTS 3 | |
#define SHADOW | |
//#include "Packages/com.unity.render-pipelines.universal/Shaders/ShadowCasterPass.hlsl" | |
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" | |
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl" | |
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl" | |
#include "grass.hlsl" | |
half4 ShadowPassFragment2(GeometryOutput input) : SV_TARGET{ | |
//Alpha(SampleAlbedoAlpha(input.uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap)).a, _BaseColor, _Cutoff); | |
return 0; | |
} | |
ENDHLSL | |
} | |
// Used for depth prepass | |
// If shadows cascade are enabled we need to perform a depth prepass. | |
// We also need to use a depth prepass in some cases camera require depth texture | |
// (e.g, MSAA is enabled and we can't resolve with Texture2DMS | |
//UsePass "Universal Render Pipeline/Lit/DepthOnly" | |
// Note, can't UsePass + SRP Batcher due to UnityPerMaterial CBUFFER having incosistent size between subshaders.. | |
// Had to comment this out for now so it doesn't break SRP Batcher. | |
// Would have to copy the pass in here and use the same UnityPerMaterial buffer the other passes use | |
} | |
} | |
-------------------------------------------------- | |
(grass.hlsl) | |
// @Cyanilux | |
// Grass Geometry Shader, Written for Universal RP with help from https://roystan.net/articles/grass-shader.html | |
// Note, doesn't include Lighting or Tessellation | |
// Structs | |
struct Attributes { | |
float4 positionOS : POSITION; | |
float3 normal : NORMAL; | |
float4 tangent : TANGENT; | |
float2 texcoord : TEXCOORD0; | |
}; | |
struct Varyings { | |
float4 positionOS : SV_POSITION; | |
float3 positionWS : TEXCOORD1; | |
float3 normal : NORMAL; | |
float4 tangent : TANGENT; | |
float2 texcoord : TEXCOORD0; | |
}; | |
struct GeometryOutput { | |
float4 positionCS : SV_POSITION; | |
float3 positionWS : TEXCOORD1; | |
float2 uv : TEXCOORD0; | |
}; | |
// Methods | |
float rand(float3 seed) { | |
return frac(sin(dot(seed.xyz, float3(12.9898, 78.233, 53.539))) * 43758.5453); | |
} | |
// https://gist.github.com/keijiro/ee439d5e7388f3aafc5296005c8c3f33 | |
float3x3 AngleAxis3x3(float angle, float3 axis) { | |
float c, s; | |
sincos(angle, s, c); | |
float t = 1 - c; | |
float x = axis.x; | |
float y = axis.y; | |
float z = axis.z; | |
return float3x3( | |
t * x * x + c, t * x * y - s * z, t * x * z + s * y, | |
t * x * y + s * z, t * y * y + c, t * y * z - s * x, | |
t * x * z - s * y, t * y * z + s * x, t * z * z + c | |
); | |
} | |
float3 _LightDirection; | |
float4 GetShadowPositionHClip(float3 positionWS, float3 normalWS) { | |
float4 positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, _LightDirection)); | |
#if UNITY_REVERSED_Z | |
positionCS.z = min(positionCS.z, positionCS.w * UNITY_NEAR_CLIP_VALUE); | |
#else | |
positionCS.z = max(positionCS.z, positionCS.w * UNITY_NEAR_CLIP_VALUE); | |
#endif | |
return positionCS; | |
} | |
float4 WorldToHClip(float3 positionWS, float3 normalWS){ | |
#ifdef SHADOW | |
return GetShadowPositionHClip(positionWS, normalWS); | |
#else | |
return TransformWorldToHClip(positionWS); | |
#endif | |
} | |
// Variables | |
CBUFFER_START(UnityPerMaterial) // Required to be compatible with SRP Batcher | |
float4 _Color; | |
float4 _Color2; | |
float _Width; | |
float _RandomWidth; | |
float _Height; | |
float _RandomHeight; | |
CBUFFER_END | |
// Vertex, Geometry & Fragment Shaders | |
Varyings vert (Attributes input) { | |
Varyings output = (Varyings)0; | |
VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz); | |
// Seems like GetVertexPositionInputs doesn't work with SRP Batcher inside geom function? | |
// Had to move it here, in order to obtain positionWS and pass it through the Varyings output. | |
output.positionOS = input.positionOS; //vertexInput.positionCS; // | |
output.positionWS = vertexInput.positionWS; | |
output.normal = input.normal; | |
output.tangent = input.tangent; | |
output.texcoord = input.texcoord; | |
return output; | |
} | |
[maxvertexcount(BLADE_SEGMENTS * 2 + 1)] | |
void geom(uint primitiveID : SV_PrimitiveID, triangle Varyings input[3], inout TriangleStream<GeometryOutput> triStream) { | |
GeometryOutput output = (GeometryOutput) 0; | |
//VertexPositionInputs vertexInput = GetVertexPositionInputs(input[0].positionOS.xyz); | |
// Note, this works fine without SRP Batcher but seems to break when using it. See vert function above. | |
/* (Normal mesh vertices. Need to add 3 to maxvertexcount if this is uncommented) | |
output.positionCS = TransformWorldToHClip(input[0].positionWS); | |
output.uv = input[0].texcoord; | |
triStream.Append(output); | |
output.positionCS = TransformWorldToHClip(input[1].positionWS); | |
output.uv = input[1].texcoord; | |
triStream.Append(output); | |
output.positionCS = TransformWorldToHClip(input[2].positionWS); | |
output.uv = input[2].texcoord; | |
triStream.Append(output); | |
triStream.RestartStrip(); | |
*/ | |
// Construct World -> Tangent Matrix (for aligning grass with mesh normals) | |
float3 normal = input[0].normal; | |
float4 tangent = input[0].tangent; | |
float3 binormal = cross(normal, tangent) * tangent.w; | |
float3x3 tangentToLocal = float3x3( | |
tangent.x, binormal.x, normal.x, | |
tangent.y, binormal.y, normal.y, | |
tangent.z, binormal.z, normal.z | |
); | |
float3 positionWS = input[0].positionWS; | |
float r = rand(positionWS.xyz); | |
float3x3 randRotation = AngleAxis3x3(r * TWO_PI, float3(0,0,1)); | |
// Wind (based on sin / cos, aka a circular motion, but strength of 0.1 * sine) | |
float2 wind = float2(sin(_Time.y + positionWS.x * 0.5), cos(_Time.y + positionWS.z * 0.5)) * 0.1 * sin(_Time.y + r); | |
float3x3 windMatrix = AngleAxis3x3(wind * PI, normalize(float3(wind.x,wind.y,0))); | |
float3x3 transformMatrix = mul(tangentToLocal, randRotation); | |
float3x3 transformMatrixWithWind = mul(mul(tangentToLocal, windMatrix), randRotation); | |
float bend = rand(positionWS.xyz) - 0.5; | |
float width = _Width + _RandomWidth * (rand(positionWS.zyx) - 0.5); | |
float height = _Height + _RandomHeight * (rand(positionWS.yxz) - 0.5); | |
float3 normalWS = mul(transformMatrix, float3(0, 1, 0)); //? | |
// Handle Geometry | |
// Base 2 vertices | |
output.positionWS = positionWS + mul(transformMatrix, float3(width, 0, 0)); | |
output.positionCS = WorldToHClip(output.positionWS, normalWS); | |
output.uv = float2(0, 0); | |
triStream.Append(output); | |
output.positionWS = positionWS + mul(transformMatrix, float3(-width, 0, 0)); | |
output.positionCS = WorldToHClip(output.positionWS, normalWS); | |
output.uv = float2(0, 0); | |
triStream.Append(output); | |
// Center (2 vertices per BLADE_SEGMENTS) | |
for (int i = 1; i < BLADE_SEGMENTS; i++) { | |
float t = i / (float)BLADE_SEGMENTS; | |
float h = height * t; | |
float w = width * (1-t); | |
float b = bend * pow(t, 2); | |
output.positionWS = positionWS + mul(transformMatrixWithWind, float3(w, b, h)); | |
output.positionCS = WorldToHClip(output.positionWS, normalWS); | |
output.uv = float2(0, t); | |
triStream.Append(output); | |
output.positionWS = positionWS + mul(transformMatrixWithWind, float3(-w, b, h)); | |
output.positionCS = WorldToHClip(output.positionWS, normalWS); | |
output.uv = float2(0, t); | |
triStream.Append(output); | |
} | |
// Final vertex at top of blade | |
output.positionWS = positionWS + mul(transformMatrixWithWind, float3(0, bend, height)); | |
output.positionCS = WorldToHClip(output.positionWS, normalWS); | |
output.uv = float2(0, 1); | |
triStream.Append(output); | |
triStream.RestartStrip(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment