Update LODs in GrassField instead of in the chunks (better performance) + other performance improvements
This commit is contained in:
parent
31b4b88675
commit
534f4367a9
@ -49,7 +49,7 @@ Material:
|
||||
- _AOStrength: 1
|
||||
- _BendStrength: 1
|
||||
- _BladeBow: 0.3
|
||||
- _BladeWidth: 0.05
|
||||
- _BladeWidth: 0.0325
|
||||
- _ExtraSawnOnCutGras: 6
|
||||
- _FieldSize: 5
|
||||
- _LOD: 0
|
||||
|
||||
@ -49,7 +49,7 @@ Material:
|
||||
- _AOStrength: 1
|
||||
- _BendStrength: 1
|
||||
- _BladeBow: 0.3
|
||||
- _BladeWidth: 0.05
|
||||
- _BladeWidth: 0.0276
|
||||
- _ExtraSawnOnCutGras: 6
|
||||
- _FieldSize: 5
|
||||
- _LOD: 1
|
||||
|
||||
@ -49,7 +49,7 @@ Material:
|
||||
- _AOStrength: 1
|
||||
- _BendStrength: 1
|
||||
- _BladeBow: 0.3
|
||||
- _BladeWidth: 0.05
|
||||
- _BladeWidth: 0.047
|
||||
- _ExtraSawnOnCutGras: 6
|
||||
- _FieldSize: 5
|
||||
- _LOD: 2
|
||||
|
||||
File diff suppressed because one or more lines are too long
6
Assets/Scripts/BladeData.cs
Normal file
6
Assets/Scripts/BladeData.cs
Normal file
@ -0,0 +1,6 @@
|
||||
using UnityEngine;
|
||||
|
||||
public struct BladeData {
|
||||
public Vector3 rootPosition;
|
||||
public Vector3 tipOffset;
|
||||
}
|
||||
11
Assets/Scripts/BladeData.cs.meta
Normal file
11
Assets/Scripts/BladeData.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 683d2496822587844a7d37e611591c20
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -5,141 +5,136 @@ using UnityEngine.Serialization;
|
||||
|
||||
[RequireComponent(typeof(MeshRenderer)), RequireComponent(typeof(MeshFilter))]
|
||||
public class GrassChunk : MonoBehaviour {
|
||||
|
||||
[SerializeField] private Material materialLOD0;
|
||||
[SerializeField] private Material _materialLOD0;
|
||||
public Material MaterialLOD0 {
|
||||
get { return materialLOD0; }
|
||||
get { return _materialLOD0; }
|
||||
set {
|
||||
materialLOD0 = value;
|
||||
_materialLOD0 = value;
|
||||
UpdateLODAssets();
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField] private Mesh meshLOD0;
|
||||
[SerializeField] private Mesh _meshLOD0;
|
||||
public Mesh MeshLOD0 {
|
||||
get { return meshLOD0; }
|
||||
get { return _meshLOD0; }
|
||||
set {
|
||||
meshLOD0 = value;
|
||||
_meshLOD0 = value;
|
||||
UpdateLODAssets();
|
||||
UpdateLOD(); // a new mesh may change the AABB which may affect the LOD
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField] private bool enableShadowsLOD0;
|
||||
[SerializeField] private bool _enableShadowsLOD0;
|
||||
public bool EnableShadowsLOD0 {
|
||||
get { return enableShadowsLOD0; }
|
||||
get { return _enableShadowsLOD0; }
|
||||
set {
|
||||
enableShadowsLOD0 = value;
|
||||
_enableShadowsLOD0 = value;
|
||||
UpdateLODAssets();
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField] private Material materialLOD1;
|
||||
[SerializeField] private Material _materialLOD1;
|
||||
public Material MaterialLOD1 {
|
||||
get { return materialLOD1; }
|
||||
get { return _materialLOD1; }
|
||||
set {
|
||||
materialLOD1 = value;
|
||||
_materialLOD1 = value;
|
||||
UpdateLODAssets();
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField] private Mesh meshLOD1;
|
||||
[SerializeField] private Mesh _meshLOD1;
|
||||
public Mesh MeshLOD1 {
|
||||
get { return meshLOD1; }
|
||||
get { return _meshLOD1; }
|
||||
set {
|
||||
meshLOD1 = value;
|
||||
_meshLOD1 = value;
|
||||
UpdateLODAssets();
|
||||
UpdateLOD(); // a new mesh may change the AABB which may affect the LOD
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField] private bool enableShadowsLOD1;
|
||||
[SerializeField] private bool _enableShadowsLOD1;
|
||||
public bool EnableShadowsLOD1 {
|
||||
get { return enableShadowsLOD1; }
|
||||
get { return _enableShadowsLOD1; }
|
||||
set {
|
||||
enableShadowsLOD1 = value;
|
||||
_enableShadowsLOD1 = value;
|
||||
UpdateLODAssets();
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField] private Material materialLOD2;
|
||||
[SerializeField] private Material _materialLOD2;
|
||||
public Material MaterialLOD2 {
|
||||
get { return materialLOD2; }
|
||||
get { return _materialLOD2; }
|
||||
set {
|
||||
materialLOD2 = value;
|
||||
_materialLOD2 = value;
|
||||
UpdateLODAssets();
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField] private Mesh meshLOD2;
|
||||
[SerializeField] private Mesh _meshLOD2;
|
||||
public Mesh MeshLOD2 {
|
||||
get { return meshLOD2; }
|
||||
get { return _meshLOD2; }
|
||||
set {
|
||||
meshLOD2 = value;
|
||||
_meshLOD2 = value;
|
||||
UpdateLODAssets();
|
||||
UpdateLOD(); // a new mesh may change the AABB which may affect the LOD
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField] private bool enableShadowsLOD2;
|
||||
[SerializeField] private bool _enableShadowsLOD2;
|
||||
public bool EnableShadowsLOD2 {
|
||||
get { return enableShadowsLOD2; }
|
||||
get { return _enableShadowsLOD2; }
|
||||
set {
|
||||
enableShadowsLOD2 = value;
|
||||
_enableShadowsLOD2 = value;
|
||||
UpdateLODAssets();
|
||||
}
|
||||
}
|
||||
|
||||
private int lod = -1; // set to -1 so LOD.set does not exit early when initially called with 0
|
||||
private int _lod;
|
||||
public int LOD {
|
||||
get { return lod; }
|
||||
get { return _lod; }
|
||||
set {
|
||||
if (lod == value) return;
|
||||
if (_lod == value) return;
|
||||
|
||||
lod = value;
|
||||
_lod = value;
|
||||
UpdateLODAssets();
|
||||
}
|
||||
}
|
||||
|
||||
private void Update() {
|
||||
UpdateLOD();
|
||||
private MeshRenderer _meshRenderer;
|
||||
public MeshRenderer MeshRenderer {
|
||||
get {
|
||||
if (!_meshRenderer) {
|
||||
_meshRenderer = GetComponent<MeshRenderer>();
|
||||
}
|
||||
return _meshRenderer;
|
||||
}
|
||||
private set { _meshRenderer = value; }
|
||||
}
|
||||
|
||||
public void UpdateLOD() {
|
||||
var meshRenderer = GetComponent<MeshRenderer>();
|
||||
if (Camera.main) {
|
||||
// using meshRenderer.bounds may cause a loop where LOD switches constantly
|
||||
var sqrCamDist = meshRenderer.bounds.SqrDistance(Camera.main.transform.position);
|
||||
if (sqrCamDist > 20f * 20f) {
|
||||
LOD = 2;
|
||||
}
|
||||
else if (sqrCamDist > 5f * 5f) {
|
||||
LOD = 1;
|
||||
}
|
||||
else {
|
||||
LOD = 0;
|
||||
private MeshFilter _meshFilter;
|
||||
public MeshFilter MeshFilter {
|
||||
get {
|
||||
if (!_meshFilter) {
|
||||
_meshFilter = GetComponent<MeshFilter>();
|
||||
}
|
||||
return _meshFilter;
|
||||
}
|
||||
private set { _meshFilter = value; }
|
||||
}
|
||||
|
||||
private void UpdateLODAssets() {
|
||||
var meshRenderer = GetComponent<MeshRenderer>();
|
||||
var meshFilter = GetComponent<MeshFilter>();
|
||||
switch (lod) {
|
||||
switch (_lod) {
|
||||
case 0:
|
||||
meshRenderer.sharedMaterial = materialLOD0;
|
||||
meshRenderer.shadowCastingMode = EnableShadowsLOD0 ? ShadowCastingMode.On : ShadowCastingMode.Off;
|
||||
meshFilter.sharedMesh = meshLOD0;
|
||||
MeshRenderer.sharedMaterial = _materialLOD0;
|
||||
MeshRenderer.shadowCastingMode = EnableShadowsLOD0 ? ShadowCastingMode.On : ShadowCastingMode.Off;
|
||||
MeshFilter.sharedMesh = _meshLOD0;
|
||||
break;
|
||||
case 1:
|
||||
meshRenderer.sharedMaterial = materialLOD1;
|
||||
meshRenderer.shadowCastingMode = EnableShadowsLOD1 ? ShadowCastingMode.On : ShadowCastingMode.Off;
|
||||
meshFilter.sharedMesh = meshLOD1;
|
||||
MeshRenderer.sharedMaterial = _materialLOD1;
|
||||
MeshRenderer.shadowCastingMode = EnableShadowsLOD1 ? ShadowCastingMode.On : ShadowCastingMode.Off;
|
||||
MeshFilter.sharedMesh = _meshLOD1;
|
||||
break;
|
||||
case 2:
|
||||
meshRenderer.sharedMaterial = materialLOD2;
|
||||
meshRenderer.shadowCastingMode = EnableShadowsLOD2 ? ShadowCastingMode.On : ShadowCastingMode.Off;
|
||||
meshFilter.sharedMesh = meshLOD2;
|
||||
MeshRenderer.sharedMaterial = _materialLOD2;
|
||||
MeshRenderer.shadowCastingMode = EnableShadowsLOD2 ? ShadowCastingMode.On : ShadowCastingMode.Off;
|
||||
MeshFilter.sharedMesh = _meshLOD2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,65 +8,88 @@ using Random = UnityEngine.Random;
|
||||
public class GrassField : MonoBehaviour {
|
||||
public int size = 100;
|
||||
public int numChunks = 5;
|
||||
public int bladesPerMeter = 100;
|
||||
public GrassChunk chunkPrefab;
|
||||
public bool ignoreHeightForLOD = true;
|
||||
[Header("LOD 0")]
|
||||
public Material grassMaterialLOD0;
|
||||
public int bladesPerMeterLOD0 = 100;
|
||||
[Range(0f, 1f)] public float densityLOD0 = 1f;
|
||||
public bool enableShadowsLOD0 = true;
|
||||
[Header("LOD 1")]
|
||||
public Material grassMaterialLOD1;
|
||||
public int bladesPerMeterLOD1 = 50;
|
||||
[Range(0f, 1f)] public float densityLOD1 = 0.5f;
|
||||
public bool enableShadowsLOD1 = true;
|
||||
[Header("LOD 2")]
|
||||
public Material grassMaterialLOD2;
|
||||
public int bladesPerMeterLOD2 = 10;
|
||||
[Range(0f, 1f)] public float densityLOD2 = 0.2f;
|
||||
public bool enableShadowsLOD2 = false;
|
||||
|
||||
private struct BladeData {
|
||||
public Vector3 rootPosition;
|
||||
public Vector3 tipOffset;
|
||||
}
|
||||
// a 2D array would be cleaner, but it can't be serialized
|
||||
// serialize it, because the chunks are generated offline and used during runtime
|
||||
[SerializeField, HideInInspector] private GrassChunk[] _chunks;
|
||||
|
||||
// ReSharper disable Unity.PerformanceAnalysis
|
||||
public void GenerateGrassField() {
|
||||
Debug.Log("Generating grass field... ");
|
||||
|
||||
var bladesLOD0 = GenerateBladesRandom(size * size * bladesPerMeterLOD0);
|
||||
var bladesLOD1 = GenerateBladesRandom(size * size * bladesPerMeterLOD1);
|
||||
var bladesLOD2 = GenerateBladesRandom(size * size * bladesPerMeterLOD2);
|
||||
var bladesLOD0 = GrassMeshGeneration.GenerateBladesRandom(size, size * size * (int)(bladesPerMeter * densityLOD0));
|
||||
// var bladesLOD1 = GenerateBladesRandom(size, size * size * (int)(bladesPerMeter * densityLOD1));
|
||||
// var bladesLOD2 = GenerateBladesRandom(size, size * size * (int)(bladesPerMeter * densityLOD2));
|
||||
var bladesLOD1 = GrassMeshGeneration.GenerateBladesReduced(bladesLOD0, densityLOD1);
|
||||
var bladesLOD2 = GrassMeshGeneration.GenerateBladesReduced(bladesLOD1, densityLOD2 / densityLOD1);
|
||||
CreateChunks(bladesLOD0, bladesLOD1, bladesLOD2);
|
||||
|
||||
Debug.Log("Generating grass field done");
|
||||
}
|
||||
|
||||
private BladeData[] GenerateBladesRandom(int num) {
|
||||
var blades = new BladeData[num];
|
||||
|
||||
for (int i = 0; i < num; i++) {
|
||||
blades[i].rootPosition = new Vector3(
|
||||
Random.Range(0f, size), Random.Range(-0.25f, 0f), Random.Range(0f, size)
|
||||
);
|
||||
blades[i].tipOffset = new Vector3(Random.Range(-.8f, .8f), Random.Range(0.7f, 1f), Random.Range(-.8f, .8f));
|
||||
private void OnEnable() {
|
||||
if (_chunks.Length != numChunks * numChunks) {
|
||||
Debug.LogError(this.name + ": Existing chunks does not match numChunks.");
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
return blades;
|
||||
}
|
||||
|
||||
private BladeData[] GenerateBladesEven() {
|
||||
var blades = new BladeData[size * size * 100];
|
||||
private void Update() {
|
||||
UpdateLODs();
|
||||
}
|
||||
|
||||
for (int x = 0; x < size * 10; x++)
|
||||
for (int y = 0; y < size * 10; y++) {
|
||||
var i = x + y * size * 10;
|
||||
blades[i].rootPosition = new Vector3(x/10.0f, 0.0f, y/10.0f);
|
||||
blades[i].tipOffset = new Vector3(0.01f, 1.0f, 0.01f);
|
||||
private void UpdateLODs() {
|
||||
var cam = Camera.main;
|
||||
if (!cam) return;
|
||||
|
||||
if (_chunks.Length != numChunks * numChunks) return;
|
||||
|
||||
var camPos = cam.transform.position - transform.position; // cam position relative to grass field
|
||||
if (ignoreHeightForLOD) {
|
||||
camPos.y = 0; // ignore height
|
||||
}
|
||||
|
||||
var chunkSize = ((float)size) / numChunks;
|
||||
var bounds = new Bounds(
|
||||
new Vector3(0.5f * chunkSize, 0f, 0.5f * chunkSize),
|
||||
new Vector3(chunkSize, 0f, chunkSize)
|
||||
);
|
||||
|
||||
for (int x = 0; x < numChunks; x++)
|
||||
for (int y = 0; y < numChunks; y++) {
|
||||
var chunkPos = new Vector3(x, 0f, y) * chunkSize;
|
||||
var localCamPos = camPos - chunkPos; // cam position relative to chunk
|
||||
|
||||
return blades;
|
||||
var sqrCamDist = bounds.SqrDistance(localCamPos);
|
||||
if (sqrCamDist > 15f * 15f) {
|
||||
_chunks[x + y * numChunks].LOD = 2;
|
||||
}
|
||||
else if (sqrCamDist > 7.5f * 7.5f) {
|
||||
_chunks[x + y * numChunks].LOD = 1;
|
||||
}
|
||||
else {
|
||||
_chunks[x + y * numChunks].LOD = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateChunks(BladeData[] bladesLOD0, BladeData[] bladesLOD1, BladeData[] bladesLOD2) {
|
||||
GameObject chunks;
|
||||
GameObject chunksContainer;
|
||||
// If a child called "Chunks" exists, destroy it -> children are also destroyed
|
||||
var chunksTransform = gameObject.transform.Find("Chunks");
|
||||
if (chunksTransform != null) {
|
||||
@ -74,8 +97,8 @@ public class GrassField : MonoBehaviour {
|
||||
DestroyImmediate(chunksTransform.gameObject);
|
||||
}
|
||||
// create GameObject "Chunks" as a container for all GrassChunk objects
|
||||
chunks = new GameObject("Chunks");
|
||||
chunks.transform.SetParent(this.transform, false);
|
||||
chunksContainer = new GameObject("Chunks");
|
||||
chunksContainer.transform.SetParent(this.transform, false);
|
||||
|
||||
var chunkSize = ((float)size) / numChunks;
|
||||
// bounds of the chunk (relative to chunk) - do not consider y position
|
||||
@ -83,6 +106,7 @@ public class GrassField : MonoBehaviour {
|
||||
new Vector3(0.5f * chunkSize, 0.0f, 0.5f * chunkSize),
|
||||
new Vector3(chunkSize, 10000f, chunkSize)
|
||||
);
|
||||
_chunks = new GrassChunk[numChunks * numChunks];
|
||||
for (int x = 0; x < numChunks; x++)
|
||||
for (int y = 0; y < numChunks; y++) {
|
||||
var chunkPos = new Vector3(x, 0.0f, y) * chunkSize;
|
||||
@ -91,7 +115,7 @@ public class GrassField : MonoBehaviour {
|
||||
var meshLOD1 = GenerateChunkMesh(chunkPos, chunkBounds, bladesLOD1, "grass_chunk_" + x + "_" + y + "_lod1");
|
||||
var meshLOD2 = GenerateChunkMesh(chunkPos, chunkBounds, bladesLOD2, "grass_chunk_" + x + "_" + y + "_lod2");
|
||||
|
||||
var chunk = Instantiate(chunkPrefab, chunks.transform);
|
||||
var chunk = Instantiate(chunkPrefab, chunksContainer.transform);
|
||||
chunk.name = "Chunk [" + x + ", " + y +"]";
|
||||
chunk.transform.localPosition = chunkPos;
|
||||
|
||||
@ -104,7 +128,11 @@ public class GrassField : MonoBehaviour {
|
||||
chunk.EnableShadowsLOD0 = enableShadowsLOD0;
|
||||
chunk.EnableShadowsLOD1 = enableShadowsLOD1;
|
||||
chunk.EnableShadowsLOD2 = enableShadowsLOD2;
|
||||
|
||||
_chunks[x + y * numChunks] = chunk;
|
||||
}
|
||||
|
||||
UpdateLODs();
|
||||
}
|
||||
|
||||
private Mesh GenerateChunkMesh(Vector3 chunkPos, Bounds chunkBounds, BladeData[] blades, string meshName) {
|
||||
@ -147,17 +175,28 @@ public class GrassField : MonoBehaviour {
|
||||
}
|
||||
|
||||
private void OnDrawGizmos() {
|
||||
Gizmos.color = new Color(0f, 0.2f, 1f, 1f);
|
||||
var chunkSize = ((float)size) / numChunks;
|
||||
var bounds = new Bounds(
|
||||
new Vector3(0.5f * chunkSize, 0.0f, 0.5f * chunkSize),
|
||||
new Vector3(chunkSize, 0f, chunkSize)
|
||||
);
|
||||
|
||||
for (int x = 0; x < numChunks; x++)
|
||||
for (int y = 0; y < numChunks; y++) {
|
||||
var chunkPos = new Vector3(x, 0.0f, y) * chunkSize;
|
||||
Gizmos.DrawWireCube(bounds.center + chunkPos + transform.position, bounds.size);
|
||||
var tPos = transform.position;
|
||||
|
||||
for (int i = 0; i < numChunks + 1; i++) {
|
||||
Gizmos.color = new Color(0f, 0.3f, 1f, 1f);
|
||||
Gizmos.DrawLine(
|
||||
tPos + new Vector3(chunkSize * i, 0.01f, 0),
|
||||
tPos + new Vector3(chunkSize * i, 0.01f, 0) + new Vector3(0, 0, size)
|
||||
);
|
||||
Gizmos.DrawLine(
|
||||
tPos + new Vector3(0, 0.01f, chunkSize * i),
|
||||
tPos + new Vector3(0, 0.01f, chunkSize * i) + new Vector3(size, 0, 0)
|
||||
);
|
||||
}
|
||||
|
||||
var cam = Camera.main;
|
||||
if (!cam) return;
|
||||
var center = cam.transform.position;
|
||||
if (ignoreHeightForLOD) {
|
||||
center.y = 0f;
|
||||
}
|
||||
Gizmos.color = new Color(1.0f, 0.0f, 1f, 2f);
|
||||
Gizmos.DrawWireSphere(center, 7.5f);
|
||||
}
|
||||
}
|
||||
|
||||
44
Assets/Scripts/GrassMeshGeneration.cs
Normal file
44
Assets/Scripts/GrassMeshGeneration.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class GrassMeshGeneration {
|
||||
static public BladeData[] GenerateBladesRandom(int size, int num) {
|
||||
var blades = new BladeData[num];
|
||||
|
||||
for (int i = 0; i < num; i++) {
|
||||
blades[i].rootPosition = new Vector3(
|
||||
Random.Range(0f, size), Random.Range(-0.25f, 0f), Random.Range(0f, size)
|
||||
);
|
||||
blades[i].tipOffset = new Vector3(Random.Range(-.8f, .8f), Random.Range(0.7f, 1f), Random.Range(-.8f, .8f));
|
||||
}
|
||||
|
||||
return blades;
|
||||
}
|
||||
|
||||
// r = 0.5 means that the generated blades will have 50% of the blades of the input blades
|
||||
static public BladeData[] GenerateBladesReduced(BladeData[] from, float r) {
|
||||
List<BladeData> blades = new();
|
||||
|
||||
for (int i = 0; i < from.Length; i++) {
|
||||
if (Random.Range(0f, 1f) > r) continue;
|
||||
|
||||
BladeData blade = from[i];
|
||||
blades.Add(blade);
|
||||
}
|
||||
|
||||
return blades.ToArray();
|
||||
}
|
||||
|
||||
static public BladeData[] GenerateBladesEven(int size) {
|
||||
var blades = new BladeData[size * size * 100];
|
||||
|
||||
for (int x = 0; x < size * 10; x++)
|
||||
for (int y = 0; y < size * 10; y++) {
|
||||
var i = x + y * size * 10;
|
||||
blades[i].rootPosition = new Vector3(x/10.0f, 0.0f, y/10.0f);
|
||||
blades[i].tipOffset = new Vector3(0.01f, 1.0f, 0.01f);
|
||||
}
|
||||
|
||||
return blades;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/GrassMeshGeneration.cs.meta
Normal file
11
Assets/Scripts/GrassMeshGeneration.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e61ad39b14108e4999fc96bf87d9d78
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -35,14 +35,6 @@ const int ns = 11;
|
||||
[MaxVertexCount(4)]
|
||||
#endif
|
||||
void geom(point v2g IN[1], inout TriangleStream<g2f> triStream) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const float3 basePos = IN[0].vertex.xyz;
|
||||
const float3 tipPosBladeSpace = IN[0].tipOffset;
|
||||
const float3 tipPosObjectSpace = basePos + tipPosBladeSpace;
|
||||
@ -54,21 +46,17 @@ void geom(point v2g IN[1], inout TriangleStream<g2f> triStream) {
|
||||
const float3 basePosWS = mul(unity_ObjectToWorld, float4(basePos.xyz, 1.0)).xyz;
|
||||
const float cameraDistance = distance(_WorldSpaceCameraPos, basePosWS);
|
||||
|
||||
float randoVal = N21(basePos);
|
||||
|
||||
|
||||
#if _LOD_LOD_0
|
||||
if (randoVal * 10.0 > max(1, 2 / (cameraDistance / 20)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
#elif _LOD_LOD_1
|
||||
if (randoVal * 10.0 > max(1, 5 / (cameraDistance / 20)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// randomly cull grass blades
|
||||
// float randoVal = N21(basePos);
|
||||
// #if _LOD_LOD_0
|
||||
// if (randoVal * 10.0 > max(1, 2 / (cameraDistance / 20))) {
|
||||
// return;
|
||||
// }
|
||||
// #elif _LOD_LOD_1
|
||||
// if (randoVal * 10.0 > max(1, 5 / (cameraDistance / 20))) {
|
||||
// return;
|
||||
// }
|
||||
// #endif
|
||||
|
||||
float halfWidth = _BladeWidth/2;
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user