Add minimal shader, generate more grass
This commit is contained in:
parent
2a75b8aa27
commit
268a22ad63
67
Assets/Materials/Grass.mat
Normal file
67
Assets/Materials/Grass.mat
Normal file
@ -0,0 +1,67 @@
|
||||
%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: Grass
|
||||
m_Shader: {fileID: 4800000, guid: eebe2969663fc40449d6052c661a6c2e, 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:
|
||||
- _BladeWidthTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _FrabVarianz:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _HeightMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MaskMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _WindTextur:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _AOStrength: 1
|
||||
- _BendStrength: 1
|
||||
- _BladeBow: 0.3
|
||||
- _BladeWidth: 0.05
|
||||
- _ExtraSawnOnCutGras: 6
|
||||
- _FieldSize: 5
|
||||
- _Metallic: 0
|
||||
- _Roughness: 0.3
|
||||
- _SSSStrength: 1
|
||||
- _SegmentsMinusOne: 10
|
||||
- _Translucency: 1
|
||||
m_Colors:
|
||||
- _BoxSize: {r: 0, g: 0, b: 0, a: 0}
|
||||
- _BoxWorldCenter: {r: 0, g: 0, b: 0, a: 0}
|
||||
- _Braun: {r: 0.33999997, g: 0.85, b: 0.92, a: 1}
|
||||
- _Farbe: {r: 0.33999997, g: 0.85, b: 0.92, a: 1}
|
||||
- _InnerColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _SpecularColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _WindOffset: {r: 0, g: 0, b: 0, a: 0}
|
||||
m_BuildTextureStacks: []
|
||||
8
Assets/Materials/Grass.mat.meta
Normal file
8
Assets/Materials/Grass.mat.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1cd7df4d77cf1ae40ad59a15c2769be9
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because one or more lines are too long
@ -10,7 +10,9 @@ public class GrassFieldEditor : Editor {
|
||||
if (GUILayout.Button("Generate")) {
|
||||
((GrassField) target).GenerateGrassField();
|
||||
// Scene may have been modified
|
||||
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
|
||||
if (!Application.isPlaying) {
|
||||
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
public class GrassField : MonoBehaviour {
|
||||
public int size = 100;
|
||||
public int numChunks = 5;
|
||||
public Material grassMaterial;
|
||||
public GrassChunk chunkPrefab;
|
||||
|
||||
private struct BladeData {
|
||||
public Vector3 rootPosition;
|
||||
public Vector3 tipPosition;
|
||||
public Vector3 tipOffset;
|
||||
}
|
||||
|
||||
// ReSharper disable Unity.PerformanceAnalysis
|
||||
public void GenerateGrassField() {
|
||||
Debug.Log("Generating grass field... ");
|
||||
|
||||
@ -22,13 +23,13 @@ public class GrassField : MonoBehaviour {
|
||||
}
|
||||
|
||||
private BladeData[] GenerateBlades() {
|
||||
var blades = new BladeData[size * size];
|
||||
var blades = new BladeData[size * size * 100];
|
||||
|
||||
for (int x = 0; x < size; x++)
|
||||
for (int y = 0; y < size; y++) {
|
||||
var i = x + y * size;
|
||||
blades[i].rootPosition = new Vector3(x + 0.5f, 0.0f, y + 0.5f);
|
||||
blades[i].tipPosition = blades[i].rootPosition + new Vector3(0.0f, 1.0f, 0.0f);
|
||||
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;
|
||||
@ -57,27 +58,26 @@ public class GrassField : MonoBehaviour {
|
||||
);
|
||||
|
||||
List<Vector3> rootPositions = new();
|
||||
List<Vector3> tipPositions = new();
|
||||
List<Vector3> tipOffsets = new();
|
||||
List<int> indices = new();
|
||||
foreach (var blade in blades) {
|
||||
var localRootPos = blade.rootPosition - chunkPos;
|
||||
if (bounds.Contains(localRootPos)) {
|
||||
rootPositions.Add(localRootPos);
|
||||
var localTipPos = blade.tipPosition - chunkPos;
|
||||
tipPositions.Add(localTipPos);
|
||||
tipOffsets.Add(blade.tipOffset);
|
||||
indices.Add(rootPositions.Count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
var mesh = new Mesh();
|
||||
// 16 bit (default) supports 65536 vertices, 32 bit supports 4 billion
|
||||
// mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
|
||||
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
|
||||
mesh.name = "grass_chunk[" + x + "," + y + "]";
|
||||
mesh.SetVertices(rootPositions);
|
||||
// do not calculate bounding box - will be set manually
|
||||
mesh.SetIndices(indices, MeshTopology.Points, 0, false);
|
||||
// Vector3 UVs are possible
|
||||
mesh.SetUVs(0, tipPositions);
|
||||
mesh.SetUVs(0, tipOffsets);
|
||||
|
||||
var chunk = Instantiate(chunkPrefab, chunks.transform);
|
||||
chunk.name = "Chunk [" + x + ", " + y +"]";
|
||||
@ -88,6 +88,7 @@ public class GrassField : MonoBehaviour {
|
||||
|
||||
var meshRenderer = chunk.GetComponent<MeshRenderer>();
|
||||
meshRenderer.localBounds = bounds;
|
||||
meshRenderer.sharedMaterial = grassMaterial;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
8
Assets/Shaders.meta
Normal file
8
Assets/Shaders.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90499031c9fd4764095827ebbf1cf418
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
59
Assets/Shaders/GrassBlade.shader
Normal file
59
Assets/Shaders/GrassBlade.shader
Normal file
@ -0,0 +1,59 @@
|
||||
Shader "Grass/GrassBlade" {
|
||||
Properties {
|
||||
_BladeWidth("Breite", Range(0.003, .1)) = 0.01
|
||||
_SegmentsMinusOne("Segments Minus One", Range(1, 60)) = 20
|
||||
_BendStrength("Bend Strength", Range(0, 1)) = 1
|
||||
}
|
||||
SubShader {
|
||||
LOD 100
|
||||
Cull Off
|
||||
|
||||
//Render Pass
|
||||
Pass {
|
||||
Tags { "LightMode" = "ForwardBase"}
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#pragma geometry geom
|
||||
|
||||
#pragma multi_compile _ SHADOWS_SCREEN
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
#include "UnityPBSLighting.cginc"
|
||||
#include "AutoLight.cginc"
|
||||
|
||||
#include "./include/Random.hlsl"
|
||||
#include "./include/Math.hlsl"
|
||||
#include "./include/Culling.hlsl"
|
||||
#include "./include/GrassHelpers.hlsl"
|
||||
|
||||
#define IS_IN_BASE_PASS
|
||||
|
||||
#include "./include/GrassBladePass.hlsl"
|
||||
|
||||
ENDCG
|
||||
}
|
||||
|
||||
// Shadow Pass
|
||||
Pass {
|
||||
Tags { "LightMode" = "ShadowCaster" }
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#pragma geometry geom
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
|
||||
#include "./include/Random.hlsl"
|
||||
#include "./include/Math.hlsl"
|
||||
#include "./include/Culling.hlsl"
|
||||
#include "./include/GrassHelpers.hlsl"
|
||||
|
||||
#define IS_IN_SHADOW_PASS
|
||||
|
||||
#include "./include/GrassBladePass.hlsl"
|
||||
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
||||
9
Assets/Shaders/GrassBlade.shader.meta
Normal file
9
Assets/Shaders/GrassBlade.shader.meta
Normal file
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eebe2969663fc40449d6052c661a6c2e
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Shaders/include.meta
Normal file
8
Assets/Shaders/include.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c6dc0a6fc332df448a4476c05ef82dd
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
14
Assets/Shaders/include/Culling.hlsl
Normal file
14
Assets/Shaders/include/Culling.hlsl
Normal file
@ -0,0 +1,14 @@
|
||||
bool isInFrustum(float4 clipSpacePosition, float screenExpansion) {
|
||||
float nearClippingPlane = _ProjectionParams.y; // y near plane, z far plane
|
||||
|
||||
float3 weirdspace = clipSpacePosition.xyz;
|
||||
weirdspace /= -clipSpacePosition.w;
|
||||
weirdspace.x = weirdspace.x / 2.0f + 0.5f;
|
||||
weirdspace.y = weirdspace.y / 2.0f + 0.5f;
|
||||
weirdspace.z = -clipSpacePosition.w;
|
||||
const bool notClippedByNearClippingPlane = weirdspace.z >= -nearClippingPlane ? 0 : 1;
|
||||
const bool inFrustum =
|
||||
(weirdspace.x > -screenExpansion) && (weirdspace.x < (1+screenExpansion))
|
||||
&& notClippedByNearClippingPlane;
|
||||
return inFrustum;
|
||||
}
|
||||
7
Assets/Shaders/include/Culling.hlsl.meta
Normal file
7
Assets/Shaders/include/Culling.hlsl.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84659d9a0ae985d4e8cd00395dc2ea8b
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
124
Assets/Shaders/include/GrassBladePass.hlsl
Normal file
124
Assets/Shaders/include/GrassBladePass.hlsl
Normal file
@ -0,0 +1,124 @@
|
||||
float _BladeWidth, _SegmentsMinusOne, _BendStrength;
|
||||
|
||||
struct MeshData {
|
||||
float4 vertex : POSITION;
|
||||
float3 tipOffset : TEXCOORD0;
|
||||
};
|
||||
struct v2g {
|
||||
float4 vertex : SV_POSITION;
|
||||
float3 tipOffset : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct g2f {
|
||||
float4 pos : SV_POSITION;
|
||||
#if !defined(IS_IN_SHADOW_PASS)
|
||||
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
|
||||
#endif
|
||||
};
|
||||
|
||||
v2g vert (MeshData v) {
|
||||
v2g o;
|
||||
o.vertex = v.vertex;
|
||||
o.tipOffset = v.tipOffset;
|
||||
return o;
|
||||
}
|
||||
|
||||
#if defined(IS_IN_SHADOW_PASS)
|
||||
[MaxVertexCount(24)]
|
||||
#else
|
||||
[MaxVertexCount(24)]
|
||||
#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;
|
||||
|
||||
// const float4 basePosClipSpace = UnityObjectToClipPos(float4(basePos, 1));
|
||||
// const float4 tipPosClipSpace = UnityObjectToClipPos(float4(tipPosObjectSpace, 1));
|
||||
// if (!isInFrustum(basePosClipSpace, 0.1) && !isInFrustum(tipPosClipSpace, 0.1)) return;
|
||||
|
||||
const float3 basePosWS = mul(unity_ObjectToWorld, float4(basePos.xyz, 1.0)).xyz;
|
||||
const float cameraDistance = distance(_WorldSpaceCameraPos, basePosWS);
|
||||
|
||||
float halfWidth = _BladeWidth/2;
|
||||
|
||||
g2f o;
|
||||
|
||||
float3 previousSegmentCenter = basePos - float3(0,-1,0); // point beneath the basePos
|
||||
|
||||
int segmentsMinusOne = max(1, (int)(_SegmentsMinusOne) - ((cameraDistance) / 8.0) + N21(basePos));
|
||||
|
||||
for (int i = 0; i < segmentsMinusOne; i++) {
|
||||
// in "blade space"
|
||||
const float lowerThickness = 0.8;
|
||||
const float segmentWidth = halfWidth * pow(sin(lowerThickness * 3.141 * ((int)_SegmentsMinusOne - i) / (int)_SegmentsMinusOne), .8);
|
||||
const float segmentHeightNormalized = i / _SegmentsMinusOne;
|
||||
// float nextSegmentHeightNormalized = (i+1) / _SegmentsMinusOne;
|
||||
|
||||
// generate new verts
|
||||
const float3 widthOffset = getWidthOffset(segmentWidth, tipPosBladeSpace.xz) * max(1, cameraDistance / 8);
|
||||
float3 segmentCenter = basePos + bendParabula(tipPosBladeSpace, segmentHeightNormalized, _BendStrength);
|
||||
const float3 segmentCenterSnapshot = segmentCenter;
|
||||
|
||||
const float3 vertLeft = segmentCenter + widthOffset;
|
||||
const float3 vertRight = segmentCenter - widthOffset;
|
||||
// const float3 nextSegmentCenter = basePos + bendParabula(tipPos, nextSegmentHeight01);
|
||||
|
||||
#if !defined(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 - _WorldSpaceCameraPos.xyz);
|
||||
|
||||
// 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);
|
||||
#if !defined(IS_IN_SHADOW_PASS)
|
||||
o.uv = float2(0, segmentHeightNormalized);
|
||||
TRANSFER_SHADOW(o)
|
||||
#endif
|
||||
triStream.Append(o);
|
||||
|
||||
o.pos = UnityObjectToClipPos(vertRight);
|
||||
#if !defined(IS_IN_SHADOW_PASS)
|
||||
o.uv = float2(1, segmentHeightNormalized);
|
||||
TRANSFER_SHADOW(o)
|
||||
#endif
|
||||
triStream.Append(o);
|
||||
|
||||
previousSegmentCenter = segmentCenterSnapshot;
|
||||
}
|
||||
|
||||
o.pos = UnityObjectToClipPos(basePos + tipPosBladeSpace * float3(_BendStrength,1,_BendStrength));
|
||||
#if !defined(IS_IN_SHADOW_PASS)
|
||||
o.uv = float2(.5, 1);
|
||||
TRANSFER_SHADOW(o)
|
||||
#endif
|
||||
triStream.Append(o);
|
||||
}
|
||||
|
||||
fixed4 frag(g2f i) : SV_Target{
|
||||
#if defined(IS_IN_SHADOW_PASS)
|
||||
return 0;
|
||||
#else
|
||||
float atten = 1.0;
|
||||
|
||||
const fixed shadow = SHADOW_ATTENUATION(i);
|
||||
|
||||
float3 lightFinal = float3(i.uv.yyy);
|
||||
lightFinal *= max(.35, shadow);
|
||||
|
||||
fixed4 albedo = fixed4(0.3,1.0,0.3,1.0);
|
||||
|
||||
fixed3 color = fixed3(lightFinal * albedo);
|
||||
return fixed4(color, 1);
|
||||
#endif
|
||||
}
|
||||
7
Assets/Shaders/include/GrassBladePass.hlsl.meta
Normal file
7
Assets/Shaders/include/GrassBladePass.hlsl.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2de784b5b53165946b9e346393407b85
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
17
Assets/Shaders/include/GrassHelpers.hlsl
Normal file
17
Assets/Shaders/include/GrassHelpers.hlsl
Normal file
@ -0,0 +1,17 @@
|
||||
// tipPos2D = xz coordinates of tip
|
||||
float3 getWidthOffset(float width, float2 tipPos2D) {
|
||||
float2 dirBaseToTip = normalize(tipPos2D);
|
||||
return float3(-dirBaseToTip.y * width, 0, dirBaseToTip.x * width);
|
||||
}
|
||||
|
||||
float3 bendParabula(float3 tipPos, float posOnBlade, float bendStrength) {
|
||||
float len = length(tipPos.xz); // Länge des flachen Grashalms
|
||||
float b = tipPos.y;
|
||||
|
||||
posOnBlade = posOnBlade * len;
|
||||
|
||||
float y = (-pow(posOnBlade - len, 2) * b) / pow(len, 2) + b;
|
||||
float2 xz = normalize(float2(tipPos.x, tipPos.z)) * posOnBlade * bendStrength;
|
||||
|
||||
return float3(xz.x, y, xz.y);
|
||||
}
|
||||
7
Assets/Shaders/include/GrassHelpers.hlsl.meta
Normal file
7
Assets/Shaders/include/GrassHelpers.hlsl.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 02464ba14b990ef42af7f595f0e65dd8
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3
Assets/Shaders/include/Math.hlsl
Normal file
3
Assets/Shaders/include/Math.hlsl
Normal file
@ -0,0 +1,3 @@
|
||||
float invLerp(float a, float b, float v) {
|
||||
return clamp( (v-a) / (b-a) , 0, 1);
|
||||
}
|
||||
7
Assets/Shaders/include/Math.hlsl.meta
Normal file
7
Assets/Shaders/include/Math.hlsl.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e6a02633975791d4491ac937efacf7a8
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
6
Assets/Shaders/include/Random.hlsl
Normal file
6
Assets/Shaders/include/Random.hlsl
Normal file
@ -0,0 +1,6 @@
|
||||
// hash function to get a random number of a 2d coordinate -> return value between 0 and 1
|
||||
float N21(float2 p) {
|
||||
p = frac(p * float2(123.34, 345.45));
|
||||
p += dot(p, p + 34.345);
|
||||
return frac(p.x * p.y);
|
||||
}
|
||||
7
Assets/Shaders/include/Random.hlsl.meta
Normal file
7
Assets/Shaders/include/Random.hlsl.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 449b724a594ffa148b2b2ad6f2ecc510
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Loading…
Reference in New Issue
Block a user