{"author_link":"\/users\/ecmjohnson","author_name":"ecmjohnson","author_uid":"ecmjohnson","comments":[],"epoch":1545883679,"event":"LD43","format":"md","ldjam_node_id":137957,"likes":5,"metadata":{"p_key":"127397","p_author":"ecmjohnson","p_authorkey":"1074151","p_urlkey":"343519","p_title":"2D Reflections in AkimBear","p_cat":"LDJam ","p_event":"LD43","p_time":"1545883679","p_likes":"5","p_comments":"0","p_status":"WAYBACK","us_key":"1074151","us_name":"ecmjohnson","us_username":"ecmjohnson","event_start":"1543536000","event_key":"71","event_name":"LD 43"},"node":{"_collation":{"body_sanitizer":"TextUtils::SanitizeHTML via existing importer","event":"LD43","removed_author":false},"_superparent":120415,"_trust":1,"author":74151,"body":"I\u2019d like to share my approach for creating reflections for AkimBear in Unity since I think it turned out rather well and could give you some ideas for future projects. I am a technical one so there will be code, but I\u2019ll try to keep it to a minimum.\n\n# Inspiration\n\nIf our reflections look at all familiar that\u2019s because I based them on [this post from Kingdom\u2019s TIGsource](https:\/\/forums.tigsource.com\/index.php?topic=40539.20). I wasn\u2019t as strict on my pixel precision and didn\u2019t do any of the perspective correction that Kingdom details (this being a game jam after all).\n\n# Overview\n\nYou can get an idea of the final result (if you haven\u2019t played [AkimBear]( https:\/\/ldjam.com\/events\/ludum-dare\/43\/akimbear) yet) from the trailer:\n\nhttps:\/\/youtu.be\/avRx-U6SNHk\n\nI felt that a flowchart of the rendering to attain the reflection would give the best sense for how this effect is achieved at a system level:\n\n![RenderFlow.png](\/\/\/raw\/7a1\/21\/z\/1fcde.png)\n\nIn terms of components, the system is just a camera for capturing the area to be reflected and the reflection itself.\n\n![ReflectionSystem.PNG](\/\/\/raw\/7a1\/21\/z\/1fcdf.png)\n\nEasy, right? Stick around for the alignment issues.\n\n# More perspectives on the world\n\nApparently, a camera in Unity cannot both display on the screen and render to a texture. Initially I found this disappointing as that\u2019s what reflection is right? I think this may have actually been a blessing in disguise; AkimBear has a rather lively camera (both position and orthographic size are constantly changing) and I might have ended up with even more mess if I\u2019d been trying to use that camera to generate reflections.\n\nA second camera it was! This camera is offset up from the main camera and it has a render texture set as its target texture. Other than that, it\u2019s identical to the main camera\u2026 or so I thought initially (see camera property matching script later). This was my first eureka moment, since I could see the screen in the inspector for the render texture:\n\n![ScreenTexture_inspector.PNG](\/\/\/raw\/7a1\/21\/z\/1fce0.png)\n\nThe TIGsource thread from Kingdom had stated that this was not possible without the Pro version of Unity so I was very, very happy when this just worked without being pay-walled.\n\nIt\u2019s basically done now right?\n\n# Sitting under a tree on a sunny day\n\nThe task of the shader is quite simple: take a sample from both the screen texture and the highlight texture and then combine them. The sample into the screen texture should ripple to give the sense of water and the highlight should show some flow. I\u2019ve commented this snippet ([shader source here]( https:\/\/gist.github.com\/ecmjohnson\/d97bf56db855c228c8761a42603e7491#file-reflection-shader)) to give you an idea of what everything does:\n\n```\nfixed4 frag(v2f IN) : SV_Target\n{\n\t\/\/ Generate an offset value in both x and y directions\n\tfloat offx = noise(15.0 * IN.texcoord - 1.5 * _Time.yx);\n\tfloat offy = noise(0.05 * IN.texcoord);\n\t\/\/ Sample the screen texture offset in the x direction\n\tfloat2 uv = IN.texcoord + float2(0.02 * offx, 0.0);\n\tfixed4 c = tex2D(_ScreenTex, uv) * IN.color;\n\t\/\/ Sample the highlight texture\n\tuv.y += offy;\n\tfixed4 h = tex2D(_Highlights, 3.0 * uv + _Time.yx \/ 2.0) * IN.color;\n\t\/\/ This is not good shader code; should be no dynamic branching!\n\t\/\/ maybe o = lerp(c, h, h.a) would have worked as well?\n\tfixed4 o;\n\tif (h.a > 0.5) {\n\t\to = lerp(c, h, 0.5);\n\t}\n\telse {\n\t\to = c;\n\t}\n\to.rgb *= o.a;\n\treturn o;\n}\n```\n\nWhy did I use a noise function seeded with a 2D vector? This is a legitimate question and I\u2019m not sure (I\u2019m sure it made more sense at the time). The same result could have been attained with a few trig functions and would likely have been more efficient.\n\nWhy all the magic numbers? If you take their product it spells game jam in ascii. Just take my word for that.\n\nAll done right?\n\n![alignment_bad.gif](\/\/\/raw\/7a1\/21\/z\/1fce1.gif)\n\nWell, its kind of reflecting.\n\n# The last 10% is 90% of the work (but not of this post)\n\nA few unacceptable issues are apparent. They all stem from using Cinemachine to control our camera for following Grizzle and creating screen shake. This means that it isn't as simple as just attaching our second camera and reflection surface to the main camera. This was solved with a few scripts to [exactly follow the Cinemachine (main) camera on any axis]( https:\/\/gist.github.com\/ecmjohnson\/d97bf56db855c228c8761a42603e7491#file-exactfollow-cs) and [match the parameters for the two cameras]( https:\/\/gist.github.com\/ecmjohnson\/d97bf56db855c228c8761a42603e7491#file-matchcameraprops-cs). The parameter matching is incredibly hacky: it uses linear interpolation across empirical values to keep the reflection camera at the right offset while the orthographic size is varying.\n\nAfter much hacking, both the reflection camera and surface were following the wild zooms and shakes. This took much longer than the actual reflection to get right. \n\n# Remaining bug\n\nThe last remaining (apparent) issue with the reflection system is quite awful. I really wished I\u2019d had time to fix it (maybe a variable passed to the shader on a per-frame basis to compensate for it?). But I\u2019ve yet to hear anyone notice it on their own, so if you see it keep it to yourself.\n\n# [Find the bug yourself by playing AkimBear!](https:\/\/ldjam.com\/events\/ludum-dare\/43\/akimbear)\n\nThis was a pretty quick overview, so if you want to know more, feel free to reach out! If you want further detail than the comments allow, my email is ecmjohnson at gmail.com","comments":0,"created":"2018-12-27T03:33:36Z","files":[],"files-timestamp":0,"id":137957,"love":5,"love-timestamp":"2018-12-27T07:36:35Z","meta":[],"modified":"2018-12-27T07:36:35Z","name":"2D Reflections in AkimBear","node-timestamp":"2018-12-27T04:09:36Z","parent":128684,"parents":[1,5,9,120415,128684],"path":"\/events\/ludum-dare\/43\/akimbear\/2d-reflections-in-akimbear","published":"2018-12-27T04:07:59Z","scope":"public","slug":"2d-reflections-in-akimbear","subsubtype":"","subtype":"","type":"post","version":415526},"node_metadata":{"n_key":"137957","n_urlkey":"343519","n_parent":"128684","n_path":"\/events\/ludum-dare\/43\/akimbear\/2d-reflections-in-akimbear","n_slug":"2d-reflections-in-akimbear","n_type":"post","n_subtype":"","n_subsubtype":"","n_author":"74151","n_created":"1545881616","n_modified":"1545896195","n_version":"415526","n_status":"WAYBACK"},"source_url":"https:\/\/ldjam.com\/events\/ludum-dare\/43\/akimbear\/2d-reflections-in-akimbear","text":"I\u2019d like to share my approach for creating reflections for AkimBear in Unity since I think it turned out rather well and could give you some ideas for future projects. I am a technical one so there will be code, but I\u2019ll try to keep it to a minimum.\n\n# Inspiration\n\nIf our reflections look at all familiar that\u2019s because I based them on [this post from Kingdom\u2019s TIGsource](https:\/\/forums.tigsource.com\/index.php?topic=40539.20). I wasn\u2019t as strict on my pixel precision and didn\u2019t do any of the perspective correction that Kingdom details (this being a game jam after all).\n\n# Overview\n\nYou can get an idea of the final result (if you haven\u2019t played [AkimBear]( https:\/\/ldjam.com\/events\/ludum-dare\/43\/akimbear) yet) from the trailer:\n\nhttps:\/\/youtu.be\/avRx-U6SNHk\n\nI felt that a flowchart of the rendering to attain the reflection would give the best sense for how this effect is achieved at a system level:\n\n![RenderFlow.png](\/\/\/raw\/7a1\/21\/z\/1fcde.png)\n\nIn terms of components, the system is just a camera for capturing the area to be reflected and the reflection itself.\n\n![ReflectionSystem.PNG](\/\/\/raw\/7a1\/21\/z\/1fcdf.png)\n\nEasy, right? Stick around for the alignment issues.\n\n# More perspectives on the world\n\nApparently, a camera in Unity cannot both display on the screen and render to a texture. Initially I found this disappointing as that\u2019s what reflection is right? I think this may have actually been a blessing in disguise; AkimBear has a rather lively camera (both position and orthographic size are constantly changing) and I might have ended up with even more mess if I\u2019d been trying to use that camera to generate reflections.\n\nA second camera it was! This camera is offset up from the main camera and it has a render texture set as its target texture. Other than that, it\u2019s identical to the main camera\u2026 or so I thought initially (see camera property matching script later). This was my first eureka moment, since I could see the screen in the inspector for the render texture:\n\n![ScreenTexture_inspector.PNG](\/\/\/raw\/7a1\/21\/z\/1fce0.png)\n\nThe TIGsource thread from Kingdom had stated that this was not possible without the Pro version of Unity so I was very, very happy when this just worked without being pay-walled.\n\nIt\u2019s basically done now right?\n\n# Sitting under a tree on a sunny day\n\nThe task of the shader is quite simple: take a sample from both the screen texture and the highlight texture and then combine them. The sample into the screen texture should ripple to give the sense of water and the highlight should show some flow. I\u2019ve commented this snippet ([shader source here]( https:\/\/gist.github.com\/ecmjohnson\/d97bf56db855c228c8761a42603e7491#file-reflection-shader)) to give you an idea of what everything does:\n\n```\nfixed4 frag(v2f IN) : SV_Target\n{\n\t\/\/ Generate an offset value in both x and y directions\n\tfloat offx = noise(15.0 * IN.texcoord - 1.5 * _Time.yx);\n\tfloat offy = noise(0.05 * IN.texcoord);\n\t\/\/ Sample the screen texture offset in the x direction\n\tfloat2 uv = IN.texcoord + float2(0.02 * offx, 0.0);\n\tfixed4 c = tex2D(_ScreenTex, uv) * IN.color;\n\t\/\/ Sample the highlight texture\n\tuv.y += offy;\n\tfixed4 h = tex2D(_Highlights, 3.0 * uv + _Time.yx \/ 2.0) * IN.color;\n\t\/\/ This is not good shader code; should be no dynamic branching!\n\t\/\/ maybe o = lerp(c, h, h.a) would have worked as well?\n\tfixed4 o;\n\tif (h.a > 0.5) {\n\t\to = lerp(c, h, 0.5);\n\t}\n\telse {\n\t\to = c;\n\t}\n\to.rgb *= o.a;\n\treturn o;\n}\n```\n\nWhy did I use a noise function seeded with a 2D vector? This is a legitimate question and I\u2019m not sure (I\u2019m sure it made more sense at the time). The same result could have been attained with a few trig functions and would likely have been more efficient.\n\nWhy all the magic numbers? If you take their product it spells game jam in ascii. Just take my word for that.\n\nAll done right?\n\n![alignment_bad.gif](\/\/\/raw\/7a1\/21\/z\/1fce1.gif)\n\nWell, its kind of reflecting.\n\n# The last 10% is 90% of the work (but not of this post)\n\nA few unacceptable issues are apparent. They all stem from using Cinemachine to control our camera for following Grizzle and creating screen shake. This means that it isn't as simple as just attaching our second camera and reflection surface to the main camera. This was solved with a few scripts to [exactly follow the Cinemachine (main) camera on any axis]( https:\/\/gist.github.com\/ecmjohnson\/d97bf56db855c228c8761a42603e7491#file-exactfollow-cs) and [match the parameters for the two cameras]( https:\/\/gist.github.com\/ecmjohnson\/d97bf56db855c228c8761a42603e7491#file-matchcameraprops-cs). The parameter matching is incredibly hacky: it uses linear interpolation across empirical values to keep the reflection camera at the right offset while the orthographic size is varying.\n\nAfter much hacking, both the reflection camera and surface were following the wild zooms and shakes. This took much longer than the actual reflection to get right. \n\n# Remaining bug\n\nThe last remaining (apparent) issue with the reflection system is quite awful. I really wished I\u2019d had time to fix it (maybe a variable passed to the shader on a per-frame basis to compensate for it?). But I\u2019ve yet to hear anyone notice it on their own, so if you see it keep it to yourself.\n\n# [Find the bug yourself by playing AkimBear!](https:\/\/ldjam.com\/events\/ludum-dare\/43\/akimbear)\n\nThis was a pretty quick overview, so if you want to know more, feel free to reach out! If you want further detail than the comments allow, my email is ecmjohnson at gmail.com","title":"2D Reflections in AkimBear","wayback_source":[]}