diff --git a/.gitignore b/.gitignore index 0f922d1..09d5506 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Custom additions .vscode/ +.vsconfig .idea/ # This .gitignore file should be placed at the root of your Unity project directory diff --git a/.vsconfig b/.vsconfig deleted file mode 100644 index f019fd0..0000000 --- a/.vsconfig +++ /dev/null @@ -1,6 +0,0 @@ -{ - "version": "1.0", - "components": [ - "Microsoft.VisualStudio.Workload.ManagedGame" - ] -} diff --git a/Assets/Materials/Grass.mat b/Assets/Materials/Grass.mat index 2be98ad..5660b27 100644 --- a/Assets/Materials/Grass.mat +++ b/Assets/Materials/Grass.mat @@ -28,6 +28,10 @@ Material: m_Texture: {fileID: 2800000, guid: bbec7f79ed7356243ae0604d95144b77, type: 3} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} + - _FieldColor: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} - _FrabVarianz: m_Texture: {fileID: 2800000, guid: 9bb4e817f09d0ea4cbab03f772e01910, type: 3} m_Scale: {x: 1, y: 1} @@ -38,9 +42,10 @@ Material: - _BendStrength: 1 - _BladeBow: 0.195 - _BladeWidth: 0.0274 - - _BladeWidthTexStrength: 1 + - _BladeWidthTexStrength: 0.74 - _LOD: 0 - _Metallic: 0 + - _NormalCurvature: 1 - _Roughness: 0 - _SSSStrength: 1 - _Translucency: 1.81 diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity index 071391c..e1a64d5 100644 --- a/Assets/Scenes/SampleScene.unity +++ b/Assets/Scenes/SampleScene.unity @@ -64477,7 +64477,7 @@ MeshFilter: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 434496868} - m_Mesh: {fileID: 1114929778} + m_Mesh: {fileID: 2061544436} --- !u!23 &434496871 MeshRenderer: m_ObjectHideFlags: 0 @@ -64498,7 +64498,7 @@ MeshRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 2100000, guid: d979d9bbf3760d948a452ba192cbf563, type: 2} + - {fileID: 2100000, guid: 91fb1ec3cf2133a4b931798a8822d055, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -140790,6 +140790,7 @@ GameObject: - component: {fileID: 963194227} - component: {fileID: 963194226} - component: {fileID: 963194229} + - component: {fileID: 963194230} m_Layer: 0 m_Name: Main Camera m_TagString: MainCamera @@ -140864,14 +140865,26 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 963194225} serializedVersion: 2 - m_LocalRotation: {x: 0.23506844, y: 0.3851214, z: -0.1021131, w: 0.8865649} - m_LocalPosition: {x: 6.34, y: 2.02, z: 3.8} + m_LocalRotation: {x: 0.23339066, y: 0.37273654, z: -0.097438015, w: 0.8928058} + m_LocalPosition: {x: 6.6, y: 3.5, z: 4.2} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_LocalEulerAnglesHint: {x: 29.7, y: 46.96, z: 0} + m_LocalEulerAnglesHint: {x: 29.3, y: 45.32, z: 0} --- !u!114 &963194229 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 963194225} + m_Enabled: 0 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 78c9485abe801d74184e00172d4b42c4, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!114 &963194230 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -140880,7 +140893,7 @@ MonoBehaviour: m_GameObject: {fileID: 963194225} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 78c9485abe801d74184e00172d4b42c4, type: 3} + m_Script: {fileID: 11500000, guid: d9be733811a3b0342953552aa8d5ab8a, type: 3} m_Name: m_EditorClassIdentifier: --- !u!43 &963631122 @@ -241007,111 +241020,6 @@ Mesh: offset: 0 size: 0 path: ---- !u!1 &1663409491 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1663409495} - - component: {fileID: 1663409494} - - component: {fileID: 1663409493} - - component: {fileID: 1663409492} - m_Layer: 0 - m_Name: Cube - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!65 &1663409492 -BoxCollider: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1663409491} - m_Material: {fileID: 0} - m_IncludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_ExcludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_LayerOverridePriority: 0 - m_IsTrigger: 0 - m_ProvidesContacts: 0 - m_Enabled: 1 - serializedVersion: 3 - m_Size: {x: 1, y: 1, z: 1} - m_Center: {x: 0, y: 0, z: 0} ---- !u!23 &1663409493 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1663409491} - m_Enabled: 1 - m_CastShadows: 1 - m_ReceiveShadows: 1 - m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 - m_MotionVectors: 1 - m_LightProbeUsage: 1 - m_ReflectionProbeUsage: 1 - m_RayTracingMode: 2 - m_RayTraceProcedural: 0 - m_RenderingLayerMask: 1 - m_RendererPriority: 0 - m_Materials: - - {fileID: 2100000, guid: 944161d6703f1e244a9216da9f82501b, type: 2} - m_StaticBatchInfo: - firstSubMesh: 0 - subMeshCount: 0 - m_StaticBatchRoot: {fileID: 0} - m_ProbeAnchor: {fileID: 0} - m_LightProbeVolumeOverride: {fileID: 0} - m_ScaleInLightmap: 1 - m_ReceiveGI: 1 - m_PreserveUVs: 0 - m_IgnoreNormalsForChartDetection: 0 - m_ImportantGI: 0 - m_StitchLightmapSeams: 1 - m_SelectedEditorRenderState: 3 - m_MinimumChartSize: 4 - m_AutoUVMaxDistance: 0.5 - m_AutoUVMaxAngle: 89 - m_LightmapParameters: {fileID: 0} - m_SortingLayerID: 0 - m_SortingLayer: 0 - m_SortingOrder: 0 - m_AdditionalVertexStreams: {fileID: 0} ---- !u!33 &1663409494 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1663409491} - m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} ---- !u!4 &1663409495 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1663409491} - serializedVersion: 2 - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 7.656, y: 0.872, z: 0.544} - m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 - m_Children: [] - m_Father: {fileID: 0} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!43 &1663750196 Mesh: m_ObjectHideFlags: 0 @@ -290519,7 +290427,6 @@ MonoBehaviour: numChunks: 20 bladesPerSquareMeter: 100 chunkPrefab: {fileID: 6414307754184109852, guid: abb47bb4e9622f7428302dfa4730d722, type: 3} - ignoreHeightForLOD: 0 grassMaterialLOD0: {fileID: 2100000, guid: d979d9bbf3760d948a452ba192cbf563, type: 2} minDistanceLOD0: 3.5 densityLOD0: 1 @@ -290946,7 +290853,7 @@ Transform: m_GameObject: {fileID: 2016058935} serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 5.11, y: 0, z: 2.71} + m_LocalPosition: {x: 5.31, y: 0, z: 2.19} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: @@ -308722,4 +308629,3 @@ SceneRoots: - {fileID: 2016058937} - {fileID: 513204364} - {fileID: 934953871} - - {fileID: 1663409495} diff --git a/Assets/Scripts/GrassField.cs b/Assets/Scripts/GrassField.cs index 0dc2cc4..adef5a7 100644 --- a/Assets/Scripts/GrassField.cs +++ b/Assets/Scripts/GrassField.cs @@ -1,37 +1,30 @@ using System; using System.Collections.Generic; using UnityEngine; -using UnityEngine.Serialization; public class GrassField : MonoBehaviour { public int size = 100; public int numChunks = 20; public int bladesPerSquareMeter = 100; public GrassChunk chunkPrefab; - public bool ignoreHeightForLOD = true; - [Header("LOD 0")] - public Material grassMaterialLOD0; + [Header("LOD 0")] public Material grassMaterialLOD0; public float minDistanceLOD0 = 2f; [Range(0f, 1f)] public float densityLOD0 = 1f; public bool enableShadowsLOD0 = true; - [Header("LOD 1")] - public float distanceLOD1 = 5f; + [Header("LOD 1")] public float distanceLOD1 = 5f; public Material grassMaterialLOD1; [Range(0f, 1f)] public float densityLOD1 = 0.5f; public bool enableShadowsLOD1 = true; - [Header("LOD 2")] - public float distanceLOD2 = 15f; + [Header("LOD 2")] public float distanceLOD2 = 15f; public Material grassMaterialLOD2; [Range(0f, 1f)] public float densityLOD2 = 0.25f; public bool enableShadowsLOD2 = false; - [Header("LOD 3")] - public float distanceLOD3 = 25f; + [Header("LOD 3")] public float distanceLOD3 = 25f; public Material grassMaterialLOD3; [Range(0f, 1f)] public float densityLOD3 = 0.125f; public bool enableShadowsLOD3 = false; - + private const float SQRT_2 = 1.4142135623730f; - private static readonly int pId_WorldSpaceCameraPosEditor = Shader.PropertyToID("_WorldSpaceCameraPosEditor"); private static readonly int pId_TransitionRange = Shader.PropertyToID("_TransitionRange"); // a 2D array would be cleaner, but it can't be serialized @@ -49,12 +42,12 @@ public class GrassField : MonoBehaviour { var camPosWorld = Camera.main ? Camera.main.transform.position : Vector3.zero; UpdateLODs(camPosWorld); - UpdateMaterials(camPosWorld); + UpdateMaterials(); } public void UpdateLODsAndMaterials() { var camPosWorld = Camera.main ? Camera.main.transform.position : Vector3.zero; - UpdateMaterials(camPosWorld); + UpdateMaterials(); UpdateLODs(camPosWorld); } @@ -64,11 +57,12 @@ public class GrassField : MonoBehaviour { if (distanceLOD2 < minDistLOD2) { Debug.LogWarning("It is recommended that minDistLOD2 is greater than " + minDistLOD2 + "."); } + var minDistLOD3 = distanceLOD2 + chunkSize * SQRT_2; if (distanceLOD3 < minDistLOD3) { Debug.LogWarning("It is recommended that minDistLOD3 is greater than " + minDistLOD3 + "."); } - + if (_chunks.Length != numChunks * numChunks) { Debug.LogError(this.name + ": Existing chunks does not match numChunks."); gameObject.SetActive(false); @@ -81,26 +75,24 @@ public class GrassField : MonoBehaviour { UpdateLODsAndMaterials(); } - private void FixedUpdate() { + private void Update() { +#if UNITY_EDITOR + // LOD distances might have been changed via Inspector -> Update materials + // Camera might have been moved -> Update LODs + UpdateLODsAndMaterials(); +#else + if (_chunks.Length != numChunks * numChunks) return; var cam = Camera.main; if (!cam) return; - var camPosWorld = cam.transform.position; - -#if UNITY_EDITOR - // update LOD distances and camera position - UpdateMaterials(camPosWorld); + // while the game is running, only update the chunks near the camera + // if the camera moved a great distance since the last frame, this fails + UpdateLODs(cam.transform.position, false); #endif - - if (_chunks.Length != numChunks * numChunks) return; - UpdateLODs(camPosWorld, false); // while the game is running, only the chunks near the camera are updated } private void UpdateLODs(Vector3 camPosWorld, bool forceCompleteCheck = true) { var camPos = camPosWorld - transform.position; // cam position relative to grass field - if (ignoreHeightForLOD) { - camPos.y = 0; - } - + var chunkSize = ((float)size) / numChunks; var bounds = new Bounds( new Vector3(0.5f * chunkSize, 0f, 0.5f * chunkSize), @@ -109,7 +101,7 @@ public class GrassField : MonoBehaviour { // small performance difference (0.13ms vs 0.02ms on a 100m 30x30 grid, LOD dist 26.6, camera at corner) var range = forceCompleteCheck - ? new GridRange{ fromX = 0, toX = numChunks-1, fromZ = 0, toZ = numChunks-1 } + ? new GridRange { fromX = 0, toX = numChunks - 1, fromZ = 0, toZ = numChunks - 1 } : GetLODGridRange(camPos, chunkSize); for (int x = range.fromX; x <= range.toX; x++) @@ -142,17 +134,17 @@ public class GrassField : MonoBehaviour { private GridRange GetLODGridRange(Vector3 camPos, float chunkSize) { GridRange range; - + var maxCamMove = 3f; // assume that the camera did not move further than this distance var fromXWorld = camPos.x - distanceLOD3 - maxCamMove; var toXWorld = camPos.x + distanceLOD3 + maxCamMove; var fromZWorld = camPos.z - distanceLOD3 - maxCamMove; var toZWorld = camPos.z + distanceLOD3 + maxCamMove; - - range.fromX = Math.Max( 0, (int) (fromXWorld / chunkSize) ); - range.toX = Math.Max( 0, (int) (toXWorld / chunkSize) ); - range.fromZ = Math.Max( 0, (int) (fromZWorld / chunkSize) ); - range.toZ = (int) (toZWorld / chunkSize); + + range.fromX = Math.Max(0, (int)(fromXWorld / chunkSize)); + range.toX = Math.Max(0, (int)(toXWorld / chunkSize)); + range.fromZ = Math.Max(0, (int)(fromZWorld / chunkSize)); + range.toZ = (int)(toZWorld / chunkSize); range.fromX = Math.Clamp(range.fromX, 0, numChunks - 1); range.toX = Math.Clamp(range.toX, 0, numChunks - 1); range.fromZ = Math.Clamp(range.fromZ, 0, numChunks - 1); @@ -161,28 +153,19 @@ public class GrassField : MonoBehaviour { return range; } - private void UpdateMaterials(Vector3 camPosWorld) { + private void UpdateMaterials() { var chunkSize = ((float)size) / numChunks; var chunkDiagonalLength = chunkSize * SQRT_2; var transition0From = minDistanceLOD0; - var transition0To = distanceLOD1; + var transition0To = distanceLOD1; var transition1From = distanceLOD1 + chunkDiagonalLength; - var transition1To = distanceLOD2; + var transition1To = distanceLOD2; var transition2From = distanceLOD2 + chunkDiagonalLength; - var transition2To = distanceLOD3; + var transition2To = distanceLOD3; grassMaterialLOD0.SetVector(pId_TransitionRange, new Vector2(transition0From, transition0To)); grassMaterialLOD1.SetVector(pId_TransitionRange, new Vector2(transition1From, transition1To)); grassMaterialLOD2.SetVector(pId_TransitionRange, new Vector2(transition2From, transition2To)); -#if UNITY_EDITOR - if (ignoreHeightForLOD) { - camPosWorld.y = 0; - } - grassMaterialLOD0.SetVector(pId_WorldSpaceCameraPosEditor, camPosWorld); - grassMaterialLOD1.SetVector(pId_WorldSpaceCameraPosEditor, camPosWorld); - grassMaterialLOD2.SetVector(pId_WorldSpaceCameraPosEditor, camPosWorld); - grassMaterialLOD3.SetVector(pId_WorldSpaceCameraPosEditor, camPosWorld); -#endif } private void CreateChunks( @@ -195,6 +178,7 @@ public class GrassField : MonoBehaviour { // Destroy(obj) does not work in edit mode DestroyImmediate(chunksTransform.gameObject); } + // create GameObject "Chunks" as a container for all GrassChunk objects chunksContainer = new GameObject("Chunks"); chunksContainer.transform.SetParent(this.transform, false); @@ -216,7 +200,7 @@ public class GrassField : MonoBehaviour { var meshLOD3 = GenerateChunkMesh(chunkPos, chunkBounds, bladesLOD3, "grass_chunk_" + x + "_" + y + "_lod3"); var chunk = Instantiate(chunkPrefab, chunksContainer.transform); - chunk.name = "Chunk [" + x + ", " + y +"]"; + chunk.name = "Chunk [" + x + ", " + y + "]"; chunk.transform.localPosition = chunkPos; chunk.MaterialLOD0 = grassMaterialLOD0; @@ -260,6 +244,7 @@ public class GrassField : MonoBehaviour { mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; Debug.LogWarning(meshName + " has more than 65536 grass blades. Mesh.indexFormat set to UInt32."); } + mesh.SetVertices(rootPositions); mesh.SetUVs(0, tipOffsets_missingInNextLOD); // Vector4 UVs // AABB gets calculated automatically (considers all root positions) @@ -272,13 +257,16 @@ public class GrassField : MonoBehaviour { const float height = 1f; // a blade should not be higher than this mesh.bounds = new Bounds( previousBounds.center + new Vector3(0f, height * 0.5f, 0f), - previousBounds.size + new Vector3(2f*width, height, 2f*width) + previousBounds.size + new Vector3(2f * width, height, 2f * width) ); return mesh; } - + private void OnDrawGizmos() { + // when in editor, update LODs every frame. nothing to do with gizmos + UpdateLODsAndMaterials(); + var chunkSize = ((float)size) / numChunks; var tPos = transform.position; @@ -299,17 +287,12 @@ public class GrassField : MonoBehaviour { if (!cam) return; var center = cam.transform.position; var oldMatrix = Gizmos.matrix; - if (ignoreHeightForLOD) { - center.y = 0.01f; - // draw a circle instead of a sphere - Gizmos.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1f, 0.001f, 1f)); - } - + Gizmos.color = new Color(0f, 0.3f, 1f, 1f); Gizmos.DrawWireSphere(center, distanceLOD1); Gizmos.DrawWireSphere(center, distanceLOD2); Gizmos.DrawWireSphere(center, distanceLOD3); - + Gizmos.color = new Color(1.0f, 1.0f, 0.0f, 1f); var minDistLOD0 = minDistanceLOD0; var minDistLOD1 = distanceLOD1 + chunkSize * SQRT_2; @@ -317,7 +300,7 @@ public class GrassField : MonoBehaviour { Gizmos.DrawWireSphere(center, minDistLOD0); Gizmos.DrawWireSphere(center, minDistLOD1); Gizmos.DrawWireSphere(center, minDistLOD2); - + Gizmos.matrix = oldMatrix; } } diff --git a/Assets/Scripts/MainCameraPositionShaderSetter.cs b/Assets/Scripts/MainCameraPositionShaderSetter.cs new file mode 100644 index 0000000..5aaadce --- /dev/null +++ b/Assets/Scripts/MainCameraPositionShaderSetter.cs @@ -0,0 +1,18 @@ +using System; +using UnityEngine; +using UnityEditor; + +[ExecuteAlways] +public class MainCameraPositionShaderSetter : MonoBehaviour { + private static readonly int pId_MainCameraPosition = Shader.PropertyToID("_MainCameraPosition"); + + private void OnEnable() { + if (!Camera.main || Camera.main.gameObject != this.gameObject) { + Debug.LogWarning("MainCameraPositionShaderSetter set on object that is not the main camera"); + } + } + + private void Update() { + Shader.SetGlobalVector(pId_MainCameraPosition, this.transform.position); + } +} diff --git a/Assets/Scripts/MainCameraPositionShaderSetter.cs.meta b/Assets/Scripts/MainCameraPositionShaderSetter.cs.meta new file mode 100644 index 0000000..6a685de --- /dev/null +++ b/Assets/Scripts/MainCameraPositionShaderSetter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d9be733811a3b0342953552aa8d5ab8a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Shaders/GrassBlade.shader b/Assets/Shaders/GrassBlade.shader index 094ea57..20adef0 100644 --- a/Assets/Shaders/GrassBlade.shader +++ b/Assets/Shaders/GrassBlade.shader @@ -5,7 +5,7 @@ _BladeWidth("Blade Width", Range(0.003, .1)) = 0.01 _BendStrength("Bend Strength", Range(0, 1)) = 1 - _FrabVarianz("Farb Varianz", 2D) = "white" {} + _FieldColor("Field Color", 2D) = "white" {} _BladeWidthTex("Blade Width Texture", 2D) = "white" {} _AOStrength("Ambient Occlusion Strength", Range(0,2)) = 1 @@ -13,14 +13,9 @@ _SSSStrength("Subsurface Scattering Strength", Range(0,1)) = 1 _Metallic("Metallic", Range(0,1)) = 0 _Roughness("Roughness", Range(0,1)) = 0.3 - _BladeBow("Blade Bow", Range(0,1)) = 0.3 + _NormalCurvature("Normal Curvature", Range(0,1)) = 0.3 _BladeWidthTexStrength("BladeWidthTexStrength", Range(0,1)) = 0.3 _InnerColor("Inner Color", Color) = (1,1,1,1) - - _Color0("Color LOD 0", Color) = (0.3, 1.0, 0.3, 1.0) - _Color1("Color LOD 1", Color) = (1.0, 0.7, 0.3, 1.0) - _Color2("Color LOD 2", Color) = (1.0, 0.3, 0.1, 1.0) - _Color3("Color LOD 3", Color) = (0.8, 0.0, 0.0, 1.0) } SubShader { LOD 100 diff --git a/Assets/Shaders/SkyColorTestShader.shader b/Assets/Shaders/SkyColorTestShader.shader deleted file mode 100644 index bb3bcf6..0000000 --- a/Assets/Shaders/SkyColorTestShader.shader +++ /dev/null @@ -1,46 +0,0 @@ -Shader "Unlit/SkyColorTestShader" -{ - SubShader - { - Pass - { - HLSLPROGRAM - #pragma vertex vert - #pragma fragment frag - #include "UnityCG.cginc" - - struct v2f { - half3 worldRefl : TEXCOORD0; - float4 pos : SV_POSITION; - }; - - v2f vert (float4 vertex : POSITION, float3 normal : NORMAL) - { - v2f o; - o.pos = UnityObjectToClipPos(vertex); - // compute world space position of the vertex - float3 worldPos = mul(unity_ObjectToWorld, vertex).xyz; - // compute world space view direction - float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos)); - // world space normal - float3 worldNormal = UnityObjectToWorldNormal(normal); - // world space reflection vector - o.worldRefl = reflect(-worldViewDir, worldNormal); - return o; - } - - fixed4 frag (v2f i) : SV_Target - { - // sample the default reflection cubemap, using the reflection vector - half4 skyData = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, i.worldRefl); - // decode cubemap data into actual color - half3 skyColor = DecodeHDR (skyData, unity_SpecCube0_HDR); - // output it! - fixed4 c = 0; - c.rgb = skyColor; - return c; - } - ENDHLSL - } - } -} diff --git a/Assets/Shaders/SkyColorTestShader.shader.meta b/Assets/Shaders/SkyColorTestShader.shader.meta deleted file mode 100644 index 6f87980..0000000 --- a/Assets/Shaders/SkyColorTestShader.shader.meta +++ /dev/null @@ -1,9 +0,0 @@ -fileFormatVersion: 2 -guid: 633e3c163a26aee4395de6b81b1aae3b -ShaderImporter: - externalObjects: {} - defaultTextures: [] - nonModifiableTextures: [] - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/SkyTest.mat b/Assets/Shaders/SkyTest.mat deleted file mode 100644 index a11d4ba..0000000 --- a/Assets/Shaders/SkyTest.mat +++ /dev/null @@ -1,83 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!21 &2100000 -Material: - serializedVersion: 8 - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_Name: SkyTest - m_Shader: {fileID: 4800000, guid: 633e3c163a26aee4395de6b81b1aae3b, type: 3} - m_Parent: {fileID: 0} - m_ModifiedSerializedProperties: 0 - m_ValidKeywords: [] - m_InvalidKeywords: [] - m_LightmapFlags: 4 - m_EnableInstancingVariants: 0 - m_DoubleSidedGI: 0 - m_CustomRenderQueue: -1 - stringTagMap: {} - disabledShaderPasses: [] - m_LockedProperties: - m_SavedProperties: - serializedVersion: 3 - m_TexEnvs: - - _BumpMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _DetailAlbedoMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _DetailMask: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _DetailNormalMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _EmissionMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _MainTex: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _MetallicGlossMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _OcclusionMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - - _ParallaxMap: - m_Texture: {fileID: 0} - m_Scale: {x: 1, y: 1} - m_Offset: {x: 0, y: 0} - m_Ints: [] - m_Floats: - - _BumpScale: 1 - - _Cutoff: 0.5 - - _DetailNormalMapScale: 1 - - _DstBlend: 0 - - _GlossMapScale: 1 - - _Glossiness: 0.5 - - _GlossyReflections: 1 - - _Metallic: 0 - - _Mode: 0 - - _OcclusionStrength: 1 - - _Parallax: 0.02 - - _SmoothnessTextureChannel: 0 - - _SpecularHighlights: 1 - - _SrcBlend: 1 - - _UVSec: 0 - - _ZWrite: 1 - m_Colors: - - _Color: {r: 1, g: 1, b: 1, a: 1} - - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} - m_BuildTextureStacks: [] diff --git a/Assets/Shaders/SkyTest.mat.meta b/Assets/Shaders/SkyTest.mat.meta deleted file mode 100644 index a379499..0000000 --- a/Assets/Shaders/SkyTest.mat.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 944161d6703f1e244a9216da9f82501b -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 2100000 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Shaders/include/GrassBladePass.hlsl b/Assets/Shaders/include/GrassBladePass.hlsl index 1865a28..3150167 100644 --- a/Assets/Shaders/include/GrassBladePass.hlsl +++ b/Assets/Shaders/include/GrassBladePass.hlsl @@ -1,14 +1,14 @@ // Material Properties float _BladeWidth, _BendStrength; -float4 _Color0, _Color1, _Color2, _Color3, _InnerColor; -float _SegmentsMinusOne, _FieldSize, _AOStrength, _Translucency, _SSSStrength, _Metallic, _Roughness, _BladeBow, _MaxBladeHeight, _HighlightStrength, _BladeWidthTexStrength; -sampler2D _FrabVarianz, _BladeWidthTex; +float4 _InnerColor; +float _AOStrength, _Translucency, _SSSStrength, _Metallic, _Roughness, _NormalCurvature, _BladeWidthTexStrength; +sampler2D _FieldColor, _BladeWidthTex; float2 _TransitionRange; -#if UNITY_EDITOR -float3 _WorldSpaceCameraPosEditor; -#endif +// Custom variable that is set to the position of the main camera in the scene +// (instead of Unity's _WorldSpaceCameraPos, which uses the scene view camera in editor mode) +float3 _MainCameraPosition; struct MeshData { float4 vertex : POSITION; @@ -23,12 +23,12 @@ struct v2g { struct g2f { float4 pos : SV_POSITION; #ifdef IS_IN_BASE_PASS - float3 normal : NORMAL; - float3 worldPos : TEXCOORD3; + float3 localNormal : NORMAL; float2 uv : TEXCOORD0; // bottom left 0.0 top right 1.1 (values converge - 0 and 1 meet at the tip) SHADOW_COORDS(1) // put shadows data into TEXCOORD1 - float transitionInterpolator : TEXCOORD2; + float3 worldPos : TEXCOORD2; #endif + float3 debug : TEXCOORD3; }; v2g vert (MeshData v) { @@ -39,14 +39,6 @@ v2g vert (MeshData v) { return o; } -float getCameraDistance(float3 pos) { -#if UNITY_EDITOR - return distance(_WorldSpaceCameraPosEditor, pos); -#else - return distance(_WorldSpaceCameraPos, pos); -#endif -} - #if _LOD_LOD_0 [MaxVertexCount(4 + ( 11 - 1 ) * 2)] #elif _LOD_LOD_1 @@ -59,7 +51,6 @@ float getCameraDistance(float3 pos) { void geom(point v2g IN[1], inout TriangleStream triStream) { const float3 basePos = IN[0].vertex.xyz; const float3 tipOffset = IN[0].tipOffset; - const float3 tipPosObjectSpace = basePos + tipOffset; // const float4 basePosClipSpace = UnityObjectToClipPos(float4(basePos, 1)); // const float4 tipPosClipSpace = UnityObjectToClipPos(float4(tipPosObjectSpace, 1)); @@ -67,9 +58,9 @@ void geom(point v2g IN[1], inout TriangleStream triStream) { const float3 basePosWS = mul(unity_ObjectToWorld, float4(basePos.xyz, 1.0)).xyz; - const float cameraDistance = getCameraDistance(basePosWS); + const float mainCamDist = distance(_MainCameraPosition, basePosWS); - float interpolator = invLerp(_TransitionRange.x, _TransitionRange.y, cameraDistance); + float interpolator = invLerp(_TransitionRange.x, _TransitionRange.y, mainCamDist); float halfWidth = _BladeWidth/2; @@ -81,225 +72,219 @@ void geom(point v2g IN[1], inout TriangleStream triStream) { } // randomly shrink shadow blades to create a smooth transition between shadowed and non-shadowed area - #ifdef IS_IN_SHADOW_PASS - #if _LOD_LOD_1 +#ifdef IS_IN_SHADOW_PASS +#if _LOD_LOD_1 float widthMultiplier = remap(N21(basePos.xz) * 0.5, 1, 1, 0, interpolator); widthMultiplier = max(0, widthMultiplier); halfWidth *= widthMultiplier; - #endif - #endif +#endif +#endif g2f o; - float3 previousSegmentCenter = basePos - float3(0,-1,0); // point beneath the basePos + o.debug = float3(0,0,0); + int numSegments; #if _LOD_LOD_0 - int numSegments = lerp(11.99, 5.0, interpolator); // 11 - 5 + numSegments = lerp(11.99, 5.0, interpolator); // 11 - 5 #elif _LOD_LOD_1 - int numSegments = lerp(5.99, 2.0, interpolator); // 5 - 2 + numSegments = lerp(5.99, 2.0, interpolator); // 5 - 2 #elif _LOD_LOD_2 - int numSegments = 2; + numSegments = 2; #elif _LOD_LOD_3 - int numSegments = 1; + numSegments = 1; #endif - float3 localCameraPos = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos.xyz, 1.0)).xyz; + const float lowerThickness = 0.8; - for (int i = 0; i < numSegments; i++) { - const float lowerThickness = 0.8; - // LOD 3 has a different algorithm #if _LOD_LOD_3 - { - - - const float segmentWidth = halfWidth * pow(sin(lowerThickness * 3.141), .8); - const float3 widthOffset = getWidthOffset(segmentWidth, tipOffset.xz) * max(1, cameraDistance / 10); - float3 segmentCenterBase = basePos; - float3 segmentCenterTip = basePos + tipOffset; - const float3 vertLeftBase = segmentCenterBase + widthOffset * 2.5; - const float3 vertRightBase = segmentCenterBase - widthOffset * 2.5; - const float3 vertLeftTip = segmentCenterTip + widthOffset * 0.4; - const float3 vertRightTip = segmentCenterTip - widthOffset * 0.4; - - const float3 surfTangent1 = vertLeftBase - vertLeftTip; - const float3 surfTangent2 = vertLeftBase - vertRightBase; - const float3 normal = normalize(cross(surfTangent1, surfTangent2)); // TODO remove normalize - float3 normalWS = UnityObjectToWorldNormal(normal); - - - const float3 camVec = normalize(vertLeftBase - localCameraPos); - - // at which side of the blade are we looking? - const bool lookingFromAbove = dot(camVec, normalWS) > 0; - if (lookingFromAbove) { - normalWS = -normalWS; - } - - //normalWS = float3(1,1,1) * lookingFromAbove; - //normalWS = basePosWS - _WorldSpaceCameraPos.xyz; - //normalWS = camVec; - - o.pos = UnityObjectToClipPos(vertLeftBase); - #ifndef IS_IN_SHADOW_PASS - - o.uv = float2(0, 0); - TRANSFER_SHADOW(o) - o.transitionInterpolator = interpolator; - o.normal = normalWS; - o.worldPos = mul(unity_ObjectToWorld, vertLeftBase).xyz; - #endif - triStream.Append(o); - - o.pos = UnityObjectToClipPos(vertRightBase); - #ifndef IS_IN_SHADOW_PASS - o.uv = float2(1, 0); - TRANSFER_SHADOW(o) - o.transitionInterpolator = interpolator; - o.normal = normalWS; - o.worldPos = mul(unity_ObjectToWorld, vertRightBase).xyz; - #endif - triStream.Append(o); - - o.pos = UnityObjectToClipPos(vertLeftTip); - #ifndef IS_IN_SHADOW_PASS - o.uv = float2(0, 1); - TRANSFER_SHADOW(o) - o.transitionInterpolator = interpolator; - o.normal = normalWS; - o.worldPos = mul(unity_ObjectToWorld, vertLeftTip).xyz; - #endif - triStream.Append(o); - - o.pos = UnityObjectToClipPos(vertRightTip); - #ifndef IS_IN_SHADOW_PASS - o.uv = float2(1, 1); - TRANSFER_SHADOW(o) - o.transitionInterpolator = interpolator; - o.normal = normalWS; - o.worldPos = mul(unity_ObjectToWorld, vertRightTip).xyz; - #endif - triStream.Append(o); - } - continue; -#endif - - // in "blade space" - const float segmentWidth = halfWidth * pow(sin(lowerThickness * 3.141 * (numSegments - i) / numSegments), .8); - const float segmentHeightNormalized = i / (float)numSegments; - // float nextSegmentHeightNormalized = (i+1) / (float)numSegments; - - // generate new verts - const float3 widthOffset = getWidthOffset(segmentWidth, tipOffset.xz) * max(1, cameraDistance / 10); - float3 segmentCenter = basePos + bendParabula(tipOffset, segmentHeightNormalized, _BendStrength); - const float3 segmentCenterSnapshot = segmentCenter; - - const float3 vertLeft = segmentCenter + widthOffset; - const float3 vertRight = segmentCenter - widthOffset; - //const float3 nextSegmentCenter = basePos + bendParabula(tipPos, nextSegmentHeight01); - -#ifndef IS_IN_SHADOW_PASS - // calculate normal - const float3 surfTangent1 = previousSegmentCenter - segmentCenter; - const float3 surfTangent2 = previousSegmentCenter - vertLeft; - const float3 normal = normalize(cross(surfTangent1, surfTangent2)); // TODO remove normalize - float3 normalWS = UnityObjectToWorldNormal(normal); - - - const float3 camVec = normalize(segmentCenter - localCameraPos); - - // at which side of the blade are we looking? - const bool lookingFromAbove = dot(camVec, normalWS) > 0; - if (lookingFromAbove) { - normalWS = -normalWS; - } -#endif - o.pos = UnityObjectToClipPos(vertLeft); -#ifndef IS_IN_SHADOW_PASS - o.uv = float2(0, segmentHeightNormalized); - TRANSFER_SHADOW(o) - o.transitionInterpolator = interpolator; - o.normal = normalize(lerp(normalWS, UnityObjectToWorldNormal(normalize(widthOffset)), _BladeBow)); - o.worldPos = mul(unity_ObjectToWorld, vertLeft).xyz; -#endif - triStream.Append(o); - - o.pos = UnityObjectToClipPos(vertRight); -#ifndef IS_IN_SHADOW_PASS - o.uv = float2(1, segmentHeightNormalized); - TRANSFER_SHADOW(o) - - o.transitionInterpolator = interpolator; - o.normal = normalize(lerp(normalWS, UnityObjectToWorldNormal(-normalize(widthOffset)), _BladeBow)); - o.worldPos = mul(unity_ObjectToWorld, vertRight).xyz; -#endif - triStream.Append(o); - previousSegmentCenter = segmentCenterSnapshot; - } + const float segmentWidth = halfWidth * pow(sin(lowerThickness * 3.141), .8); + const float3 widthOffset = getWidthOffset(tipOffset.xz) * segmentWidth * max(1, mainCamDist / 10); + float3 segmentCenterBase = basePos; + float3 segmentCenterTip = basePos + tipOffset; + const float3 vertLeftBase = segmentCenterBase + widthOffset * 2.5; + const float3 vertRightBase = segmentCenterBase - widthOffset * 2.5; + const float3 vertLeftTip = segmentCenterTip + widthOffset * 0.4; + const float3 vertRightTip = segmentCenterTip - widthOffset * 0.4; - o.pos = UnityObjectToClipPos(basePos + tipOffset * float3(_BendStrength, 1, _BendStrength)); -#ifndef IS_IN_SHADOW_PASS - o.uv = float2(.5, 1); + const float3 surfTangent1 = vertRightBase - segmentCenterTip; + const float3 surfTangent2 = vertLeftBase - segmentCenterTip; + const float3 normal = cross(surfTangent1, surfTangent2); + + o.pos = UnityObjectToClipPos(vertRightBase); +#ifdef IS_IN_BASE_PASS + o.uv = float2(1, 0); TRANSFER_SHADOW(o) - o.transitionInterpolator = interpolator; - o.normal = o.normal > 0 ? float3(0, 1, 0) : float3(0, -1, 0); + o.localNormal = normal; + o.worldPos = mul(unity_ObjectToWorld, float4(vertRightBase,1.0)).xyz; #endif triStream.Append(o); + + o.pos = UnityObjectToClipPos(vertLeftBase); +#ifdef IS_IN_BASE_PASS + o.uv = float2(0, 0); + TRANSFER_SHADOW(o) + o.localNormal = normal; + o.worldPos = mul(unity_ObjectToWorld, float4(vertLeftBase,1.0)).xyz; +#endif + triStream.Append(o); + + o.pos = UnityObjectToClipPos(vertRightTip); +#ifdef IS_IN_BASE_PASS + o.uv = float2(1, 1); + TRANSFER_SHADOW(o) + o.localNormal = normal; + o.worldPos = mul(unity_ObjectToWorld, float4(vertRightTip,1.0)).xyz; +#endif + triStream.Append(o); + + o.pos = UnityObjectToClipPos(vertLeftTip); +#ifdef IS_IN_BASE_PASS + o.uv = float2(0, 1); + TRANSFER_SHADOW(o) + o.localNormal = normal; + o.worldPos = mul(unity_ObjectToWorld, float4(vertLeftTip,1.0)).xyz; +#endif + triStream.Append(o); + +#else + const float3 widthOffset = getWidthOffset(tipOffset.xz) * max(1, mainCamDist / 10); + + // previous vertex positions, used for normal calculation + float3 previousVertLeft; + float3 previousVertRight; + for (int i = 0; i < numSegments; i++) { + const float segmentWidth = halfWidth * pow(sin(lowerThickness * 3.141 * (numSegments - i) / numSegments), .8); + const float3 segmentWidthOffset = widthOffset * segmentWidth; + const float segmentHeightNormalized = i / (float)numSegments; + + const float3 segmentCenter = basePos + bendParabula(tipOffset, segmentHeightNormalized, _BendStrength); + const float3 vertLeft = segmentCenter + segmentWidthOffset; + const float3 vertRight = segmentCenter - segmentWidthOffset; + +#ifdef IS_IN_BASE_PASS + // calculate normal + float3 surfTangent1, surfTangent2; + if (i == 0) { + // for the normals at the bottom vertices, use the normals of the next segment + const float nextSegmentHeightNormalized = (i+1) / (float)numSegments; + const float3 nextSegmentCenter = basePos + bendParabula(tipOffset, nextSegmentHeightNormalized, _BendStrength); + surfTangent1 = nextSegmentCenter - vertRight; + surfTangent2 = nextSegmentCenter - vertLeft; + } + else { + surfTangent1 = segmentCenter - previousVertRight; + surfTangent2 = segmentCenter - previousVertLeft; + } + const float3 normal = cross(surfTangent1, surfTangent2); + +#if _LOD_LOD_0 + const float curvature = _NormalCurvature; +#elif _LOD_LOD_1 + const float curvature = lerp(_NormalCurvature, 0.0, interpolator); +#else + const float curvature = 0.0; +#endif + float3 leftCurvedNormal = normalize(normalize(normal) + widthOffset * curvature); + float3 rightCurvedNormal = normalize(normalize(normal) - widthOffset * curvature); + + previousVertLeft = vertLeft; + previousVertRight = vertRight; +#endif + + o.pos = UnityObjectToClipPos(vertRight); +#ifdef IS_IN_BASE_PASS + o.uv = float2(1, segmentHeightNormalized); + TRANSFER_SHADOW(o) + o.localNormal = rightCurvedNormal; + o.worldPos = mul(unity_ObjectToWorld, float4(vertRight,1.0)).xyz; +#endif + triStream.Append(o); + + o.pos = UnityObjectToClipPos(vertLeft); +#ifdef IS_IN_BASE_PASS + o.uv = float2(0, segmentHeightNormalized); + TRANSFER_SHADOW(o) + o.localNormal = leftCurvedNormal; + o.worldPos = mul(unity_ObjectToWorld, float4(vertLeft,1.0)).xyz; +#endif + triStream.Append(o); + } + + // tip vertex + const float3 tipPosObjectSpace = basePos + tipOffset; + const float3 surfTangent1 = tipPosObjectSpace - previousVertRight; + const float3 surfTangent2 = tipPosObjectSpace - previousVertLeft; + const float3 normal = cross(surfTangent1, surfTangent2); + o.pos = UnityObjectToClipPos(tipPosObjectSpace * float3(_BendStrength, 1, _BendStrength)); +#ifdef IS_IN_BASE_PASS + o.uv = float2(.5, 1); + TRANSFER_SHADOW(o) + o.localNormal = normal; + o.worldPos = mul(unity_ObjectToWorld, float4(tipPosObjectSpace,1.0)).xyz; +#endif + triStream.Append(o); + +#endif } -float4 frag(g2f i) : SV_Target{ - - #ifndef IS_IN_SHADOW_PASS +float4 frag(g2f i, bool isFrontFace : SV_IsFrontFace) : SV_Target { +#ifdef IS_IN_BASE_PASS + float3 localPos = mul(unity_WorldToObject, float4(i.worldPos, 1)).xyz; + float d = distance(localPos, i.debug) * 50; + float k = frac(d); + // return float4(k,k,k, 1); + // return float4(i.debug,1); + + float3 worldNormal = UnityObjectToWorldNormal(i.localNormal); + + // at which side of the blade are we looking? + // VERY INACCURATE, because the normals don't match the geometry (smoothing, fake curvature) + if (!isFrontFace) { + worldNormal = -worldNormal; + } - float3 worldNormal = normalize(i.normal); float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz); - - half3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); - half3 worldRefl = reflect(-worldViewDir, worldNormal); + float3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); + float3 worldRefl = reflect(-worldViewDir, worldNormal); - // same as in previous shader - half4 skyData = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0, worldRefl, 0); - half3 skyColor = DecodeHDR(skyData, unity_SpecCube0_HDR); + float4 skyData = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0, worldRefl, 0); + float3 skyColor = DecodeHDR(skyData, unity_SpecCube0_HDR); float3 lightReflectDirection = reflect(-lightDirection, worldNormal); - float3 viewDirection = normalize(float3(float4(_WorldSpaceCameraPos.xyz, 1.0) - i.worldPos.xyz)); - - float atten = 1.0; + float3 viewDirection = normalize(float3(float4(_MainCameraPosition.xyz, 1.0) - i.worldPos.xyz)); - float3 translucency = atten * _LightColor0.xyz * max(0.0, dot(worldNormal, -lightDirection)) * _Translucency; + float3 translucency = _LightColor0.xyz * max(0.0, dot(worldNormal, -lightDirection)) * _Translucency; float3 subsurf = max(0.0, dot(lightReflectDirection, -viewDirection)) * _SSSStrength * _InnerColor; - const fixed shadow = SHADOW_ATTENUATION(i); + const float shadow = max(.35, SHADOW_ATTENUATION(i)); float3 lightFinal = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0, worldNormal, 2.0); lightFinal += translucency; lightFinal += subsurf; - lightFinal *= max(.35, shadow); - lightFinal *= 1.0; + lightFinal *= shadow; + + // ATTENTION DO NOT TRY TO UNDERSTAND THE CODE BELOW (or you will go crazy) - fixed4 middleCol = min(1, abs(i.uv.x - 0.5) * 1); - fixed4 col = max(0.87, pow(middleCol, .01)); - fixed4 farbvarianz = tex2Dlod(_FrabVarianz, float4(i.worldPos.xz / 40, 0, 3)); - fixed4 farbvarianz2 = tex2Dlod(_FrabVarianz, float4(i.worldPos.xz / 10, 0, 3)); - fixed4 farbvarianz3 = tex2Dlod(_FrabVarianz, float4(i.worldPos.xz / 3, 0, 3)); - fixed4 bladeWidthTex = tex2Dlod(_BladeWidthTex, float4(i.uv.x, 0, 0, 3)); + float4 middleCol = min(1, abs(i.uv.x - 0.5) * 1); + float4 col = max(0.87, pow(middleCol, .01)); + float3 c0 = tex2Dlod(_FieldColor, float4(i.worldPos.xz / 40, 0, 3)).xyz; + float3 c1 = tex2Dlod(_FieldColor, float4(i.worldPos.xz / 10, 0, 3)).xyz; + float3 c2 = tex2Dlod(_FieldColor, float4(i.worldPos.xz / 3, 0, 3)).xyz; + float4 bladeWidthTex = tex2Dlod(_BladeWidthTex, float4(i.uv.x, 0, 0, 3)); - fixed4 albedo = lerp(farbvarianz2, farbvarianz3, 0.4); - albedo = lerp(albedo, farbvarianz, _Metallic) * 0.7; + float3 albedo = lerp(c1, c2, 0.4); + albedo = lerp(albedo, c0, _Metallic) * 0.7; albedo *= 1 + _AOStrength; albedo *= lerp(1, bladeWidthTex.x * 2.5, _BladeWidthTexStrength); - fixed3 color = fixed3(skyColor * max(.35, shadow) * .2 + lightFinal * albedo * col); - fixed3 highlightedColor = lerp(color, fixed3(2.5, 0.7, 0.5), 0.5); - fixed3 finalTestColor = albedo.xyz ; + float3 color = float3(skyColor * shadow * .2 + lightFinal * albedo * col); + float3 highlightedColor = lerp(color, float3(2.5, 0.7, 0.5), 0.5); + float3 finalTestColor = albedo.xyz ; lightFinal = min(1.4, max(0.4, lightFinal)); - return float4(lightFinal * albedo * 2, 0); - //return float4(worldNormal, 0); dot(worldNormal, lightDirection) - - #endif - - - - return 0; + return float4(color, 1); +#endif + return 0; // shadow pass } diff --git a/Assets/Shaders/include/GrassHelpers.hlsl b/Assets/Shaders/include/GrassHelpers.hlsl index 269252a..224c9ba 100644 --- a/Assets/Shaders/include/GrassHelpers.hlsl +++ b/Assets/Shaders/include/GrassHelpers.hlsl @@ -1,7 +1,7 @@ // tipPos2D = xz coordinates of tip -float3 getWidthOffset(float width, float2 tipPos2D) { +float3 getWidthOffset(float2 tipPos2D) { float2 dirBaseToTip = normalize(tipPos2D); - return float3(-dirBaseToTip.y * width, 0, dirBaseToTip.x * width); + return float3(-dirBaseToTip.y, 0, dirBaseToTip.x); } float3 bendParabula(float3 tipPos, float posOnBlade, float bendStrength) {