{"author_link":"\/users\/joe-z","author_name":"joe.z","author_uid":"joe-z","comments":[],"epoch":1619989414,"event":"LD48","format":"md","ldjam_node_id":255937,"likes":2,"metadata":{"p_key":"157512","p_author":"joe.z","p_authorkey":"1245803","p_urlkey":"377759","p_title":"How We Made Our Program Visual For Egress!","p_cat":"LDJam ","p_event":"LD48","p_time":"1619989414","p_likes":"2","p_comments":"0","p_status":"WAYBACK","us_key":"1245803","us_name":"joe.z","us_username":"joe-z","event_start":"1619222400","event_key":"109","event_name":"Ludum Dare 48"},"node":{"_collation":{"body_sanitizer":"TextUtils::SanitizeHTML via existing importer","event":"LD48","removed_author":false},"_superparent":233335,"_trust":1,"author":245803,"body":"When you making a game about hacking, you obviously need some sort of unrealistic visualization of the process. That's just a given. For our game, [Egress](https:\/\/ldjam.com\/events\/ludum-dare\/48\/egress), we wrote a shader program using Godot's native shading language that would display an cyclical-bar-visualizer-thingamajig:\n\n![program.gif](\/\/\/raw\/b20\/c3\/z\/42090.gif)\n\nThe first step in the process was figuring out how to display the circle as individual bars that could be animated and scaled independently. This was done by getting the angle of a given UV coordinate with respect to the centre of the circle:\n```\nvec2 xy = UV - 0.5;\nfloat angle = (atan(xy.y \/ xy.x));\n```\nThen, this angle was sorted into a section of the circle with a span equal to ```PI \/ number_of_bars```\n```\nfloat section = angle - mod(angle, 3.14159 \/ float(bars));\n```\nNow that we have the section it falls into, we can then use this to generate a random value unique to that section by using a random function (thanks Stack Overflow!):\n```\nuniform float seed;  \/\/ Can be used to seed the random number generation\n\n\/\/ https:\/\/stackoverflow.com\/questions\/4200224\/random-noise-functions-for-glsl\nfloat rand(float value) {\n\treturn fract(sin(dot(vec2(value, seed), vec2(12.9898,78.233))) * 43758.5453);\n}\n```\n\nUsing this function, we can generate a random height for each bar and have that height also fluctuate using the same random value:\n```\nfloat radius = min_length + rand(section) * (max_length - min_length);\nradius += pulsate_strength * sin(rand(section) * 6.282 + TIME * pulsate_speed);\n```\n\nThe last piece of the puzzle is using this radius for rendering the object. This is done by determining if the UV coordinate lies within a circle of that radius, using the implicit function of a circle:\n```\nif (pow(xy.x, 2) + pow(xy.y, 2) < pow(radius, 2) &&\n\t\tpow(xy.x, 2) + pow(xy.y, 2) > pow(inner_radius, 2)) {\n\tCOLOR = vec4(1.0);\n} else {\n\tCOLOR = vec4(0.0);\n}\n```\nAs the output colour is white, we can then modulate the resulting texture to be whatever colour we want!\n\n## The Complete Code\n```\nshader_type canvas_item;\n\nuniform float inner_radius : hint_range(0, 0.5);\nuniform float min_length : hint_range(0, 0.5) = 0.2;\nuniform float max_length : hint_range(0, 0.5) = 0.5;\nuniform int bars : hint_range(2, 256);\nuniform float seed;\nuniform float rotation_speed;\nuniform float pulsate_speed;\nuniform float pulsate_strength;\n\n\/\/ https:\/\/stackoverflow.com\/questions\/4200224\/random-noise-functions-for-glsl\nfloat rand(float value) {\n\treturn fract(sin(dot(vec2(value, seed), vec2(12.9898,78.233))) * 43758.5453);\n}\n\nvec2 rotate(vec2 uv, float rotation) {\n\tvec2 rotated;\n\trotated.x = uv.x * cos(rotation) - uv.y * sin(rotation);\n\trotated.y = uv.x * sin(rotation) + uv.y * cos(rotation);\n\treturn rotated;\n}\n\nvoid fragment() {\n\tvec2 xy = rotate(UV - 0.5, TIME * rotation_speed);\n\t\n\tfloat angle = (atan(xy.y \/ xy.x));\n\tfloat section = angle - mod(angle, 3.14159 \/ float(bars));\n\tfloat radius = min_length + rand(section) * (max_length - min_length);\n\tradius += pulsate_strength * sin(rand(section) * 6.282 + TIME * pulsate_speed);\n\tif (pow(xy.x, 2) + pow(xy.y, 2) < pow(radius, 2) &&\n\t\t\tpow(xy.x, 2) + pow(xy.y, 2) > pow(inner_radius, 2)) {\n\t\tCOLOR = vec4(1.0);\n\t} else {\n\t\tCOLOR = vec4(0.0);\n\t}\n}\n```\nIf you haven't checked out our game yet, we would love for you to give a shot and give some feedback!  \n  \nIt can be played in your browser here: https:\/\/lunatic-games.itch.io\/egress","comments":0,"created":"2021-05-02T19:57:48Z","files":[],"files-timestamp":0,"id":255937,"love":2,"love-timestamp":"2021-05-02T21:58:11Z","meta":[],"modified":"2021-05-02T21:58:11Z","name":"How We Made Our Program Visual For Egress!","node-timestamp":"2021-05-02T21:03:34Z","parent":245128,"parents":[1,5,9,233335,245128],"path":"\/events\/ludum-dare\/48\/egress\/how-we-made-our-program-visual-for-egress","published":"2021-05-02T21:03:34Z","scope":"public","slug":"how-we-made-our-program-visual-for-egress","subsubtype":"","subtype":"","type":"post","version":786891},"node_metadata":{"n_key":"255937","n_urlkey":"377759","n_parent":"245128","n_path":"\/events\/ludum-dare\/48\/egress\/how-we-made-our-program-visual-for-egress","n_slug":"how-we-made-our-program-visual-f","n_type":"post","n_subtype":"","n_subsubtype":"","n_author":"245803","n_created":"1619985468","n_modified":"1619992691","n_version":"786891","n_status":"WAYBACK"},"source_url":"https:\/\/ldjam.com\/events\/ludum-dare\/48\/egress\/how-we-made-our-program-visual-for-egress","text":"When you making a game about hacking, you obviously need some sort of unrealistic visualization of the process. That's just a given. For our game, [Egress](https:\/\/ldjam.com\/events\/ludum-dare\/48\/egress), we wrote a shader program using Godot's native shading language that would display an cyclical-bar-visualizer-thingamajig:\n\n![program.gif](\/\/\/raw\/b20\/c3\/z\/42090.gif)\n\nThe first step in the process was figuring out how to display the circle as individual bars that could be animated and scaled independently. This was done by getting the angle of a given UV coordinate with respect to the centre of the circle:\n```\nvec2 xy = UV - 0.5;\nfloat angle = (atan(xy.y \/ xy.x));\n```\nThen, this angle was sorted into a section of the circle with a span equal to ```PI \/ number_of_bars```\n```\nfloat section = angle - mod(angle, 3.14159 \/ float(bars));\n```\nNow that we have the section it falls into, we can then use this to generate a random value unique to that section by using a random function (thanks Stack Overflow!):\n```\nuniform float seed;  \/\/ Can be used to seed the random number generation\n\n\/\/ https:\/\/stackoverflow.com\/questions\/4200224\/random-noise-functions-for-glsl\nfloat rand(float value) {\n\treturn fract(sin(dot(vec2(value, seed), vec2(12.9898,78.233))) * 43758.5453);\n}\n```\n\nUsing this function, we can generate a random height for each bar and have that height also fluctuate using the same random value:\n```\nfloat radius = min_length + rand(section) * (max_length - min_length);\nradius += pulsate_strength * sin(rand(section) * 6.282 + TIME * pulsate_speed);\n```\n\nThe last piece of the puzzle is using this radius for rendering the object. This is done by determining if the UV coordinate lies within a circle of that radius, using the implicit function of a circle:\n```\nif (pow(xy.x, 2) + pow(xy.y, 2) < pow(radius, 2) &&\n\t\tpow(xy.x, 2) + pow(xy.y, 2) > pow(inner_radius, 2)) {\n\tCOLOR = vec4(1.0);\n} else {\n\tCOLOR = vec4(0.0);\n}\n```\nAs the output colour is white, we can then modulate the resulting texture to be whatever colour we want!\n\n## The Complete Code\n```\nshader_type canvas_item;\n\nuniform float inner_radius : hint_range(0, 0.5);\nuniform float min_length : hint_range(0, 0.5) = 0.2;\nuniform float max_length : hint_range(0, 0.5) = 0.5;\nuniform int bars : hint_range(2, 256);\nuniform float seed;\nuniform float rotation_speed;\nuniform float pulsate_speed;\nuniform float pulsate_strength;\n\n\/\/ https:\/\/stackoverflow.com\/questions\/4200224\/random-noise-functions-for-glsl\nfloat rand(float value) {\n\treturn fract(sin(dot(vec2(value, seed), vec2(12.9898,78.233))) * 43758.5453);\n}\n\nvec2 rotate(vec2 uv, float rotation) {\n\tvec2 rotated;\n\trotated.x = uv.x * cos(rotation) - uv.y * sin(rotation);\n\trotated.y = uv.x * sin(rotation) + uv.y * cos(rotation);\n\treturn rotated;\n}\n\nvoid fragment() {\n\tvec2 xy = rotate(UV - 0.5, TIME * rotation_speed);\n\t\n\tfloat angle = (atan(xy.y \/ xy.x));\n\tfloat section = angle - mod(angle, 3.14159 \/ float(bars));\n\tfloat radius = min_length + rand(section) * (max_length - min_length);\n\tradius += pulsate_strength * sin(rand(section) * 6.282 + TIME * pulsate_speed);\n\tif (pow(xy.x, 2) + pow(xy.y, 2) < pow(radius, 2) &&\n\t\t\tpow(xy.x, 2) + pow(xy.y, 2) > pow(inner_radius, 2)) {\n\t\tCOLOR = vec4(1.0);\n\t} else {\n\t\tCOLOR = vec4(0.0);\n\t}\n}\n```\nIf you haven't checked out our game yet, we would love for you to give a shot and give some feedback!  \n  \nIt can be played in your browser here: https:\/\/lunatic-games.itch.io\/egress","title":"How We Made Our Program Visual For Egress!","wayback_source":[]}