├── .gitignore ├── Demo.tscn ├── LICENSE.md ├── README.md ├── addons └── ascii-shader │ ├── AsciiRenderer.tscn │ ├── ascii.gdshader │ ├── character_map.png │ └── character_map.png.import ├── default_env.tres ├── icon.png ├── icon.png.import ├── project.godot ├── screenshot.png └── screenshot.png.import /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Godot-specific ignores 3 | .import/ 4 | export.cfg 5 | export_presets.cfg 6 | 7 | # Imported translations (automatically generated from CSV files) 8 | *.translation 9 | 10 | # Mono-specific ignores 11 | .mono/ 12 | data_*/ 13 | -------------------------------------------------------------------------------- /Demo.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=8 format=3 uid="uid://bymo8onom66rj"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://d3ecdsitw4adk" path="res://addons/ascii-shader/AsciiRenderer.tscn" id="1"] 4 | [ext_resource type="Texture2D" uid="uid://doyegtmktitfo" path="res://icon.png" id="2"] 5 | 6 | [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_41wf3"] 7 | albedo_color = Color(1, 0.34902, 0.34902, 1) 8 | 9 | [sub_resource type="BoxMesh" id="4"] 10 | size = Vector3(2, 2, 2) 11 | 12 | [sub_resource type="CapsuleMesh" id="6"] 13 | radius = 2.98 14 | height = 13.216 15 | 16 | [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_eouq5"] 17 | albedo_texture = ExtResource("2") 18 | 19 | [sub_resource type="PlaneMesh" id="7"] 20 | size = Vector2(10, 10) 21 | 22 | [node name="Demo" type="Node3D"] 23 | 24 | [node name="Cube" type="MeshInstance3D" parent="."] 25 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.29525, 0.61121, -0.0295248) 26 | material_override = SubResource("StandardMaterial3D_41wf3") 27 | mesh = SubResource("4") 28 | 29 | [node name="Capsule" type="MeshInstance3D" parent="."] 30 | transform = Transform3D(0.941169, -0.231923, -0.24579, 0.260389, 0.0340937, 0.964902, -0.215403, -0.972137, 0.092478, -7.01063, 0, 0) 31 | mesh = SubResource("6") 32 | 33 | [node name="AsciiRenderer" parent="." instance=ExtResource("1")] 34 | cast_shadow = 0 35 | extra_cull_margin = 16384.0 36 | 37 | [node name="MeshInstance3D" type="MeshInstance3D" parent="."] 38 | transform = Transform3D(1, 0, 0, 0, 0.512508, -0.858683, 0, 0.858683, 0.512508, 1.78742, 2.04265, -7.38823) 39 | material_override = SubResource("StandardMaterial3D_eouq5") 40 | mesh = SubResource("7") 41 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Karl 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # godot-ascii-shader 2 | 3 | ![Screenshot](screenshot.png) 4 | 5 | A simple ASCII shader for Godot. Renders whatever the camera would render, but as ASCII art. Works in-game and in-editor. 6 | 7 | Inspired by https://github.com/StefanJo3107/ASCII-Rendering-Shader-in-Unity. 8 | 9 | ## Usage 10 | 11 | Just add the AsciiRenderer node to the scene which should be rendered that way, and you're done! The character size can be changed in the shader parameters. 12 | 13 | ## Explanation 14 | 15 | The coordinates are clamped to a grid (which will become the ASCII characters). The greyscale color value (the brightness) at these coordinates is used to decide whether a mostly empty character like `.` or a mostly full character like `#` should be used. The coordinates within the current grid cell serve as the UV coordinates in a texture containing ASCII characters, along with the offset from the previous step. 16 | -------------------------------------------------------------------------------- /addons/ascii-shader/AsciiRenderer.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=3 uid="uid://d3ecdsitw4adk"] 2 | 3 | [ext_resource type="Shader" path="res://addons/ascii-shader/ascii.gdshader" id="1"] 4 | [ext_resource type="Texture2D" uid="uid://bwu6thqq1p5v4" path="res://addons/ascii-shader/character_map.png" id="2"] 5 | 6 | [sub_resource type="ShaderMaterial" id="ShaderMaterial_vgbwm"] 7 | render_priority = 0 8 | shader = ExtResource("1") 9 | shader_parameter/number_of_characters = 8.0 10 | shader_parameter/character_size = Vector2(8, 16) 11 | shader_parameter/character_map = ExtResource("2") 12 | 13 | [sub_resource type="QuadMesh" id="1"] 14 | flip_faces = true 15 | 16 | [node name="AsciiRenderer" type="MeshInstance3D"] 17 | material_override = SubResource("ShaderMaterial_vgbwm") 18 | mesh = SubResource("1") 19 | -------------------------------------------------------------------------------- /addons/ascii-shader/ascii.gdshader: -------------------------------------------------------------------------------- 1 | // _ _ _ _ _ _ 2 | // | | | | (_)(_) | | | | 3 | // __ _ ___ __| | ___ | |_ ______ __ _ ___ ___ _ _ ______ ___ | |__ __ _ __| | ___ _ __ 4 | // / _` | / _ \ / _` | / _ \ | __||______|/ _` |/ __| / __|| || ||______|/ __|| '_ \ / _` | / _` | / _ \| '__| 5 | // | (_| || (_) || (_| || (_) || |_ | (_| |\__ \| (__ | || | \__ \| | | || (_| || (_| || __/| | 6 | // \__, | \___/ \__,_| \___/ \__| \__,_||___/ \___||_||_| |___/|_| |_| \__,_| \__,_| \___||_| 7 | // __/ | 8 | // |___/ 9 | // 10 | 11 | shader_type spatial; 12 | render_mode unshaded; 13 | 14 | // Screen texture 15 | uniform sampler2D screen_texture: hint_screen_texture, repeat_disable, filter_nearest; 16 | 17 | // An image containing ASCII characters, from darkest / emptiest (e.g. ' ') to brightest / fullest (e.g. '@') 18 | uniform sampler2D character_map; 19 | 20 | // The number of ASCII characters in the character_map 21 | uniform float number_of_characters = 8.0; 22 | 23 | // The pixel size of ASCII characters rendered on the screen 24 | uniform vec2 character_size = vec2(8.0, 16.0); 25 | 26 | void vertex() { 27 | // Cover the viewport with the mesh 28 | POSITION = vec4(VERTEX, 0.5); 29 | } 30 | 31 | void fragment() { 32 | vec2 character_count = VIEWPORT_SIZE / character_size; 33 | 34 | // Clamp the screen UV coordinates to the future ASCII character grid 35 | vec2 clamped_uv = floor(SCREEN_UV * character_count) / character_count; 36 | 37 | // Calculate the coordinates we're at within the current character 38 | vec2 character_uv = SCREEN_UV * character_count - floor(SCREEN_UV * character_count); 39 | 40 | // The pixel color in this grid position 41 | // We use the clamped_uv here to make sure one character has one color 42 | vec3 color = textureLod(screen_texture, clamped_uv, 0.0).rgb; 43 | 44 | // Get the greyscale value (or brightness) here 45 | float grey = (color.r + color.g + color.b) / 3.0; 46 | 47 | // Using this greyscale value, decide which character in the character_map to use 48 | // The floor and division is to get integer values from 0 to 7 instead of floating points inbetween 49 | vec2 offset = vec2(round(grey * (number_of_characters - 1.0)) / number_of_characters, 0.0); 50 | 51 | // Stencil out the ASCII character by multiplying the color with its alpha 52 | color *= texture(character_map, offset + character_uv * vec2(1.0 / number_of_characters, 1.0)).a; 53 | 54 | // Assign the result 55 | ALBEDO = color; 56 | } -------------------------------------------------------------------------------- /addons/ascii-shader/character_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kb173/godot-ascii-shader/fac202ccaf5d324799c8802bf1498bf3b0834a96/addons/ascii-shader/character_map.png -------------------------------------------------------------------------------- /addons/ascii-shader/character_map.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bwu6thqq1p5v4" 6 | path.s3tc="res://.godot/imported/character_map.png-8a3db54e9896c8abee66212a73579efd.s3tc.ctex" 7 | metadata={ 8 | "imported_formats": ["s3tc_bptc"], 9 | "vram_texture": true 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/ascii-shader/character_map.png" 15 | dest_files=["res://.godot/imported/character_map.png-8a3db54e9896c8abee66212a73579efd.s3tc.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=2 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=true 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=0 36 | -------------------------------------------------------------------------------- /default_env.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" load_steps=2 format=3 uid="uid://cfpicwl321w1x"] 2 | 3 | [sub_resource type="Sky" id="1"] 4 | 5 | [resource] 6 | background_mode = 2 7 | sky = SubResource("1") 8 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kb173/godot-ascii-shader/fac202ccaf5d324799c8802bf1498bf3b0834a96/icon.png -------------------------------------------------------------------------------- /icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://doyegtmktitfo" 6 | path.s3tc="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.s3tc.ctex" 7 | metadata={ 8 | "imported_formats": ["s3tc_bptc"], 9 | "vram_texture": true 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://icon.png" 15 | dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.s3tc.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=2 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=true 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=0 36 | -------------------------------------------------------------------------------- /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=5 10 | 11 | [application] 12 | 13 | config/name="Godot-ascii" 14 | config/features=PackedStringArray("4.2") 15 | config/icon="res://icon.png" 16 | 17 | [rendering] 18 | 19 | environment/defaults/default_environment="res://default_env.tres" 20 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kb173/godot-ascii-shader/fac202ccaf5d324799c8802bf1498bf3b0834a96/screenshot.png -------------------------------------------------------------------------------- /screenshot.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cmwfdi3sw45as" 6 | path="res://.godot/imported/screenshot.png-024a21af5d37bf0f0dd0e2bccdd149d0.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://screenshot.png" 14 | dest_files=["res://.godot/imported/screenshot.png-024a21af5d37bf0f0dd0e2bccdd149d0.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | --------------------------------------------------------------------------------