{"id":1513,"date":"2024-05-27T23:29:56","date_gmt":"2024-05-27T14:29:56","guid":{"rendered":"https:\/\/xn--k10aa.com\/?p=1513"},"modified":"2024-10-20T21:16:31","modified_gmt":"2024-10-20T12:16:31","slug":"cs2","status":"publish","type":"post","link":"https:\/\/remoooo.com\/en\/cs2\/","title":{"rendered":"Compute Shader Learning Notes (II) Post-processing Effects"},"content":{"rendered":"<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-373.png\" alt=\"img\" class=\"wp-image-1519 lazyload\"\/><noscript><img decoding=\"async\" width=\"1440\" height=\"705\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-373.png\" alt=\"img\" class=\"wp-image-1519 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-373.png 1440w, https:\/\/remoooo.com\/wp-content\/uploads\/image-373-300x147.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-373-1024x501.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-373-768x376.png 768w\" sizes=\"(max-width: 1440px) 100vw, 1440px\" \/><\/noscript><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Preface<\/h2>\n\n\n\n<p>Get a preliminary understanding of Compute Shader and implement some simple effects. All the codes are in:<\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/Remyuu\/Unity-Compute-Shader-Learngithub.com\/Remyuu\/Unity-Compute-Shader-Learn\">https:\/\/github.com\/Remyuu\/Unity-Compute-Shader-Learngithub.com\/Remyuu\/Unity-Compute-Shader-Learn<\/a><\/p>\n\n\n\n<p>The main branch is the initial code. You can download the complete project and follow me. PS: I have opened a separate branch for each version of the code.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-374.png\" alt=\"img\" class=\"wp-image-1518 lazyload\"\/><noscript><img decoding=\"async\" width=\"1440\" height=\"455\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-374.png\" alt=\"img\" class=\"wp-image-1518 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-374.png 1440w, https:\/\/remoooo.com\/wp-content\/uploads\/image-374-300x95.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-374-1024x324.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-374-768x243.png 768w\" sizes=\"(max-width: 1440px) 100vw, 1440px\" \/><\/noscript><\/figure>\n\n\n\n<p>This article learns how to use Compute Shader to make:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Post-processing effects<\/li>\n\n\n\n<li>Particle System<\/li>\n<\/ul>\n\n\n\n<p>The previous article did not mention the GPU architecture because I felt that it would be difficult to understand if I explained a bunch of terms right at the beginning. With the experience of actually writing Compute Shader, you can connect the abstract concepts with the actual code.<\/p>\n\n\n\n<p>CUDA on GPU<strong>Execution Program<\/strong>It can be explained by a three-tier architecture:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Grid \u2013 corresponds to a Kernel<\/li>\n\n\n\n<li>|-Block \u2013 A Grid has multiple Blocks, executing the same program<\/li>\n\n\n\n<li>| |-Thread \u2013 The most basic computing unit on the GPU<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-369.png\" alt=\"img\" class=\"wp-image-1514 lazyload\"\/><noscript><img decoding=\"async\" width=\"1440\" height=\"786\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-369.png\" alt=\"img\" class=\"wp-image-1514 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-369.png 1440w, https:\/\/remoooo.com\/wp-content\/uploads\/image-369-300x164.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-369-1024x559.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-369-768x419.png 768w\" sizes=\"(max-width: 1440px) 100vw, 1440px\" \/><\/noscript><\/figure>\n\n\n\n<p>Thread is the most basic unit of GPU, and there will naturally be information exchange between different threads. In order to effectively support the operation of a large number of parallel threads and solve the data exchange requirements between these threads, the memory is designed into multiple levels.<strong>Storage Angle<\/strong>It can also be divided into three layers:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Per-Thread memory \u2013 Within a Thread, the transmission cycle is one clock cycle (less than 1 nanosecond), which can be hundreds of times faster than global memory.<\/li>\n\n\n\n<li>Shared memory \u2013 Between blocks, the speed is much faster than the global speed.<\/li>\n\n\n\n<li>Global memory \u2013 between all threads, but the slowest, usually the bottleneck of the GPU. The Volta architecture uses HBM2 as the global memory of the device, while Turing uses GDDR6.<\/li>\n<\/ul>\n\n\n\n<p>If the memory size limit is exceeded, it will be pushed to larger but slower storage space.<\/p>\n\n\n\n<p>Shared Memory and L1 cache share the same physical space, but they are functionally different: the former needs to be managed manually, while the latter is automatically managed by hardware. My understanding is that Shared Memory is functionally similar to a programmable L1 cache.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-371.png\" alt=\"img\" class=\"wp-image-1516 lazyload\"\/><noscript><img decoding=\"async\" width=\"1440\" height=\"851\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-371.png\" alt=\"img\" class=\"wp-image-1516 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-371.png 1440w, https:\/\/remoooo.com\/wp-content\/uploads\/image-371-300x177.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-371-1024x605.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-371-768x454.png 768w\" sizes=\"(max-width: 1440px) 100vw, 1440px\" \/><\/noscript><\/figure>\n\n\n\n<p>In NVIDIA&#039;s CUDA architecture,<strong>Streaming Multiprocessor (SM)<\/strong>It is a processing unit on the GPU that is responsible for executing the<strong>Blocks<\/strong>Threads in .<strong>Stream Processors<\/strong>, also known as &quot;CUDA cores&quot;, are processing elements within the SM, and each stream processor can process multiple threads in parallel. In general:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>GPU -&gt; Multi-Processors (SMs) -&gt; Stream Processors<\/strong><\/li>\n<\/ul>\n\n\n\n<p>That is, the GPU contains multiple SMs (multiprocessors), each of which contains multiple stream processors. Each stream processor is responsible for executing the computing instructions of one or more threads.<\/p>\n\n\n\n<p>In GPU,<strong>Thread<\/strong>It is the smallest unit for performing calculations.<strong>Warp (latitude)<\/strong>It is the basic execution unit in CUDA.<\/p>\n\n\n\n<p>In NVIDIA&#039;s CUDA architecture, each<strong>Warp<\/strong>Usually contains 32<strong>Threads<\/strong>(AMD has 64).<strong>Block<\/strong>A thread group contains multiple threads.<strong>Block<\/strong>Can contain multiple<strong>Warp<\/strong>.<strong>Kernel<\/strong>is a function executed on the GPU. You can think of it as a specific piece of code that is executed in parallel by all activated threads. In general:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Kernel -&gt; Grid -&gt; Blocks -&gt; Warps -&gt; Threads<\/strong><\/li>\n<\/ul>\n\n\n\n<p>But in daily development, it is usually necessary to execute<strong>Threads<\/strong>Far more than 32.<\/p>\n\n\n\n<p>In order to solve the mismatch between software requirements and hardware architecture, the GPU adopts a strategy: grouping threads belonging to the same block. This grouping is called a &quot;Warp&quot;, and each Warp contains a fixed number of threads. When the number of threads that need to be executed exceeds the number that a Warp can contain, the GPU will schedule additional Warps. The principle of doing this is to ensure that no thread is missed, even if it means starting more Warps.<\/p>\n\n\n\n<p>For example, if a block has 128 threads, and my graphics card is wearing a leather jacket (Nvidia has 32 threads per warp), then a block will have 128\/32=4 warps. To give an extreme example, if there are 129 threads, then 5 warps will be opened. There are 31 thread positions that will be directly idle! Therefore, when we write a compute shader, the a in [numthreads(a,b,c)]<em>b<\/em>c should preferably be a multiple of 32 to reduce the waste of CUDA cores.<\/p>\n\n\n\n<p>You must be confused after reading this. I drew a picture based on my personal understanding. Please point out any mistakes.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-372.png\" alt=\"img\" class=\"wp-image-1517 lazyload\"\/><noscript><img decoding=\"async\" width=\"1440\" height=\"496\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-372.png\" alt=\"img\" class=\"wp-image-1517 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-372.png 1440w, https:\/\/remoooo.com\/wp-content\/uploads\/image-372-300x103.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-372-1024x353.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-372-768x265.png 768w\" sizes=\"(max-width: 1440px) 100vw, 1440px\" \/><\/noscript><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">L3 post-processing effects<\/h2>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>The current build is based on the BIRP pipeline, and the SRP pipeline only requires a few code changes.<\/p>\n<\/blockquote>\n\n\n\n<p>The key to this chapter is to build an abstract base class to manage the resources required by Compute Shader (Section 1). Then, based on this abstract base class, write some simple post-processing effects, such as Gaussian blur, grayscale effect, low-resolution pixel effect, and night vision effect. A brief summary of the knowledge points in this chapter:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Get and process the Camera&#039;s rendering texture<\/li>\n\n\n\n<li>ExecuteInEditMode Keywords<\/li>\n\n\n\n<li>SystemInfo.supportsComputeShaders Checks whether the system supports<\/li>\n\n\n\n<li>Use of Graphics.Blit() function (the whole process is Bit Block Transfer)<\/li>\n\n\n\n<li>Using smoothstep() to create various effects<\/li>\n\n\n\n<li>Data transmission between multiple Kernels Shared keyword<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">1. Introduction and preparation<\/h3>\n\n\n\n<p>Post-processing effects require two textures, one read-only and the other read-write. As for where the textures come from, since it is post-processing, it must be obtained from the camera, that is, the Target Texture on the Camera component.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Source: Read-only<\/li>\n\n\n\n<li>Destination: Readable and writable, used for final output<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-370.png\" alt=\"img\" class=\"wp-image-1515 lazyload\"\/><noscript><img decoding=\"async\" width=\"1262\" height=\"878\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-370.png\" alt=\"img\" class=\"wp-image-1515 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-370.png 1262w, https:\/\/remoooo.com\/wp-content\/uploads\/image-370-300x209.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-370-1024x712.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-370-768x534.png 768w\" sizes=\"(max-width: 1262px) 100vw, 1262px\" \/><\/noscript><\/figure>\n\n\n\n<p>Since a variety of post-processing effects will be implemented later, a base class is abstracted to reduce the workload in the later stage.<\/p>\n\n\n\n<p>The following features are encapsulated in the base class:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Initialize resources (create textures, buffers, etc.)<\/li>\n\n\n\n<li>Manage resources (for example, recreate buffers when screen resolution changes, etc.)<\/li>\n\n\n\n<li>Hardware check (check whether the current device supports Compute Shader)<\/li>\n<\/ul>\n\n\n\n<p>Abstract class complete code link: https:\/\/pastebin.com\/9pYvHHsh<\/p>\n\n\n\n<p>First, when the script instance is activated or attached to a live GO, OnEnable() is called. Write the initialization operations in it. Check whether the hardware supports it, check whether the Compute Shader is bound in the Inspector, get the specified Kernel, get the Camera component of the current GO, create a texture, and set the initialized state to true.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if (!SystemInfo.supportsComputeShaders)\n    ...\n\nif (!shader)\n    ...\n\nkernelHandle = shader.FindKernel(kernelName);\n\nthisCamera = GetComponent&lt;Camera&gt;();\n\nif (!thisCamera)\n    ...\n\nCreateTextures();\n\ninit = true;<\/code><\/pre>\n\n\n\n<p>Create two textures CreateTextures(), one Source and one Destination, with the size of the camera resolution.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>texSize.x = thisCamera.pixelWidth;\ntexSize.y = thisCamera.pixelHeight;\n\nif (shader)\n{\n    uint x, y;\n    shader.GetKernelThreadGroupSizes(kernelHandle, out x, out y, out _);\n    groupSize.x = Mathf.CeilToInt((float)texSize.x \/ (float)x);\n    groupSize.y = Mathf.CeilToInt((float)texSize.y \/ (float)y);\n}\n\nCreateTexture(ref output);\nCreateTexture(ref renderedSource);\n\nshader.SetTexture(kernelHandle, \"source\", renderedSource);\nshader.SetTexture(kernelHandle, \"outputrt\", output);<\/code><\/pre>\n\n\n\n<p>Creation of specific textures:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>protected void CreateTexture(ref RenderTexture textureToMake, int divide=1) { textureToMake = new RenderTexture(texSize.x\/divide, texSize.y\/divide, 0); textureToMake.enableRandomWrite = true; textureToMake.Create(); }<\/code><\/pre>\n\n\n\n<p>This completes the initialization. When the camera finishes rendering the scene and is ready to display it on the screen, Unity will call OnRenderImage(), and then call Compute Shader to start the calculation. If it is not initialized or there is no shader, it will be Blitted and the source will be directly copied to the destination, that is, nothing will be done. CheckResolution(out _) This method checks whether the resolution of the rendered texture needs to be updated. If so, it will regenerate the Texture. After that, it is time for the Dispatch stage. Here, the source map needs to be passed to the GPU through the Buffer, and after the calculation is completed, it will be passed back to the destination.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>protected virtual void OnRenderImage(RenderTexture source, RenderTexture destination) { if (!init || shader == null) { Graphics.Blit(source, destination); } else { CheckResolution(out _); DispatchWithSource(ref source, ref destination) ; } }<\/code><\/pre>\n\n\n\n<p>Note that we don&#039;t use any SetData() or GetData() operations here. Because all the data is on the GPU now, we can just instruct the GPU to do it by itself, and the CPU should not get involved. If we fetch the texture back to memory and then pass it to the GPU, the performance will be very poor.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>protected virtual void DispatchWithSource(ref RenderTexture source, ref RenderTexture destination)\n{\n    Graphics.Blit(source, renderedSource);\n\n    shader.Dispatch(kernelHandle, groupSize.x, groupSize.y, 1);\n\n    Graphics.Blit(output, destination);\n}<\/code><\/pre>\n\n\n\n<p>I didn&#039;t believe it, so I had to transfer it back to the CPU and then back to the GPU. The test results were quite shocking, and the performance was more than 4 times worse. Therefore, we need to reduce the communication between the CPU and GPU, which is very important when using Compute Shader.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \u7b28\u86cb\u65b9\u6cd5\nprotected virtual void DispatchWithSource(ref RenderTexture source, ref RenderTexture destination)\n{\n    \/\/ \u5c06\u6e90\u8d34\u56feBlit\u5230\u7528\u4e8e\u5904\u7406\u7684\u8d34\u56fe\n    Graphics.Blit(source, renderedSource);\n\n    \/\/ \u4f7f\u7528\u8ba1\u7b97\u7740\u8272\u5668\u5904\u7406\u8d34\u56fe\n    shader.Dispatch(kernelHandle, groupSize.x, groupSize.y, 1);\n\n    \/\/ \u5c06\u8f93\u51fa\u8d34\u56fe\u590d\u5236\u5230\u4e00\u4e2aTexture2D\u5bf9\u8c61\u4e2d\uff0c\u4ee5\u4fbf\u8bfb\u53d6\u6570\u636e\u5230CPU\n    Texture2D tempTexture = new Texture2D(renderedSource.width, renderedSource.height, TextureFormat.RGBA32, false);\n    RenderTexture.active = output;\n    tempTexture.ReadPixels(new Rect(0, 0, output.width, output.height), 0, 0);\n    tempTexture.Apply();\n    RenderTexture.active = null;\n\n    \/\/ \u5c06Texture2D\u6570\u636e\u4f20\u56deGPU\u5230\u4e00\u4e2a\u65b0\u7684RenderTexture\n    RenderTexture tempRenderTexture = RenderTexture.GetTemporary(output.width, output.height);\n    Graphics.Blit(tempTexture, tempRenderTexture);\n\n    \/\/ \u6700\u7ec8\u5c06\u5904\u7406\u540e\u7684\u8d34\u56feBlit\u5230\u76ee\u6807\u8d34\u56fe\n    Graphics.Blit(tempRenderTexture, destination);\n\n    \/\/ \u6e05\u7406\u8d44\u6e90\n    RenderTexture.ReleaseTemporary(tempRenderTexture);\n    Destroy(tempTexture);\n}<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-375.png\" alt=\"img\" class=\"wp-image-1520 lazyload\"\/><noscript><img decoding=\"async\" width=\"1372\" height=\"436\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-375.png\" alt=\"img\" class=\"wp-image-1520 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-375.png 1372w, https:\/\/remoooo.com\/wp-content\/uploads\/image-375-300x95.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-375-1024x325.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-375-768x244.png 768w\" sizes=\"(max-width: 1372px) 100vw, 1372px\" \/><\/noscript><\/figure>\n\n\n\n<p>Next, we will start writing our first post-processing effect.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Interlude: Strange BUG<\/h3>\n\n\n\n<p>Also insert a strange bug.<\/p>\n\n\n\n<p>In Compute Shader, if the final output map result is named output, there will be problems in some APIs such as Metal. The solution is to change the name.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>RWTexture2D outputrt;<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-376.png\" alt=\"img\" class=\"wp-image-1521 lazyload\"\/><noscript><img decoding=\"async\" width=\"1090\" height=\"160\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-376.png\" alt=\"img\" class=\"wp-image-1521 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-376.png 1090w, https:\/\/remoooo.com\/wp-content\/uploads\/image-376-300x44.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-376-1024x150.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-376-768x113.png 768w\" sizes=\"(max-width: 1090px) 100vw, 1090px\" \/><\/noscript><\/figure>\n\n\n\n<p>Add a caption for the image, no more than 140 characters (optional)<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2. RingHighlight effect<\/h3>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-382.png\" alt=\"img\" class=\"wp-image-1527 lazyload\"\/><noscript><img decoding=\"async\" width=\"1088\" height=\"604\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-382.png\" alt=\"img\" class=\"wp-image-1527 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-382.png 1088w, https:\/\/remoooo.com\/wp-content\/uploads\/image-382-300x167.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-382-1024x568.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-382-768x426.png 768w\" sizes=\"(max-width: 1088px) 100vw, 1088px\" \/><\/noscript><\/figure>\n\n\n\n<p>Create the RingHighlight class, inheriting from the base class just written.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-378.png\" alt=\"img\" class=\"wp-image-1523 lazyload\"\/><noscript><img decoding=\"async\" width=\"684\" height=\"154\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-378.png\" alt=\"img\" class=\"wp-image-1523 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-378.png 684w, https:\/\/remoooo.com\/wp-content\/uploads\/image-378-300x68.png 300w\" sizes=\"(max-width: 684px) 100vw, 684px\" \/><\/noscript><\/figure>\n\n\n\n<p>Overload the initialization method and specify Kernel.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>protected override void Init() { center = new Vector4(); kernelName = &quot;Highlight&quot;; base.Init(); }<\/code><\/pre>\n\n\n\n<p>Overload the rendering method. To achieve the effect of focusing on a certain character, you need to pass the coordinate center of the character&#039;s screen space to the Compute Shader. And if the screen resolution changes before Dispatch, reinitialize it.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>protected void SetProperties()\n{\n    float rad = (radius \/ 100.0f) * texSize.y;\n    shader.SetFloat(\"radius\", rad);\n    shader.SetFloat(\"edgeWidth\", rad * softenEdge \/ 100.0f);\n    shader.SetFloat(\"shade\", shade);\n}\n\nprotected override void OnRenderImage(RenderTexture source, RenderTexture destination)\n{\n    if (!init || shader == null)\n    {\n        Graphics.Blit(source, destination);\n    }\n    else\n    {\n        if (trackedObject &amp;&amp; thisCamera)\n        {\n            Vector3 pos = thisCamera.WorldToScreenPoint(trackedObject.position);\n            center.x = pos.x;\n            center.y = pos.y;\n            shader.SetVector(\"center\", center);\n        }\n        bool resChange = false;\n        CheckResolution(out resChange);\n        if (resChange) SetProperties();\n        DispatchWithSource(ref source, ref destination);\n    }\n}<\/code><\/pre>\n\n\n\n<p>And when changing the Inspector panel, you can see the parameter change effect in real time and add the OnValidate() method.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>private void OnValidate()\n{\n    if(!init)\n        Init();\n\n    SetProperties();\n}<\/code><\/pre>\n\n\n\n<p>In GPU, how can we make a circle without shadow inside, with smooth transition at the edge of the circle and shadow outside the transition layer? Based on the method of judging whether a point is inside the circle in the previous article, we can use smoothstep() to process the transition layer.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewbox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" data-code=\"#pragma kernel HighlightTexture2D&lt;float4&gt; source;RWTexture2D&lt;float4&gt; outputrt;float radius;float edgeWidth;float shade;float4 center;float inCircle( float2 pt, float2 center, float radius, float edgeWidth ){    float len = length(pt - center);    return 1.0 - smoothstep(radius-edgeWidth, radius, len);}[numthreads(8, 8, 1)]void Highlight(uint3 id : SV_DispatchThreadID){    float4 srcColor = source[id.xy];    float4 shadedSrcColor = srcColor * shade;    float highlight = inCircle( (float2)id.xy, center.xy, radius, edgeWidth);    float4 color = lerp( shadedSrcColor, srcColor, highlight );    outputrt[id.xy] = color;}\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewbox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #D8DEE9FF\">#<\/span><span style=\"color: #D8DEE9\">Pragmas<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">kernel<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">Highlight<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9\">Texture2D<\/span><span style=\"color: #81A1C1\">&lt;<\/span><span style=\"color: #D8DEE9\">float4<\/span><span style=\"color: #81A1C1\">&gt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">source<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9\">RWTexture2D<\/span><span style=\"color: #81A1C1\">&lt;<\/span><span style=\"color: #D8DEE9\">float4<\/span><span style=\"color: #81A1C1\">&gt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">outputrt<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9\">float<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">radius<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9\">float<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">edgeWidth<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9\">float<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">shade<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9\">float4<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">center<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9\">float<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">inCircle<\/span><span style=\"color: #D8DEE9FF\">( <\/span><span style=\"color: #D8DEE9\">float2<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">pt<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">float2<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">center<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">float<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">radius<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">float<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">edgeWidth<\/span><span style=\"color: #D8DEE9FF\"> )<\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #D8DEE9\">float<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">len<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">length<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">pt<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">center<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">return<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">1.0<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">smoothstep<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">radius<\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #D8DEE9\">edgeWidth<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">radius<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">len<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">[<\/span><span style=\"color: #88C0D0\">numthreads<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #B48EAD\">8<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">8<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">1<\/span><span style=\"color: #D8DEE9FF\">)]<\/span><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">void<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">Highlight<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">uint3<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">id<\/span><span style=\"color: #D8DEE9FF\"> : <\/span><span style=\"color: #D8DEE9\">SV_DispatchThreadID<\/span><span style=\"color: #D8DEE9FF\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #D8DEE9\">float4<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">srcColor<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">source<\/span><span style=\"color: #D8DEE9FF\">[<\/span><span style=\"color: #D8DEE9\">id<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">xy<\/span><span style=\"color: #D8DEE9FF\">]<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #D8DEE9\">float4<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">shadedSrcColor<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">srcColor<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">*<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">shade<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #D8DEE9\">float<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">highlight<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">inCircle<\/span><span style=\"color: #D8DEE9FF\">( (<\/span><span style=\"color: #D8DEE9\">float2<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #D8DEE9\">id<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">xy<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">center<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">xy<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">radius<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">edgeWidth<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #D8DEE9\">float4<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">color<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">lerp<\/span><span style=\"color: #D8DEE9FF\">( <\/span><span style=\"color: #D8DEE9\">shadedSrcColor<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">srcColor<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">highlight<\/span><span style=\"color: #D8DEE9FF\"> )<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #D8DEE9\">outputrt<\/span><span style=\"color: #D8DEE9FF\">[<\/span><span style=\"color: #D8DEE9\">id<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">xy<\/span><span style=\"color: #D8DEE9FF\">] <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">color<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p><\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-377.png\" alt=\"img\" class=\"wp-image-1522 lazyload\"\/><noscript><img decoding=\"async\" width=\"1030\" height=\"480\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-377.png\" alt=\"img\" class=\"wp-image-1522 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-377.png 1030w, https:\/\/remoooo.com\/wp-content\/uploads\/image-377-300x140.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-377-1024x477.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-377-768x358.png 768w\" sizes=\"(max-width: 1030px) 100vw, 1030px\" \/><\/noscript><\/figure>\n\n\n\n<p>Current version code:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Compute Shader: https:\/\/github.com\/Remyuu\/Unity-Compute-Shader-Learn\/blob\/L3_RingHighlight\/Assets\/Shaders\/RingHighlight.compute<\/li>\n\n\n\n<li>CPU: https:\/\/github.com\/Remyuu\/Unity-Compute-Shader-Learn\/blob\/L3_RingHighlight\/Assets\/Scripts\/RingHighlight.cs<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3. Blur effect<\/h3>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-379.png\" alt=\"img\" class=\"wp-image-1524 lazyload\"\/><noscript><img decoding=\"async\" width=\"1106\" height=\"602\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-379.png\" alt=\"img\" class=\"wp-image-1524 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-379.png 1106w, https:\/\/remoooo.com\/wp-content\/uploads\/image-379-300x163.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-379-1024x557.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-379-768x418.png 768w\" sizes=\"(max-width: 1106px) 100vw, 1106px\" \/><\/noscript><\/figure>\n\n\n\n<p>The principle of blur effect is very simple. The final effect can be obtained by taking the weighted average of the n*n pixels around each pixel sample.<\/p>\n\n\n\n<p>But there is an efficiency problem. As we all know, reducing the number of texture sampling is very important for optimization. If each pixel needs to sample 20*20 surrounding pixels, then rendering one pixel requires 400 samplings, which is obviously unacceptable. Moreover, for a single pixel, the operation of sampling a whole rectangular pixel around it is difficult to handle in the Compute Shader. How to solve it?<\/p>\n\n\n\n<p>The usual practice is to sample once horizontally and once vertically. What does this mean? For each pixel, only 20 pixels are sampled in the x direction and 20 pixels in the y direction, a total of 20+20 pixels are sampled, and then weighted average is taken. This method not only reduces the number of samples, but also conforms to the logic of Compute Shader. For horizontal sampling, set a kernel; for vertical sampling, set another kernel.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#pragma kernel HorzPass #pragma kernel Highlight<\/code><\/pre>\n\n\n\n<p>Since Dispatch is executed sequentially, after we calculate the horizontal blur, we use the calculated result to sample vertically again.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>shader.Dispatch(kernelHorzPassID, groupSize.x, groupSize.y, 1); shader.Dispatch(kernelHandle, groupSize.x, groupSize.y, 1);<\/code><\/pre>\n\n\n\n<p>After completing the blur operation, combine it with the RingHighlight in the previous section, and you\u2019re done!<\/p>\n\n\n\n<p>One difference is, after calculating the horizontal blur, how do we pass the result to the next kernel? The answer is obvious: just use the shared keyword. The specific steps are as follows.<\/p>\n\n\n\n<p>Declare a reference to the horizontal blurred texture in the CPU, create a kernel for the horizontal texture, and bind it.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>RenderTexture horzOutput = null;\nint kernelHorzPassID;\n\nprotected override void Init()\n{\n    ...\n    kernelHorzPassID = shader.FindKernel(\"HorzPass\");\n    ...\n}<\/code><\/pre>\n\n\n\n<p>Additional space needs to be allocated in the GPU to store the results of the first kernel.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>protected override void CreateTextures()\n{\n    base.CreateTextures();\n    shader.SetTexture(kernelHorzPassID, \"source\", renderedSource);\n\n    CreateTexture(ref horzOutput);\n\n    shader.SetTexture(kernelHorzPassID, \"horzOutput\", horzOutput);\n    shader.SetTexture(kernelHandle, \"horzOutput\", horzOutput);\n}<\/code><\/pre>\n\n\n\n<p>The GPU is set up like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>shared Texture2D source; shared RWTexture2D horzOutput; RWTexture2D outputrt;<\/code><\/pre>\n\n\n\n<p>Another question is, it seems that it doesn&#039;t matter whether the shared keyword is included or not. In actual testing, different kernels can access it. So what is the point of shared?<\/p>\n\n\n\n<p>In Unity, adding shared before a variable means that this resource is not reinitialized for each call, but keeps its state for use by different shader or dispatch calls. This helps to share data between different shader calls. Marking shared can help the compiler optimize code for higher performance.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-380.png\" alt=\"img\" class=\"wp-image-1525 lazyload\"\/><noscript><img decoding=\"async\" width=\"1440\" height=\"573\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-380.png\" alt=\"img\" class=\"wp-image-1525 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-380.png 1440w, https:\/\/remoooo.com\/wp-content\/uploads\/image-380-300x119.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-380-1024x407.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-380-768x306.png 768w\" sizes=\"(max-width: 1440px) 100vw, 1440px\" \/><\/noscript><\/figure>\n\n\n\n<p>When calculating the pixels at the border, there may be a situation where the number of available pixels is insufficient. Either the remaining pixels on the left are insufficient for blurRadius, or the remaining pixels on the right are insufficient. Therefore, first calculate the safe left index, and then calculate the maximum number that can be taken from left to right.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;numthreads(8, 8, 1)]\nvoid HorzPass(uint3 id : SV_DispatchThreadID)\n{\n    int left = max(0, (int)id.x-blurRadius);\n    int count = min(blurRadius, (int)id.x) + min(blurRadius, source.Length.x - (int)id.x);\n    float4 color = 0;\n\n    uint2 index = uint2((uint)left, id.y);\n\n    &#91;unroll(100)]\n    for(int x=0; x&lt;count; x++){\n        color += source&#91;index];\n        index.x++;\n    }\n\n    color \/= (float)count;\n    horzOutput&#91;id.xy] = color;\n\n}\n\n&#91;numthreads(8, 8, 1)]\nvoid Highlight(uint3 id : SV_DispatchThreadID)\n{\n    \/\/Vert blur\n    int top = max(0, (int)id.y-blurRadius);\n    int count = min(blurRadius, (int)id.y) + min(blurRadius, source.Length.y - (int)id.y);\n    float4 blurColor = 0;\n\n    uint2 index = uint2(id.x, (uint)top);\n\n    &#91;unroll(100)]\n    for(int y=0; y&lt;count; y++){\n        blurColor += horzOutput&#91;index];\n        index.y++;\n    }\n\n    blurColor \/= (float)count;\n\n    float4 srcColor = source&#91;id.xy];\n    float4 shadedBlurColor = blurColor * shade;\n    float highlight = inCircle( (float2)id.xy, center.xy, radius, edgeWidth);\n    float4 color = lerp( shadedBlurColor, srcColor, highlight );\n\n    outputrt&#91;id.xy] = color;\n\n}<\/code><\/pre>\n\n\n\n<p>Current version code:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Compute Shader: https:\/\/github.com\/Remyuu\/Unity-Compute-Shader-Learn\/blob\/L3_BlurEffect\/Assets\/Shaders\/BlurHighlight.compute<\/li>\n\n\n\n<li>CPU: https:\/\/github.com\/Remyuu\/Unity-Compute-Shader-Learn\/blob\/L3_BlurEffect\/Assets\/Scripts\/BlurHighlight.cs<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">4. Gaussian Blur<\/h3>\n\n\n\n<p>The difference from the above is that after sampling, the average value is no longer taken, but a Gaussian function is used to weight it.<\/p>\n\n\n\n<p>Where is the standard deviation, which controls the width.<\/p>\n\n\n\n<p>For more Blur content: https:\/\/www.gamedeveloper.com\/programming\/four-tricks-for-fast-blurring-in-software-and-hardware#close-modal<\/p>\n\n\n\n<p>Since the amount of calculation is not small, it would be very time-consuming to calculate this formula once for each pixel. We use the pre-calculation method to transfer the calculation results to the GPU through the Buffer. Since both kernels need to use it, add a shared when declaring the Buffer.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>float&#91;] SetWeightsArray(int radius, float sigma)\n{\n    int total = radius * 2 + 1;\n    float&#91;] weights = new float&#91;total];\n    float sum = 0.0f;\n\n    for (int n=0; n&lt;radius; n++)\n    {\n        float weight = 0.39894f * Mathf.Exp(-0.5f * n * n \/ (sigma * sigma)) \/ sigma;\n        weights&#91;radius + n] = weight;\n        weights&#91;radius - n] = weight;\n        if (n != 0)\n            sum += weight * 2.0f;\n        else\n            sum += weight;\n    }\n    \/\/ normalize kernels\n    for (int i=0; i&lt;total; i++) weights&#91;i] \/= sum;\n\n    return weights;\n}\n\nprivate void UpdateWeightsBuffer()\n{\n    if (weightsBuffer != null)\n        weightsBuffer.Dispose();\n\n    float sigma = (float)blurRadius \/ 1.5f;\n\n    weightsBuffer = new ComputeBuffer(blurRadius * 2 + 1, sizeof(float));\n    float&#91;] blurWeights = SetWeightsArray(blurRadius, sigma);\n    weightsBuffer.SetData(blurWeights);\n\n    shader.SetBuffer(kernelHorzPassID, \"weights\", weightsBuffer);\n    shader.SetBuffer(kernelHandle, \"weights\", weightsBuffer);\n}<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-384.png\" alt=\"img\" class=\"wp-image-1529 lazyload\"\/><noscript><img decoding=\"async\" width=\"1354\" height=\"912\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-384.png\" alt=\"img\" class=\"wp-image-1529 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-384.png 1354w, https:\/\/remoooo.com\/wp-content\/uploads\/image-384-300x202.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-384-1024x690.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-384-768x517.png 768w\" sizes=\"(max-width: 1354px) 100vw, 1354px\" \/><\/noscript><\/figure>\n\n\n\n<p>Full code:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>https:\/\/pastebin.com\/0qWtUKgy<\/li>\n\n\n\n<li>https:\/\/pastebin.com\/A6mDKyJE<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">5. Low-resolution effects<\/h3>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>GPU: It\u2019s really a refreshing computing experience.<\/p>\n<\/blockquote>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-381.png\" alt=\"img\" class=\"wp-image-1526 lazyload\"\/><noscript><img decoding=\"async\" width=\"854\" height=\"518\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-381.png\" alt=\"img\" class=\"wp-image-1526 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-381.png 854w, https:\/\/remoooo.com\/wp-content\/uploads\/image-381-300x182.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-381-768x466.png 768w\" sizes=\"(max-width: 854px) 100vw, 854px\" \/><\/noscript><\/figure>\n\n\n\n<p>Blur the edges of a high-definition texture without changing the resolution. The implementation method is very simple. For every n*n pixels, only the color of the pixel in the lower left corner is taken. Using the characteristics of integers, the id.x index is divided by n first, and then multiplied by n.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>uint2 index = (uint2(id.x, id.y)\/3) * 3; float3 srcColor = source[index].rgb; float3 finalColor = srcColor;<\/code><\/pre>\n\n\n\n<p>The effect is already there. But the effect is too sharp, so add noise to soften the jagged edges.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>uint2 index = (uint2(id.x, id.y)\/3) * 3;\n\nfloat noise = random(id.xy, time);\nfloat3 srcColor = lerp(source&#91;id.xy].rgb, source&#91;index],noise);\nfloat3 finalColor = srcColor;<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-383.png\" alt=\"img\" class=\"wp-image-1528 lazyload\"\/><noscript><img decoding=\"async\" width=\"1018\" height=\"484\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-383.png\" alt=\"img\" class=\"wp-image-1528 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-383.png 1018w, https:\/\/remoooo.com\/wp-content\/uploads\/image-383-300x143.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-383-768x365.png 768w\" sizes=\"(max-width: 1018px) 100vw, 1018px\" \/><\/noscript><\/figure>\n\n\n\n<p>The pixel of each n*n grid no longer takes the color of the lower left corner, but takes the random interpolation result of the original color and the color of the lower left corner. The effect is much more refined. When n is relatively large, you can also see the following effect. It can only be said that it is not very good-looking, but it can still be explored in some glitch-style roads.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-385.png\" alt=\"img\" class=\"wp-image-1530 lazyload\"\/><noscript><img decoding=\"async\" width=\"1252\" height=\"514\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-385.png\" alt=\"img\" class=\"wp-image-1530 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-385.png 1252w, https:\/\/remoooo.com\/wp-content\/uploads\/image-385-300x123.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-385-1024x420.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-385-768x315.png 768w\" sizes=\"(max-width: 1252px) 100vw, 1252px\" \/><\/noscript><\/figure>\n\n\n\n<p>If you want to get a noisy picture, you can try adding coefficients at both ends of lerp, for example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>float3 srcColor = lerp(source[id.xy].rgb * 2, source[index],noise);<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-386.png\" alt=\"img\" class=\"wp-image-1531 lazyload\"\/><noscript><img decoding=\"async\" width=\"740\" height=\"376\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-386.png\" alt=\"img\" class=\"wp-image-1531 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-386.png 740w, https:\/\/remoooo.com\/wp-content\/uploads\/image-386-300x152.png 300w\" sizes=\"(max-width: 740px) 100vw, 740px\" \/><\/noscript><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">6. Grayscale Effects and Staining<\/h3>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Grayscale Effect &amp; Tinted<\/p>\n<\/blockquote>\n\n\n\n<p>The process of converting a color image to a grayscale image involves converting the RGB value of each pixel into a single color value. This color value is a weighted average of the RGB values. There are two methods here, one is a simple average, and the other is a weighted average that conforms to human eye perception.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Average method (simple but inaccurate):<\/li>\n<\/ol>\n\n\n\n<p>This method gives equal weight to all color channels. 2. Weighted average method (more accurate, reflects human eye perception):<\/p>\n\n\n\n<p>This method gives different weights to different color channels based on the fact that the human eye is more sensitive to green, less sensitive to red, and least sensitive to blue. (The screenshot below doesn&#039;t look very good, I can&#039;t tell lol)<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-388.png\" alt=\"img\" class=\"wp-image-1533 lazyload\"\/><noscript><img decoding=\"async\" width=\"1440\" height=\"570\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-388.png\" alt=\"img\" class=\"wp-image-1533 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-388.png 1440w, https:\/\/remoooo.com\/wp-content\/uploads\/image-388-300x119.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-388-1024x405.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-388-768x304.png 768w\" sizes=\"(max-width: 1440px) 100vw, 1440px\" \/><\/noscript><\/figure>\n\n\n\n<p>After weighting, the colors are simply mixed (multiplied) and finally lerp to obtain a controllable color intensity result.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>uint2 index = (uint2(id.x, id.y)\/6) * 6;\n\nfloat noise = random(id.xy, time);\nfloat3 srcColor = lerp(source&#91;id.xy].rgb, source&#91;index],noise);\n\/\/ float3 finalColor = srcColor;\n\nfloat3 grayScale = (srcColor.r+srcColor.g+srcColor.b)\/3.0;\n\/\/ float3 grayScale = srcColor.r*0.299f+srcColor.g*0.587f+srcColor.b*0.114f;\n\nfloat3 tinted = grayScale * tintColor.rgb;\nfloat3 finalColor = lerp(srcColor, tinted, tintStrength);\n\noutputrt&#91;id.xy] = float4(finalColor, 1);<\/code><\/pre>\n\n\n\n<p>Dye a wasteland color:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-390.png\" alt=\"img\" class=\"wp-image-1535 lazyload\"\/><noscript><img decoding=\"async\" width=\"1258\" height=\"618\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-390.png\" alt=\"img\" class=\"wp-image-1535 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-390.png 1258w, https:\/\/remoooo.com\/wp-content\/uploads\/image-390-300x147.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-390-1024x503.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-390-768x377.png 768w\" sizes=\"(max-width: 1258px) 100vw, 1258px\" \/><\/noscript><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">7. Screen scan line effect<\/h3>\n\n\n\n<p>First, uvY normalizes the coordinates to [0,1].<\/p>\n\n\n\n<p>lines is a parameter that controls the number of scan lines.<\/p>\n\n\n\n<p>Then add a time offset, and the coefficient controls the offset speed. You can open a parameter to control the speed of line offset.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>float uvY = (float)id.y\/(float)source.Length.y; float scanline = saturate(frac(uvY * lines + time * 3));<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-389.png\" alt=\"img\" class=\"wp-image-1534 lazyload\"\/><noscript><img decoding=\"async\" width=\"646\" height=\"318\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-389.png\" alt=\"img\" class=\"wp-image-1534 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-389.png 646w, https:\/\/remoooo.com\/wp-content\/uploads\/image-389-300x148.png 300w\" sizes=\"(max-width: 646px) 100vw, 646px\" \/><\/noscript><\/figure>\n\n\n\n<p>This &quot;line&quot; doesn&#039;t look quite &quot;line&quot; enough, lose some weight.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>float uvY = (float)id.y\/(float)source.Length.y; float scanline = saturate(smoothstep(0.1,0.2,frac(uvY * lines + time * 3)));<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-387.png\" alt=\"img\" class=\"wp-image-1532 lazyload\"\/><noscript><img decoding=\"async\" width=\"786\" height=\"394\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-387.png\" alt=\"img\" class=\"wp-image-1532 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-387.png 786w, https:\/\/remoooo.com\/wp-content\/uploads\/image-387-300x150.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-387-768x385.png 768w\" sizes=\"(max-width: 786px) 100vw, 786px\" \/><\/noscript><\/figure>\n\n\n\n<p>Then lerp the colors.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>float uvY = (float)id.y\/(float)source.Length.y; float scanline = saturate(smoothstep(0.1, 0.2, frac(uvY * lines + time*3)) + 0.3); finalColor = lerp(source [id.xy].rgb*0.5, finalColor, scanline);<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-395.png\" alt=\"img\" class=\"wp-image-1540 lazyload\"\/><noscript><img decoding=\"async\" width=\"1140\" height=\"568\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-395.png\" alt=\"img\" class=\"wp-image-1540 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-395.png 1140w, https:\/\/remoooo.com\/wp-content\/uploads\/image-395-300x149.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-395-1024x510.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-395-768x383.png 768w\" sizes=\"(max-width: 1140px) 100vw, 1140px\" \/><\/noscript><\/figure>\n\n\n\n<p>Before and after \u201cweight loss\u201d, each gets what they need!<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-391.png\" alt=\"img\" class=\"wp-image-1536 lazyload\"\/><noscript><img decoding=\"async\" width=\"1066\" height=\"448\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-391.png\" alt=\"img\" class=\"wp-image-1536 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-391.png 1066w, https:\/\/remoooo.com\/wp-content\/uploads\/image-391-300x126.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-391-1024x430.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-391-768x323.png 768w\" sizes=\"(max-width: 1066px) 100vw, 1066px\" \/><\/noscript><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">8. Night Vision Effect<\/h3>\n\n\n\n<p>This section summarizes all the above content and realizes the effect of a night vision device. First, make a single-eye effect.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>float2 pt = (float2)id.xy; float2 center = (float2)(source.Length &gt;&gt; 1); float inVision = inCircle(pt, center, radius, edgeWidth); float3 blackColor = float3(0,0,0) ; finalColor = lerp(blackColor, finalColor, inVision);<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-394.png\" alt=\"img\" class=\"wp-image-1539 lazyload\"\/><noscript><img decoding=\"async\" width=\"1400\" height=\"666\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-394.png\" alt=\"img\" class=\"wp-image-1539 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-394.png 1400w, https:\/\/remoooo.com\/wp-content\/uploads\/image-394-300x143.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-394-1024x487.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-394-768x365.png 768w\" sizes=\"(max-width: 1400px) 100vw, 1400px\" \/><\/noscript><\/figure>\n\n\n\n<p>The difference between the binocular effect and the binocular effect is that there are two centers of the circle. The two calculated masks can be merged using max() or saturate().<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>float2 pt = (float2)id.xy; float2 centerLeft = float2(source.Length.x \/ 3.0, source.Length.y \/2); float2 centerRight = float2(source.Length.x \/ 3.0 * 2.0, source.Length .y \/2); float inVisionLeft = inCircle(pt, centerLeft, radius, edgeWidth); float inVisionRight = inCircle(pt, centerRight, radius, edgeWidth); float3 blackColor = float3(0,0,0); \/\/ float inVision = max(inVisionLeft, inVisionRight); float inVision = saturate(inVisionLeft + inVisionRight); finalColor = lerp (blackColor, finalColor, inVision);<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-403.png\" alt=\"img\" class=\"wp-image-1548 lazyload\"\/><noscript><img decoding=\"async\" width=\"1432\" height=\"746\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-403.png\" alt=\"img\" class=\"wp-image-1548 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-403.png 1432w, https:\/\/remoooo.com\/wp-content\/uploads\/image-403-300x156.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-403-1024x533.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-403-768x400.png 768w\" sizes=\"(max-width: 1432px) 100vw, 1432px\" \/><\/noscript><\/figure>\n\n\n\n<p>Current version code:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Compute Shader: https:\/\/github.com\/Remyuu\/Unity-Compute-Shader-Learn\/blob\/L3_NightVision\/Assets\/Shaders\/NightVision.compute<\/li>\n\n\n\n<li>CPU: https:\/\/github.com\/Remyuu\/Unity-Compute-Shader-Learn\/blob\/L3_NightVision\/Assets\/Scripts\/NightVision.cs<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">9. Smooth transition lines<\/h3>\n\n\n\n<p>Think about how we should draw a smooth straight line on the screen.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-393.png\" alt=\"img\" class=\"wp-image-1538 lazyload\"\/><noscript><img decoding=\"async\" width=\"776\" height=\"224\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-393.png\" alt=\"img\" class=\"wp-image-1538 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-393.png 776w, https:\/\/remoooo.com\/wp-content\/uploads\/image-393-300x87.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-393-768x222.png 768w\" sizes=\"(max-width: 776px) 100vw, 776px\" \/><\/noscript><\/figure>\n\n\n\n<p>The smoothstep() function can do this. Readers familiar with this function can skip this section. This function is used to create a smooth gradient. The smoothstep(edge0, edge1, x) function outputs a gradient from 0 to 1 when x is between edge0 and edge1. If x &lt; edge0, it returns 0; if x &gt; edge1, it returns 1. Its output value is calculated based on Hermite interpolation:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-392.png\" alt=\"img\" class=\"wp-image-1537 lazyload\"\/><noscript><img decoding=\"async\" width=\"1440\" height=\"764\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-392.png\" alt=\"img\" class=\"wp-image-1537 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-392.png 1440w, https:\/\/remoooo.com\/wp-content\/uploads\/image-392-300x159.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-392-1024x543.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-392-768x407.png 768w\" sizes=\"(max-width: 1440px) 100vw, 1440px\" \/><\/noscript><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>float onLine(float position, float center, float lineWidth, float edgeWidth) {\n    float halfWidth = lineWidth \/ 2.0;\n    float edge0 = center - halfWidth - edgeWidth;\n    float edge1 = center - halfWidth;\n    float edge2 = center + halfWidth;\n    float edge3 = center + halfWidth + edgeWidth;\n\n    return smoothstep(edge0, edge1, position) - smoothstep(edge2, edge3, position);\n}<\/code><\/pre>\n\n\n\n<p>In the above code, the parameters passed in have been normalized to [0,1]. position is the position of the point under investigation, center is the center of the line, lineWidth is the actual width of the line, and edgeWidth is the width of the edge, which is used for smooth transition. I am really unhappy with my ability to express myself! As for how to calculate it, I will draw a picture for you to understand!<\/p>\n\n\n\n<p>It&#039;s probably:,,.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-396.png\" alt=\"img\" class=\"wp-image-1541 lazyload\"\/><noscript><img decoding=\"async\" width=\"1108\" height=\"434\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-396.png\" alt=\"img\" class=\"wp-image-1541 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-396.png 1108w, https:\/\/remoooo.com\/wp-content\/uploads\/image-396-300x118.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-396-1024x401.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-396-768x301.png 768w\" sizes=\"(max-width: 1108px) 100vw, 1108px\" \/><\/noscript><\/figure>\n\n\n\n<p>Think about how to draw a circle with a smooth transition.<\/p>\n\n\n\n<p>For each point, first calculate the distance vector to the center of the circle and return the result to position, and then calculate its length and return it to len.<\/p>\n\n\n\n<p>Imitating the difference method of the above two smoothsteps, a ring line effect is generated by subtracting the outer edge interpolation result.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>float circle(float2 position, float2 center, float radius, float lineWidth, float edgeWidth){\n    position -= center;\n    float len = length(position);\n    \/\/Change true to false to soften the edge\n    float result = smoothstep(radius - lineWidth \/ 2.0 - edgeWidth, radius - lineWidth \/ 2.0, len) - smoothstep(radius + lineWidth \/ 2.0, radius + lineWidth \/ 2.0 + edgeWidth, len);\n\n    return result;\n}<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-402.png\" alt=\"img\" class=\"wp-image-1547 lazyload\"\/><noscript><img decoding=\"async\" width=\"942\" height=\"574\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-402.png\" alt=\"img\" class=\"wp-image-1547 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-402.png 942w, https:\/\/remoooo.com\/wp-content\/uploads\/image-402-300x183.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-402-768x468.png 768w\" sizes=\"(max-width: 942px) 100vw, 942px\" \/><\/noscript><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">10. Scanline Effect<\/h3>\n\n\n\n<p>Then add a horizontal line, a vertical line, and a few circles to create a radar scanning effect.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>float3 color = float3(0.0f,0.0f,0.0f); color += onLine(uv.y, center.y, 0.002, 0.001) * axisColor.rgb;\/\/xAxis color += onLine(uv.x, center .x, 0.002, 0.001) * axisColor.rgb;\/\/yAxis color += circle(uv, center, 0.2f, 0.002, 0.001) * axisColor.rgb; color += circle(uv, center, 0.3f, 0.002, 0.001) * axisColor.rgb; color += circle(uv, center, 0.4f , 0.002, 0.001) * axisColor.rgb;<\/code><\/pre>\n\n\n\n<p>Draw another scan line with a trajectory.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>float sweep(float2 position, float2 center, float radius, float lineWidth, float edgeWidth) {\n    float2 direction = position - center;\n    float theta = time + 6.3;\n    float2 circlePoint = float2(cos(theta), -sin(theta)) * radius;\n    float projection = clamp(dot(direction, circlePoint) \/ dot(circlePoint, circlePoint), 0.0, 1.0);\n    float lineDistance = length(direction - circlePoint * projection);\n\n    float gradient = 0.0;\n    const float maxGradientAngle = PI * 0.5;\n\n    if (length(direction) &lt; radius) {\n        float angle = fmod(theta + atan2(direction.y, direction.x), PI2);\n        gradient = clamp(maxGradientAngle - angle, 0.0, maxGradientAngle) \/ maxGradientAngle * 0.5;\n    }\n\n    return gradient + 1.0 - smoothstep(lineWidth, lineWidth + edgeWidth, lineDistance);\n}<\/code><\/pre>\n\n\n\n<p>Add to the color.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>... color += sweep(uv, center, 0.45f, 0.003, 0.001) * sweepColor.rgb; ...<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-399.png\" alt=\"img\" class=\"wp-image-1544 lazyload\"\/><noscript><img decoding=\"async\" width=\"1098\" height=\"546\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-399.png\" alt=\"img\" class=\"wp-image-1544 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-399.png 1098w, https:\/\/remoooo.com\/wp-content\/uploads\/image-399-300x149.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-399-1024x509.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-399-768x382.png 768w\" sizes=\"(max-width: 1098px) 100vw, 1098px\" \/><\/noscript><\/figure>\n\n\n\n<p>Current version code:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Compute Shader: https:\/\/github.com\/Remyuu\/Unity-Compute-Shader-Learn\/blob\/L3_HUDOverlay\/Assets\/Shaders\/HUDOverlay.compute<\/li>\n\n\n\n<li>CPU: https:\/\/github.com\/Remyuu\/Unity-Compute-Shader-Learn\/blob\/L3_HUDOverlay\/Assets\/Scripts\/HUDOverlay.cs<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">11. Gradient background shadow effect<\/h3>\n\n\n\n<p>This effect can be used in subtitles or some explanatory text. Although you can directly add a texture to the UI Canvas, using Compute Shader can achieve more flexible effects and resource optimization.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-397.png\" alt=\"img\" class=\"wp-image-1542 lazyload\"\/><noscript><img decoding=\"async\" width=\"936\" height=\"354\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-397.png\" alt=\"img\" class=\"wp-image-1542 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-397.png 936w, https:\/\/remoooo.com\/wp-content\/uploads\/image-397-300x113.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-397-768x290.png 768w\" sizes=\"(max-width: 936px) 100vw, 936px\" \/><\/noscript><\/figure>\n\n\n\n<p>The background of subtitles and dialogue text is usually at the bottom of the screen, and the top is not processed. At the same time, a higher contrast is required, so the original picture is grayed out and a shadow is specified.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if (id.y&lt;(uint)tintHeight){ float3 grayScale = (srcColor.r + srcColor.g + srcColor.b) * 0.33 * tintColor.rgb; float3 shaded = lerp(srcColor.rgb, grayScale, tintStrength) * shade ; ... \/\/ Continue}else{ color = srcColor; }<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-404.png\" alt=\"img\" class=\"wp-image-1549 lazyload\"\/><noscript><img decoding=\"async\" width=\"1440\" height=\"621\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-404.png\" alt=\"img\" class=\"wp-image-1549 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-404.png 1440w, https:\/\/remoooo.com\/wp-content\/uploads\/image-404-300x129.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-404-1024x442.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-404-768x331.png 768w\" sizes=\"(max-width: 1440px) 100vw, 1440px\" \/><\/noscript><\/figure>\n\n\n\n<p>Gradient effect.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>...\/\/ Continue from the previous text float srcAmount = smoothstep(tintHeight-edgeWidth, (float)tintHeight, (float)id.y); ...\/\/ Continue from the following text<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-401.png\" alt=\"img\" class=\"wp-image-1546 lazyload\"\/><noscript><img decoding=\"async\" width=\"1440\" height=\"622\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-401.png\" alt=\"img\" class=\"wp-image-1546 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-401.png 1440w, https:\/\/remoooo.com\/wp-content\/uploads\/image-401-300x130.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-401-1024x442.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-401-768x332.png 768w\" sizes=\"(max-width: 1440px) 100vw, 1440px\" \/><\/noscript><\/figure>\n\n\n\n<p>Finally, lerp it up again.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>...\/\/ Continue from the previous text color = lerp(float4(shaded, 1), srcColor, srcAmount);<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-411.png\" alt=\"img\" class=\"wp-image-1556 lazyload\"\/><noscript><img decoding=\"async\" width=\"1420\" height=\"668\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-411.png\" alt=\"img\" class=\"wp-image-1556 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-411.png 1420w, https:\/\/remoooo.com\/wp-content\/uploads\/image-411-300x141.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-411-1024x482.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-411-768x361.png 768w\" sizes=\"(max-width: 1420px) 100vw, 1420px\" \/><\/noscript><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">12. Summary\/Quiz<\/h3>\n\n\n\n<p>If id.xy = [ 100, 30 ]. What would be the return value of inCircle((float2)id.xy, float2(130, 40), 40, 0.1)<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-400.png\" alt=\"img\" class=\"wp-image-1545 lazyload\"\/><noscript><img decoding=\"async\" width=\"1440\" height=\"377\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-400.png\" alt=\"img\" class=\"wp-image-1545 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-400.png 1440w, https:\/\/remoooo.com\/wp-content\/uploads\/image-400-300x79.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-400-1024x268.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-400-768x201.png 768w\" sizes=\"(max-width: 1440px) 100vw, 1440px\" \/><\/noscript><\/figure>\n\n\n\n<p>When creating a blur effect which answer describes our approach best?<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-398.png\" alt=\"img\" class=\"wp-image-1543 lazyload\"\/><noscript><img decoding=\"async\" width=\"1440\" height=\"373\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-398.png\" alt=\"img\" class=\"wp-image-1543 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-398.png 1440w, https:\/\/remoooo.com\/wp-content\/uploads\/image-398-300x78.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-398-1024x265.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-398-768x199.png 768w\" sizes=\"(max-width: 1440px) 100vw, 1440px\" \/><\/noscript><\/figure>\n\n\n\n<p>Which answer would create a blocky low resolution version of the source image?<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-405.png\" alt=\"img\" class=\"wp-image-1550 lazyload\"\/><noscript><img decoding=\"async\" width=\"1440\" height=\"446\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-405.png\" alt=\"img\" class=\"wp-image-1550 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-405.png 1440w, https:\/\/remoooo.com\/wp-content\/uploads\/image-405-300x93.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-405-1024x317.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-405-768x238.png 768w\" sizes=\"(max-width: 1440px) 100vw, 1440px\" \/><\/noscript><\/figure>\n\n\n\n<p>What is smoothstep(5, 10, 6); ?<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-406.png\" alt=\"img\" class=\"wp-image-1551 lazyload\"\/><noscript><img decoding=\"async\" width=\"1440\" height=\"365\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-406.png\" alt=\"img\" class=\"wp-image-1551 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-406.png 1440w, https:\/\/remoooo.com\/wp-content\/uploads\/image-406-300x76.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-406-1024x260.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-406-768x195.png 768w\" sizes=\"(max-width: 1440px) 100vw, 1440px\" \/><\/noscript><\/figure>\n\n\n\n<p>If an and b are both vectors. Which answer best describes dot(a,b)\/dot(b,b); ?<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-407.png\" alt=\"img\" class=\"wp-image-1552 lazyload\"\/><noscript><img decoding=\"async\" width=\"1440\" height=\"372\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-407.png\" alt=\"img\" class=\"wp-image-1552 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-407.png 1440w, https:\/\/remoooo.com\/wp-content\/uploads\/image-407-300x78.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-407-1024x265.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-407-768x198.png 768w\" sizes=\"(max-width: 1440px) 100vw, 1440px\" \/><\/noscript><\/figure>\n\n\n\n<p>What is _MainTex_TexelSize.x? If _MainTex is 512 x 256 pixel resolution.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-408.png\" alt=\"img\" class=\"wp-image-1553 lazyload\"\/><noscript><img decoding=\"async\" width=\"1440\" height=\"366\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-408.png\" alt=\"img\" class=\"wp-image-1553 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-408.png 1440w, https:\/\/remoooo.com\/wp-content\/uploads\/image-408-300x76.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-408-1024x260.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-408-768x195.png 768w\" sizes=\"(max-width: 1440px) 100vw, 1440px\" \/><\/noscript><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">13. Use Blit and Material for post-processing<\/h3>\n\n\n\n<p>In addition to using Compute Shader for post-processing, there is another simple method.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ .cs Graphics.Blit(source, dest, material, passIndex); \/\/ .shader Pass{ CGPROGRAM #pragma vertex vert_img #pragma fragment frag fixed4 frag(v2f_img input) : SV_Target{ return tex2D(_MainTex, input.uv); } ENDCG }<\/code><\/pre>\n\n\n\n<p>Image data is processed by combining Shader.<\/p>\n\n\n\n<p>So the question is, what is the difference between the two? And isn&#039;t the input a texture? Where do the vertices come from?<\/p>\n\n\n\n<p>answer:<\/p>\n\n\n\n<p>The first question. This method is called &quot;screen space shading&quot; and is fully integrated into Unity&#039;s graphics pipeline. Its performance is actually higher than Compute Shader. Compute Shader provides finer-grained control over GPU resources. It is not restricted by the graphics pipeline and can directly access and modify resources such as textures and buffers.<\/p>\n\n\n\n<p>The second question. Pay attention to vert_img. In UnityCG, you can find the following definition:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-409.png\" alt=\"img\" class=\"wp-image-1554 lazyload\"\/><noscript><img decoding=\"async\" width=\"1218\" height=\"436\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-409.png\" alt=\"img\" class=\"wp-image-1554 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-409.png 1218w, https:\/\/remoooo.com\/wp-content\/uploads\/image-409-300x107.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-409-1024x367.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-409-768x275.png 768w\" sizes=\"(max-width: 1218px) 100vw, 1218px\" \/><\/noscript><\/figure>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAIAAAAAAAP\/\/\/yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" data-src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-410.png\" alt=\"img\" class=\"wp-image-1555 lazyload\"\/><noscript><img decoding=\"async\" width=\"1440\" height=\"517\" src=\"https:\/\/\u80a5\u80a5.com\/wp-content\/uploads\/image-410.png\" alt=\"img\" class=\"wp-image-1555 lazyload\" srcset=\"https:\/\/remoooo.com\/wp-content\/uploads\/image-410.png 1440w, https:\/\/remoooo.com\/wp-content\/uploads\/image-410-300x108.png 300w, https:\/\/remoooo.com\/wp-content\/uploads\/image-410-1024x368.png 1024w, https:\/\/remoooo.com\/wp-content\/uploads\/image-410-768x276.png 768w\" sizes=\"(max-width: 1440px) 100vw, 1440px\" \/><\/noscript><\/figure>\n\n\n\n<p>Unity will automatically convert the incoming texture into two triangles (a rectangle that fills the screen). When we write post-processing using the material method, we can just write it directly on the frag.<\/p>\n\n\n\n<p>In the next chapter, you will learn how to connect Material, Shader, Compute Shader and C#.<\/p>","protected":false},"excerpt":{"rendered":"<p>\u524d\u8a00 \u521d\u6b65\u8ba4\u8bc6\u4e86Compute Shader\uff0c\u5b9e\u73b0\u4e00\u4e9b\u7b80\u5355\u7684\u6548\u679c\u3002\u6240\u6709\u7684\u4ee3\u7801\u90fd\u5728\uff1a https:\/\/githu [&hellip;]<\/p>","protected":false},"author":1,"featured_media":1519,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[53],"tags":[74,37],"class_list":["post-1513","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tech","tag-compute-shader","tag-unity"],"_links":{"self":[{"href":"https:\/\/remoooo.com\/en\/wp-json\/wp\/v2\/posts\/1513","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/remoooo.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/remoooo.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/remoooo.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/remoooo.com\/en\/wp-json\/wp\/v2\/comments?post=1513"}],"version-history":[{"count":1,"href":"https:\/\/remoooo.com\/en\/wp-json\/wp\/v2\/posts\/1513\/revisions"}],"predecessor-version":[{"id":1557,"href":"https:\/\/remoooo.com\/en\/wp-json\/wp\/v2\/posts\/1513\/revisions\/1557"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/remoooo.com\/en\/wp-json\/wp\/v2\/media\/1519"}],"wp:attachment":[{"href":"https:\/\/remoooo.com\/en\/wp-json\/wp\/v2\/media?parent=1513"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/remoooo.com\/en\/wp-json\/wp\/v2\/categories?post=1513"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/remoooo.com\/en\/wp-json\/wp\/v2\/tags?post=1513"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}