diff options
| author | yum <yum.food.vr@gmail.com> | 2026-02-16 16:32:00 -0800 |
|---|---|---|
| committer | yum <yum.food.vr@gmail.com> | 2026-02-16 16:36:24 -0800 |
| commit | 864c2ba12dc864d9cb55cb797ba8919bee5b5913 (patch) | |
| tree | aa6cd98a71e4ef05d23f762127d3759a4a3e3e21 /Scripts/InstanceGrass.cs | |
| parent | 6504b2c4631bab477838548167b88c1052eac263 (diff) | |
Add instancing distance culling, scale deformation
* GPU instance distance culling now takes a min/max range
* Fold recovers ops from material, allowing state to persist across
editor restarts
* Add scale node to vertex deformation framework
* Remove fold presets - dumb LLM idea, unused
* Drop more "undeform" code; unused, was for ray marching, which does
not work well
* Fix reflection energy compensation; was using cloth math, which makes
things too bright
Diffstat (limited to 'Scripts/InstanceGrass.cs')
| -rwxr-xr-x | Scripts/InstanceGrass.cs | 70 |
1 files changed, 52 insertions, 18 deletions
diff --git a/Scripts/InstanceGrass.cs b/Scripts/InstanceGrass.cs index 3c0d8f6..d6a1eab 100755 --- a/Scripts/InstanceGrass.cs +++ b/Scripts/InstanceGrass.cs @@ -15,6 +15,7 @@ public class InstanceGrass : UdonSharpBehaviour // The extent along each cardinal axis where instances will be rendered, in // meters. I.e. render inside a cube with edges this long. [SerializeField] public Vector3 extent_meters_; + [SerializeField] public float min_distance_; [SerializeField] public Vector3 angle_randomization_; [SerializeField] public float scale_randomization_; @@ -34,12 +35,15 @@ public class InstanceGrass : UdonSharpBehaviour // Track fields to detect runtime changes. private float density_live_; private Vector3 extent_meters_live_; + private float min_distance_live_; private Vector3 angle_randomization_live_; private float scale_randomization_live_; // GPU-specific resources private RenderTexture instance_data_tex_; private Matrix4x4[] identity_transforms_; // All identity matrices + private float[] instance_ids_; + private MaterialPropertyBlock instance_properties_; private int tex_width_; private int tex_height_; @@ -67,6 +71,7 @@ public class InstanceGrass : UdonSharpBehaviour Material[] materials = mesh_renderer.sharedMaterials; if (materials != null && materials.Length > 0) { instance_material_ = materials[0]; + instance_material_.enableInstancing = true; } else { Debug.LogError("[Grass::Debug] MeshRenderer has no materials."); } @@ -117,26 +122,29 @@ public class InstanceGrass : UdonSharpBehaviour instance_data_tex_.filterMode = FilterMode.Point; instance_data_tex_.Create(); - // Create transform array with encoded instance IDs - must match actual draw count - int actual_draw_count = tex_width_ * tex_height_; - if (identity_transforms_ == null || identity_transforms_.Length != actual_draw_count) { - identity_transforms_ = new Matrix4x4[actual_draw_count]; - for (int i = 0; i < actual_draw_count; i++) { - // Encode instance ID in the matrix translation component - // We'll put the ID in the X component, which the shader will read - Matrix4x4 m = Matrix4x4.identity; - m.m03 = i; // Store instance ID in translation.x - identity_transforms_[i] = m; + // Create transform array and per-instance IDs. + if (identity_transforms_ == null || identity_transforms_.Length != total_instances_) { + identity_transforms_ = new Matrix4x4[total_instances_]; + for (int i = 0; i < total_instances_; i++) { + identity_transforms_[i] = Matrix4x4.identity; + } + } + + if (instance_ids_ == null || instance_ids_.Length != total_instances_) { + instance_ids_ = new float[total_instances_]; + for (int i = 0; i < total_instances_; i++) { + instance_ids_[i] = i; } } if (mesh_ != null) { - // Set huge bounds so culling doesn't interfere - mesh_.bounds = new Bounds(Vector3.zero, Vector3.one * 10000f); + // Prevent frustum culling from clipping instanced draws that move in-shader. + mesh_.bounds = new Bounds(Vector3.zero, Vector3.one * 100000f); } density_live_ = density_; extent_meters_live_ = extent_meters_; + min_distance_live_ = min_distance_; angle_randomization_live_ = angle_randomization_; scale_randomization_live_ = scale_randomization_; @@ -182,6 +190,7 @@ public class InstanceGrass : UdonSharpBehaviour // Reinitialize if any config changed at runtime. if (density_ != density_live_ || extent_meters_ != extent_meters_live_ || + min_distance_ != min_distance_live_ || angle_randomization_ != angle_randomization_live_ || scale_randomization_ != scale_randomization_live_) { Init(); @@ -194,9 +203,12 @@ public class InstanceGrass : UdonSharpBehaviour VRCPlayerApi lcl_player = Networking.LocalPlayer; Vector3 player_pos = lcl_player.GetPosition(); - int grid_x = Mathf.FloorToInt(player_pos.x * inv_cell_dim_.x); - int grid_y = Mathf.FloorToInt(player_pos.y * inv_cell_dim_.y); - int grid_z = Mathf.FloorToInt(player_pos.z * inv_cell_dim_.z); + + // Only update grid position on axes with extent >= 1cm + // For axes with smaller extent, keep grid at 0 (don't move with player) + int grid_x = extent_meters_.x >= 0.01f ? Mathf.FloorToInt(player_pos.x * inv_cell_dim_.x) : 0; + int grid_y = extent_meters_.y >= 0.01f ? Mathf.FloorToInt(player_pos.y * inv_cell_dim_.y) : 0; + int grid_z = extent_meters_.z >= 0.01f ? Mathf.FloorToInt(player_pos.z * inv_cell_dim_.z) : 0; int half_x = count_.x / 2; int half_y = count_.y / 2; @@ -222,20 +234,24 @@ public class InstanceGrass : UdonSharpBehaviour Quaternion rot = base_transform_.localRotation; instance_material_.SetVector("_Instance_Texture_Offset_Base_Rotation", new Vector4(rot.x, rot.y, rot.z, rot.w)); + // Distance culling parameters + instance_material_.SetFloat("_Instance_Distance_Culling_Min_Distance", min_distance_); + float max_distance = Mathf.Max(extent_meters_.x, Mathf.Max(extent_meters_.y, extent_meters_.z)); + instance_material_.SetFloat("_Instance_Distance_Culling_Max_Distance", max_distance * 0.5f); + if (Time.frameCount % 300 == 0) { Debug.Log($"[Grass::Debug] Drawing {total_instances_} GPU instances"); Debug.Log($"[Grass::Debug] GridCount={count_}, TexDim={tex_width_}x{tex_height_}, CellDim={cell_dim_}"); } // Draw instances with identity transforms - GPU handles everything - // Must draw tex_width * tex_height instances to match texture VRCGraphics.DrawMeshInstanced( mesh_, 0, instance_material_, identity_transforms_, total_instances_, - null, + GetInstanceProperties(), UnityEngine.Rendering.ShadowCastingMode.Off, true, 0, @@ -246,6 +262,15 @@ public class InstanceGrass : UdonSharpBehaviour initialized_ = true; } + private MaterialPropertyBlock GetInstanceProperties() { + if (instance_properties_ == null) { + instance_properties_ = new MaterialPropertyBlock(); + } + + instance_properties_.SetFloatArray("_Instance_ID", instance_ids_); + return instance_properties_; + } + void OnDestroy() { if (instance_data_tex_ != null) { instance_data_tex_.Release(); @@ -288,7 +313,16 @@ public class InstanceGrassEditor : Editor return; } - string assetPath = "Assets/yum_food/3ner/Grass_Generated/Grass_generated.mat"; + // Get hierarchy path + Transform current = script.transform; + string hierarchyPath = current.name; + while (current.parent != null) + { + current = current.parent; + hierarchyPath = current.name + "/" + hierarchyPath; + } + + string assetPath = $"Assets/yum_food/3ner/Grass_Generated/{hierarchyPath}/Grass_generated.mat"; // Create or load existing material Material blitMat = AssetDatabase.LoadAssetAtPath<Material>(assetPath); |
