97ac0f71f5
https://assetstore.unity.com/packages/tools/generative-ai/synaptic-ai-pro-natural-language-control-for-unity-336030
177 lines
4.4 KiB
Plaintext
177 lines
4.4 KiB
Plaintext
// Synaptic AI Pro - GPU Grass Instancing Compute Shader
|
|
// Handles culling, LOD, and instance buffer generation for massive grass rendering
|
|
|
|
#pragma kernel CSMain
|
|
#pragma kernel CSClear
|
|
|
|
// Grass instance data
|
|
struct GrassInstance
|
|
{
|
|
float3 position;
|
|
float3 normal;
|
|
float2 uv;
|
|
float height;
|
|
float width;
|
|
float rotation;
|
|
float stiffness;
|
|
float windPhase;
|
|
};
|
|
|
|
// Indirect draw arguments
|
|
struct IndirectArgs
|
|
{
|
|
uint vertexCountPerInstance;
|
|
uint instanceCount;
|
|
uint startVertexLocation;
|
|
uint startInstanceLocation;
|
|
};
|
|
|
|
// Input buffers
|
|
StructuredBuffer<GrassInstance> _SourceInstances;
|
|
RWStructuredBuffer<GrassInstance> _CulledInstances;
|
|
RWStructuredBuffer<IndirectArgs> _IndirectArgs;
|
|
|
|
// Counters
|
|
RWStructuredBuffer<uint> _InstanceCounter;
|
|
|
|
// Parameters
|
|
float4x4 _ViewProjectionMatrix;
|
|
float4x4 _ViewMatrix;
|
|
float3 _CameraPosition;
|
|
float3 _CameraForward;
|
|
float _FrustumCullMargin;
|
|
float _MaxRenderDistance;
|
|
float _LOD0Distance;
|
|
float _LOD1Distance;
|
|
float _LOD2Distance;
|
|
float _DensityFalloff;
|
|
float _Time;
|
|
|
|
// Wind parameters
|
|
float3 _WindDirection;
|
|
float _WindStrength;
|
|
float _WindSpeed;
|
|
float _WindFrequency;
|
|
|
|
// Frustum planes
|
|
float4 _FrustumPlanes[6];
|
|
|
|
// Instance counts
|
|
uint _TotalInstances;
|
|
uint _MaxVisibleInstances;
|
|
|
|
// LOD group (0, 1, 2)
|
|
uint _LODGroup;
|
|
|
|
// Check if point is inside frustum
|
|
bool IsInsideFrustum(float3 position, float margin)
|
|
{
|
|
for (int i = 0; i < 6; i++)
|
|
{
|
|
float dist = dot(_FrustumPlanes[i].xyz, position) + _FrustumPlanes[i].w;
|
|
if (dist < -margin)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Calculate LOD level based on distance
|
|
uint GetLODLevel(float distance)
|
|
{
|
|
if (distance < _LOD0Distance)
|
|
return 0;
|
|
else if (distance < _LOD1Distance)
|
|
return 1;
|
|
else if (distance < _LOD2Distance)
|
|
return 2;
|
|
else
|
|
return 3; // Culled
|
|
}
|
|
|
|
// Hash function for consistent randomization
|
|
float Hash(float2 p)
|
|
{
|
|
return frac(sin(dot(p, float2(127.1, 311.7))) * 43758.5453);
|
|
}
|
|
|
|
// Wind offset calculation (matches shader)
|
|
float3 CalculateWindOffset(float3 position, float height, float windPhase)
|
|
{
|
|
float3 windDir = normalize(_WindDirection);
|
|
|
|
// Main wind wave
|
|
float phase = dot(position.xz, windDir.xz) * _WindFrequency + _Time * _WindSpeed + windPhase;
|
|
float wave = sin(phase) * 0.5 + 0.5;
|
|
|
|
// Gust
|
|
float gustPhase = _Time * 0.5 + windPhase;
|
|
float gust = sin(gustPhase) * sin(gustPhase * 2.3) * 0.3;
|
|
|
|
float windFactor = (wave + gust) * _WindStrength * height * height;
|
|
|
|
return windDir * windFactor;
|
|
}
|
|
|
|
[numthreads(256, 1, 1)]
|
|
void CSMain(uint3 id : SV_DispatchThreadID)
|
|
{
|
|
if (id.x >= _TotalInstances)
|
|
return;
|
|
|
|
GrassInstance instance = _SourceInstances[id.x];
|
|
|
|
// Calculate distance to camera
|
|
float3 toCamera = _CameraPosition - instance.position;
|
|
float distance = length(toCamera);
|
|
|
|
// Distance culling
|
|
if (distance > _MaxRenderDistance)
|
|
return;
|
|
|
|
// LOD selection
|
|
uint lodLevel = GetLODLevel(distance);
|
|
if (lodLevel != _LODGroup)
|
|
return;
|
|
|
|
// Density falloff (render fewer instances at distance)
|
|
if (lodLevel > 0)
|
|
{
|
|
float densityThreshold = 1.0 - (distance - _LOD0Distance) / (_MaxRenderDistance - _LOD0Distance) * _DensityFalloff;
|
|
float randomValue = Hash(instance.position.xz);
|
|
if (randomValue > densityThreshold)
|
|
return;
|
|
}
|
|
|
|
// Calculate wind-affected position for frustum culling
|
|
float3 windOffset = CalculateWindOffset(instance.position, instance.height, instance.windPhase);
|
|
float3 tipPosition = instance.position + float3(0, instance.height, 0) + windOffset;
|
|
|
|
// Frustum culling (check both root and tip)
|
|
float cullMargin = _FrustumCullMargin + instance.height;
|
|
if (!IsInsideFrustum(instance.position, cullMargin) && !IsInsideFrustum(tipPosition, cullMargin))
|
|
return;
|
|
|
|
// Backface culling optimization (skip grass facing away)
|
|
float3 grassForward = instance.normal;
|
|
float facingRatio = dot(normalize(toCamera), grassForward);
|
|
// Allow grass to be seen from both sides, but skip if completely edge-on
|
|
if (abs(facingRatio) < 0.1)
|
|
return;
|
|
|
|
// Add to visible buffer
|
|
uint index;
|
|
InterlockedAdd(_InstanceCounter[0], 1, index);
|
|
|
|
if (index < _MaxVisibleInstances)
|
|
{
|
|
_CulledInstances[index] = instance;
|
|
}
|
|
}
|
|
|
|
[numthreads(1, 1, 1)]
|
|
void CSClear(uint3 id : SV_DispatchThreadID)
|
|
{
|
|
_InstanceCounter[0] = 0;
|
|
_IndirectArgs[0].instanceCount = 0;
|
|
}
|