├── .gitattributes ├── icon.png ├── logs └── godot.log ├── .gitignore ├── README.md ├── assets ├── grass_straw.mtl ├── grass_straw.obj └── grass_straw.obj.import ├── default_env.tres ├── grass_mover.gd ├── icon.png.import ├── LICENSE ├── grass.shader └── project.godot /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Janders1800/GLES2-Interactive-Grass/HEAD/icon.png -------------------------------------------------------------------------------- /logs/godot.log: -------------------------------------------------------------------------------- 1 | Godot Engine v3.2.3.stable.official - https://godotengine.org 2 | OpenGL ES 3.0 Renderer: Intel(R) UHD Graphics 620 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Godot-specific ignores 3 | .import/ 4 | export.cfg 5 | export_presets.cfg 6 | 7 | # Mono-specific ignores 8 | .mono/ 9 | data_*/ 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GLES2-Interactive-Grass 2 | A GLES2 version of the GDQuest grass shader. 3 | The player displacement is based on Kasper Arnklit Frandsen shader. 4 | -------------------------------------------------------------------------------- /assets/grass_straw.mtl: -------------------------------------------------------------------------------- 1 | # Blender MTL File: 'None' 2 | # Material Count: 1 3 | 4 | newmtl None 5 | Ns 500 6 | Ka 0.8 0.8 0.8 7 | Kd 0.8 0.8 0.8 8 | Ks 0.8 0.8 0.8 9 | d 1 10 | illum 2 11 | -------------------------------------------------------------------------------- /default_env.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" load_steps=2 format=2] 2 | 3 | [sub_resource type="ProceduralSky" id=1] 4 | 5 | [resource] 6 | background_mode = 2 7 | background_sky = SubResource( 1 ) 8 | -------------------------------------------------------------------------------- /grass_mover.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends Spatial 3 | 4 | 5 | # Called when the node enters the scene tree for the first time. 6 | func _process(delta: float) -> void: 7 | get_parent().get_parent().get_node("Grass").material_override.set_shader_param("player_pos", global_transform.origin) 8 | -------------------------------------------------------------------------------- /assets/grass_straw.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.91.2 OBJ File: '' 2 | # www.blender.org 3 | o grass_straw_Plane 4 | v -0.000000 0.500000 0.000000 5 | v 0.000000 0.000000 0.050000 6 | v 0.000000 0.000000 -0.050000 7 | vt 0.500000 1.000000 8 | vt 0.000000 0.000000 9 | vt 1.000000 0.000000 10 | vn 1.0000 0.0000 0.0000 11 | s off 12 | f 1/1/1 2/2/1 3/3/1 13 | -------------------------------------------------------------------------------- /assets/grass_straw.obj.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="wavefront_obj" 4 | type="Mesh" 5 | path="res://.import/grass_straw.obj-51027a4338858db480209e3245334277.mesh" 6 | 7 | [deps] 8 | 9 | files=[ "res://.import/grass_straw.obj-51027a4338858db480209e3245334277.mesh" ] 10 | 11 | source_file="res://assets/grass_straw.obj" 12 | dest_files=[ "res://.import/grass_straw.obj-51027a4338858db480209e3245334277.mesh", "res://.import/grass_straw.obj-51027a4338858db480209e3245334277.mesh" ] 13 | 14 | [params] 15 | 16 | generate_tangents=true 17 | scale_mesh=Vector3( 1, 1, 1 ) 18 | offset_mesh=Vector3( 0, 0, 0 ) 19 | optimize_mesh=true 20 | -------------------------------------------------------------------------------- /icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://icon.png" 13 | dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 GDQuest, Kasper Arnklit Frandsen, Janders1800 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /grass.shader: -------------------------------------------------------------------------------- 1 | shader_type spatial; 2 | render_mode cull_disabled; 3 | 4 | uniform float wind_speed = 0.2; 5 | uniform float wind_strength = 2.0; 6 | // How big, in world space, is the noise texture 7 | // wind will tile every wind_texture_tile_size 8 | uniform float wind_texture_tile_size = 20.0; 9 | uniform float wind_vertical_strength = 0.3; 10 | uniform vec2 wind_horizontal_direction = vec2(1.0,0.5); 11 | 12 | uniform sampler2D color_ramp : hint_black_albedo; 13 | // we need a tiling noise here! 14 | uniform sampler2D wind_noise : hint_black; 15 | 16 | uniform vec3 character_position; 17 | uniform float character_radius = 3.0; 18 | uniform sampler2D character_distance_falloff_curve : hint_black_albedo; 19 | uniform float character_push_strength = 1.0; 20 | 21 | // Player deformation variables 22 | 23 | uniform vec3 player_pos = vec3(0.0); 24 | uniform float interact_power = 0.5; 25 | uniform float radius = 1.0; 26 | 27 | 28 | void vertex() { 29 | 30 | vec3 world_vert = (WORLD_MATRIX * vec4(VERTEX, 1.0)).xyz; 31 | 32 | vec2 normalized_wind_direction = normalize(wind_horizontal_direction); 33 | vec2 world_uv = world_vert.xz / wind_texture_tile_size + normalized_wind_direction * TIME * wind_speed; 34 | // we displace only the top part of the mesh 35 | // note that this means that the mesh needs to have UV in a way that the bottom of UV space 36 | // is at the top of the mesh 37 | float displacement_affect = (1.0 - UV.y); 38 | float wind_noise_intensity = (textureLod(wind_noise, world_uv , 0.0).r - 0.5); 39 | 40 | // We convert the direction of the wind into vertex space from world space 41 | // if we used it directly in vertex space, rotated blades of grass wouldn't behave properly 42 | vec2 vert_space_horizontal_dir = (inverse(WORLD_MATRIX) * vec4(wind_horizontal_direction, 0.0,0.0)).xy; 43 | vert_space_horizontal_dir = normalize(vert_space_horizontal_dir); 44 | 45 | vec3 bump_wind = vec3( 46 | wind_noise_intensity * vert_space_horizontal_dir.x, 47 | 1.0 - wind_noise_intensity, 48 | wind_noise_intensity * vert_space_horizontal_dir.y 49 | ); 50 | normalize(bump_wind); 51 | bump_wind *= vec3( 52 | wind_strength, 53 | wind_vertical_strength, 54 | wind_strength 55 | ); 56 | VERTEX += bump_wind * displacement_affect; 57 | 58 | // Simple deformation based on the player position, needed for GLES2 59 | 60 | vec3 direction = world_vert - player_pos; 61 | direction.y = 0.0; 62 | direction = normalize(direction); 63 | float dist = distance(player_pos, world_vert); 64 | float power = smoothstep(radius, 0.0, dist); 65 | direction = (vec4(direction, 1.0) * WORLD_MATRIX).xyz; // world space direction to model space 66 | VERTEX += direction * power * interact_power * (1.0 - UV.y); 67 | 68 | } 69 | 70 | void fragment() { 71 | ALBEDO = texture(color_ramp, vec2(1.0 - UV.y, 0)).rgb ; 72 | } -------------------------------------------------------------------------------- /project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=4 10 | 11 | _global_script_classes=[ ] 12 | _global_script_class_icons={ 13 | 14 | } 15 | 16 | [application] 17 | 18 | config/name="grass_interact" 19 | run/main_scene="res://World.tscn" 20 | config/icon="res://icon.png" 21 | 22 | [input] 23 | 24 | ui_left={ 25 | "deadzone": 0.5, 26 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777231,"unicode":0,"echo":false,"script":null) 27 | , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":14,"pressure":0.0,"pressed":false,"script":null) 28 | , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":65,"unicode":0,"echo":false,"script":null) 29 | ] 30 | } 31 | ui_right={ 32 | "deadzone": 0.5, 33 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777233,"unicode":0,"echo":false,"script":null) 34 | , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":15,"pressure":0.0,"pressed":false,"script":null) 35 | , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":68,"unicode":0,"echo":false,"script":null) 36 | ] 37 | } 38 | ui_up={ 39 | "deadzone": 0.5, 40 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777232,"unicode":0,"echo":false,"script":null) 41 | , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":12,"pressure":0.0,"pressed":false,"script":null) 42 | , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":87,"unicode":0,"echo":false,"script":null) 43 | ] 44 | } 45 | ui_down={ 46 | "deadzone": 0.5, 47 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777234,"unicode":0,"echo":false,"script":null) 48 | , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":13,"pressure":0.0,"pressed":false,"script":null) 49 | , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":83,"unicode":0,"echo":false,"script":null) 50 | ] 51 | } 52 | 53 | [rendering] 54 | 55 | quality/driver/driver_name="GLES2" 56 | quality/filters/msaa=3 57 | environment/default_environment="res://default_env.tres" 58 | quality/filters/use_fxaa=true 59 | --------------------------------------------------------------------------------