Last active
March 8, 2024 22:07
-
-
Save niuage/933abbc7a26a311f7d9e016e124afbd5 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
/////////////////////////////////////////////////// | |
//////////////// grass_struct.hlsl //////////////// | |
/////////////////////////////////////////////////// | |
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; | |
}; | |
struct GeometryOutput { | |
float4 positionCS : SV_POSITION; | |
float3 positionWS : TEXCOORD1; | |
float2 uv : TEXCOORD0; | |
}; | |
//////////////////////////////////////////// | |
//////////////// grass.hlsl //////////////// | |
//////////////////////////////////////////// | |
// Original by @Cyanilux | |
// Grass Geometry Shader, Written for Universal RP with help from https://roystan.net/articles/grass-shader.html | |
// Modified by @niuage to add tessellation. Also removed some code I didnt need in my own game, so feel free to revert to the original | |
// version from Cyanilux here: https://pastebin.com/Ey01tzLq | |
// 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 _WindStrength; | |
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; | |
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; | |
// 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)) * _WindStrength * sin(_Time.y + r); | |
float3x3 windMatrix = AngleAxis3x3((wind * PI).y, 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); | |
// 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(); | |
} | |
///////////////////////////////////////////////// | |
//////////////// GeoGrass.shader //////////////// | |
///////////////////////////////////////////////// | |
// @Cyanilux | |
// Grass Geometry Shader, Written for Universal RP with help from https://roystan.net/articles/grass-shader.html | |
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 | |
_WindStrength("Wind Strength", Float) = 0.1 | |
[Space] | |
_TessellationUniform("Tessellation Uniform", Range(1, 64)) = 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 hull hull | |
#pragma domain domain | |
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS | |
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE | |
#pragma multi_compile _ _SHADOWS_SOFT | |
// Defines | |
#define BLADE_SEGMENTS 1 | |
// 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_structs.hlsl" | |
#include "tessellation/CustomTessellation.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 | |
} | |
} | |
} | |
///////////////////////////////////////////////////////////////////// | |
//////////////////// tessellation/CustomTessellation.hlsl //////////////////////// | |
///////////////////////////////////////////////////////////////////// | |
// Tessellation programs based on this article by Catlike Coding: | |
// https://catlikecoding.com/unity/tutorials/advanced-rendering/tessellation/ | |
#if defined(SHADER_API_D3D11) || defined(SHADER_API_GLES3) || defined(SHADER_API_GLCORE) || defined(SHADER_API_VULKAN) || defined(SHADER_API_METAL) || defined(SHADER_API_PSSL) | |
#define UNITY_CAN_COMPILE_TESSELLATION 1 | |
# define UNITY_domain domain | |
# define UNITY_partitioning partitioning | |
# define UNITY_outputtopology outputtopology | |
# define UNITY_patchconstantfunc patchconstantfunc | |
# define UNITY_outputcontrolpoints outputcontrolpoints | |
#endif | |
struct TessellationFactors | |
{ | |
float edge[3] : SV_TessFactor; | |
float inside : SV_InsideTessFactor; | |
}; | |
CBUFFER_START(UnityPerMaterial) | |
float _TessellationUniform; | |
CBUFFER_END | |
TessellationFactors patchConstantFunction (InputPatch<Varyings, 3> patch) | |
{ | |
TessellationFactors f; | |
f.edge[0] = _TessellationUniform; | |
f.edge[1] = _TessellationUniform; | |
f.edge[2] = _TessellationUniform; | |
f.inside = _TessellationUniform; | |
return f; | |
} | |
[UNITY_domain("tri")] | |
[UNITY_outputcontrolpoints(3)] | |
[UNITY_outputtopology("triangle_cw")] | |
[UNITY_partitioning("integer")] | |
[UNITY_patchconstantfunc("patchConstantFunction")] | |
Varyings hull (InputPatch<Varyings, 3> patch, uint id : SV_OutputControlPointID) | |
{ | |
return patch[id]; | |
} | |
[UNITY_domain("tri")] | |
Varyings domain(TessellationFactors factors, OutputPatch<Varyings, 3> patch, float3 barycentricCoordinates : SV_DomainLocation) | |
{ | |
Varyings v; | |
#define MY_DOMAIN_PROGRAM_INTERPOLATE(fieldName) v.fieldName = \ | |
patch[0].fieldName * barycentricCoordinates.x + \ | |
patch[1].fieldName * barycentricCoordinates.y + \ | |
patch[2].fieldName * barycentricCoordinates.z; | |
MY_DOMAIN_PROGRAM_INTERPOLATE(positionWS) | |
MY_DOMAIN_PROGRAM_INTERPOLATE(positionOS) | |
MY_DOMAIN_PROGRAM_INTERPOLATE(normal) | |
MY_DOMAIN_PROGRAM_INTERPOLATE(tangent) | |
return v; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment