<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Daniel Chou Rainho</title>
    <description>The latest articles on DEV Community by Daniel Chou Rainho (@alpenglow).</description>
    <link>https://dev.to/alpenglow</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1088103%2F1a59e6ac-f9ce-4587-b6e6-fef523200c91.jpeg</url>
      <title>DEV Community: Daniel Chou Rainho</title>
      <link>https://dev.to/alpenglow</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alpenglow"/>
    <language>en</language>
    <item>
      <title>[Unity] Loading GLTF Models at Runtime for Android</title>
      <dc:creator>Daniel Chou Rainho</dc:creator>
      <pubDate>Wed, 19 Jul 2023 10:24:59 +0000</pubDate>
      <link>https://dev.to/alpenglow/unity-loading-gltf-models-at-runtime-for-android-133n</link>
      <guid>https://dev.to/alpenglow/unity-loading-gltf-models-at-runtime-for-android-133n</guid>
      <description>&lt;p&gt;The &lt;strong&gt;goal&lt;/strong&gt; of this project is to load &lt;code&gt;.gltf&lt;/code&gt; files at runtime in Unity for Android, with specific focus on the Oculus Quest. This can be challenging due to differences in rendering pipelines and limited native GLTF support in Unity.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;While Unity provides support for common 3D formats like &lt;code&gt;.fbx&lt;/code&gt;, &lt;code&gt;.obj&lt;/code&gt;, and &lt;code&gt;.dae&lt;/code&gt;, it lacks native support for GLTF files.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Contenders: UnityGLTF, GLTFUtility, and UniGLTF
&lt;/h2&gt;

&lt;p&gt;On reviewing the available options, three libraries emerged as the front runners: &lt;a href="https://github.com/KhronosGroup/UnityGLTF" rel="noopener noreferrer"&gt;UnityGLTF&lt;/a&gt;, &lt;a href="https://github.com/Siccity/GLTFUtility" rel="noopener noreferrer"&gt;GLTFUtility&lt;/a&gt;, and &lt;a href="https://github.com/ousttrue/UniGLTF" rel="noopener noreferrer"&gt;UniGLTF&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Selection
&lt;/h2&gt;

&lt;p&gt;Taking into account all three options, I opted for &lt;strong&gt;GLTFUtility&lt;/strong&gt; for this project. The native URP support is critical for the seamless loading of GLTF models on the Oculus Quest platform without any shader issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UnityGLTF&lt;/strong&gt;, despite its strong feature set, was less suitable due to its lack of URP support, requiring additional workarounds or library extensions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The key differences lie in their complexity and features. While UnityGLTF boasts more features, it seems like an extremely messy project, and having to compile a DLL for it to work makes it inaccessible to most of the people who actually need it.&lt;br&gt;
GLTFUtility is built with simplicity as the main focus, uses no external libraries, and is easy to navigate for potential contributors. It's plug and play. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://forum.unity.com/threads/gltfutility-a-simple-gltf-plugin.654319/" rel="noopener noreferrer"&gt;Thor Brigsted&lt;/a&gt;, creator of GLTFUtility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;UniGLTF&lt;/strong&gt; was a close competitor and offered much of what &lt;strong&gt;GLTFUtility&lt;/strong&gt; provided, including URP support. However, I leaned towards &lt;strong&gt;GLTFUtility&lt;/strong&gt;, as it is also designed with a focus on simplicity and performance which could prove beneficial when loading models at runtime on a device like the Oculus Quest 2.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;Getting GLTFUtility up and running was quite straightforward. After adding it to my Unity project, I set up a basic script to load a &lt;code&gt;.gltf&lt;/code&gt; file into my scene.&lt;/p&gt;

&lt;p&gt;The GLTFUtility package also provides an asynchronous loading option which is useful for loading complex 3D models without freezing the main thread.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Siccity.GLTFUtility&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.IO&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GLTFLoader&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;relativeFilePath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"YourFile.gltf"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;absoluteFilePath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;streamingAssetsPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;relativeFilePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;StartCoroutine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;LoadGLTF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;absoluteFilePath&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;IEnumerator&lt;/span&gt; &lt;span class="nf"&gt;LoadGLTF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;GameObject&lt;/span&gt; &lt;span class="n"&gt;loadedObject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;Importer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ImportGLTFAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ImportSettings&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;animations&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;loadedObject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loadedObject&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;loadedObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetParent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;loadedObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;localPosition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Vector3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zero&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: For this project, place your &lt;code&gt;.gltf&lt;/code&gt; file in the &lt;code&gt;StreamingAssets&lt;/code&gt; folder, which is located at &lt;code&gt;YourUnityProject/Assets/StreamingAssets&lt;/code&gt;. Ensure the &lt;code&gt;relativeFilePath&lt;/code&gt; variable in the script matches your &lt;code&gt;.gltf&lt;/code&gt; file's name.&lt;/p&gt;

&lt;p&gt;The source code for this project can be found on &lt;a href="https://github.com/CR-Daniel/GLTFUtility-example-use" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>[Unity] GameObject to Shader Communication: MaterialPropertyBlocks</title>
      <dc:creator>Daniel Chou Rainho</dc:creator>
      <pubDate>Sun, 18 Jun 2023 17:56:01 +0000</pubDate>
      <link>https://dev.to/alpenglow/unity-gameobject-to-shader-communication-bp4</link>
      <guid>https://dev.to/alpenglow/unity-gameobject-to-shader-communication-bp4</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Each &lt;em&gt;GameObject&lt;/em&gt; in my scene is given a unique ID, and I want this ID value to be used inside the &lt;em&gt;Shader&lt;/em&gt; that is part of the &lt;em&gt;Material&lt;/em&gt; of every &lt;em&gt;GameObject&lt;/em&gt; in my scene.&lt;/p&gt;

&lt;p&gt;Specifically, I want this identifier to be able to be stored in a &lt;em&gt;Color&lt;/em&gt; channel, i.e. it has to be a float between 0 and 1 that is unique to every &lt;em&gt;GameObject&lt;/em&gt; in the scene.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;In the following 3 sections I outline the 3 main scripts I used for this post.&lt;/p&gt;

&lt;p&gt;They interact as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;My &lt;em&gt;Identification&lt;/em&gt; script is attached to each &lt;em&gt;GameObject&lt;/em&gt; in the scene that I want to attribute to an unique ID.&lt;/li&gt;
&lt;li&gt;This &lt;em&gt;Identification&lt;/em&gt; script calls a function from the script &lt;em&gt;IDManager&lt;/em&gt; (which I have attached to another &lt;em&gt;GameObject&lt;/em&gt; in my scence) to get a unique ID.&lt;/li&gt;
&lt;li&gt;Then, the &lt;em&gt;Identification&lt;/em&gt; script creates a &lt;em&gt;MaterialPropertyBlock&lt;/em&gt; object to which it assigns the ID.&lt;/li&gt;
&lt;li&gt;Finally, in my &lt;em&gt;Shader&lt;/em&gt; which I assign to the material associated with a custom &lt;em&gt;Render Objects Render Feature&lt;/em&gt;, I can read this ID value from the &lt;em&gt;MaterialPropertyBlock&lt;/em&gt; to then return it in the red color channel in the fragment shader.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  IDManager
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections.Generic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
* The first id starts at 0.0001.
* We don't use 0 because that's reserved for the background.
*/&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IDManager&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IDManager&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Singleton instance&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;lastAssignedID&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Keep track of last assigned ID&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;totalObjects&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Total number of objects you expect&lt;/span&gt;

    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Awake&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Singleton setup&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nf"&gt;DontDestroyOnLoad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Ensure the manager persists between scenes&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;Destroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="nf"&gt;GetUniqueID&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;lastAssignedID&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;lastAssignedID&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;totalObjects&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Normalize the ID&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Identification
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections.Generic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Identification&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Renderer&lt;/span&gt; &lt;span class="n"&gt;_renderer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;MaterialPropertyBlock&lt;/span&gt; &lt;span class="n"&gt;_propBlock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Awake&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;IDManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetUniqueID&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;_propBlock&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MaterialPropertyBlock&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;_renderer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Renderer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// TEMP&lt;/span&gt;
        &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ID: "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;_renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetPropertyBlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_propBlock&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_propBlock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"_ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetPropertyBlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_propBlock&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  URP Shader
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Shader "Custom/UniqueColor2"
{
    Properties
    {
        _ID ("ID", Range(0, 1)) = 0.01
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        HLSLINCLUDE

        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

        CBUFFER_START(UnityPerMaterial)
            float _ID;
        CBUFFER_END

        struct VertexInput
        {
            float4 position : POSITION;
        };

        struct VertexOutput
        {
            float4 position : SV_POSITION;
        };

        ENDHLSL

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            VertexInput vert(VertexInput v)
            {
                VertexInput o;
                o.position = TransformObjectToHClip(v.position.xyz);
                return o;
            }

            float4 frag(VertexOutput i) : SV_Target
            {
                return float4(_ID, 0, 0, 1);
            }

            ENDHLSL
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>[Unity] Encoding Data into Pixels (Part 2): Precision Considerations</title>
      <dc:creator>Daniel Chou Rainho</dc:creator>
      <pubDate>Thu, 08 Jun 2023 09:09:46 +0000</pubDate>
      <link>https://dev.to/alpenglow/unity-encoding-data-into-pixels-part-2-precision-2bjo</link>
      <guid>https://dev.to/alpenglow/unity-encoding-data-into-pixels-part-2-precision-2bjo</guid>
      <description>&lt;p&gt;32 bits.&lt;br&gt;
That's how much data can be stored in a &lt;code&gt;float&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In my previous post I noted the following:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In a 32-bit system, each bit can represent two possible values: 0 or 1. Since there are 32 bits in total, we can calculate the number of possible values by raising 2 to the power of 32: 2^32 = 4,294,967,296.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, in our case, we are only considering floats between 0 and 1.&lt;/p&gt;

&lt;p&gt;So the question becomes: &lt;strong&gt;How many numbers can you represent in a &lt;code&gt;float&lt;/code&gt; between 0 and 1?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Theory
&lt;/h2&gt;

&lt;p&gt;The total number of representable numbers between 0 and 1 in a 32-bit floating point format is a bit tricky to answer directly because the format doesn't distribute precision evenly across all possible values.&lt;/p&gt;

&lt;p&gt;A 32-bit floating-point number follows the IEEE 754 standard for floating-point arithmetic. The standard specifies that a &lt;code&gt;float&lt;/code&gt; is composed of 1 sign bit, 8 exponent bits, and 23 fraction (or significand) bits.&lt;/p&gt;

&lt;p&gt;The significand provides the precision of the number. With 23 bits for the fraction part, we have 2^23, or about &lt;strong&gt;8.39 million&lt;/strong&gt;, unique values. But this doesn't mean that we can represent exactly 8.39 million unique values between 0 and 1.&lt;/p&gt;

&lt;p&gt;The reason for this is because floating-point numbers distribute more precision close to 0 and less precision as values increase. This property allows them to represent very large numbers and very small numbers at the cost of precision.&lt;/p&gt;

&lt;p&gt;So while there are roughly 8.39 million unique fractional parts, they are not evenly distributed between 0 and 1. Instead, there are far more representable numbers close to 0 and gradually fewer as you get closer to 1.&lt;/p&gt;

&lt;p&gt;A precise count of the total representable numbers between 0 and 1 is not straightforward due to this varying precision, but know that there are far more than just 8.39 million due to the ability of the exponent to shift the location of the decimal point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;Let's look at a simplified example using binary fractions, which are analogous to the fraction part of floating-point numbers.&lt;/p&gt;

&lt;p&gt;In binary, a fraction is represented the same way as in decimal: each digit (bit) after the binary point represents a negative power of 2. So, the first bit after the binary point is 1/2 (2^-1), the second bit is 1/4 (2^-2), the third bit is 1/8 (2^-3), and so on.&lt;/p&gt;

&lt;p&gt;If we only have 3 bits to represent fractions (as opposed to 23 in a real float), we can only represent 8 (2^3) unique fractions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;000 in binary -&amp;gt; 0 in decimal&lt;/li&gt;
&lt;li&gt;001 in binary -&amp;gt; 0.125 in decimal (1/8)&lt;/li&gt;
&lt;li&gt;010 in binary -&amp;gt; 0.25 in decimal (1/4)&lt;/li&gt;
&lt;li&gt;011 in binary -&amp;gt; 0.375 in decimal (1/4 + 1/8)&lt;/li&gt;
&lt;li&gt;100 in binary -&amp;gt; 0.5 in decimal (1/2)&lt;/li&gt;
&lt;li&gt;101 in binary -&amp;gt; 0.625 in decimal (1/2 + 1/8)&lt;/li&gt;
&lt;li&gt;110 in binary -&amp;gt; 0.75 in decimal (1/2 + 1/4)&lt;/li&gt;
&lt;li&gt;111 in binary -&amp;gt; 0.875 in decimal (1/2 + 1/4 + 1/8)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, if you look at the range between 0 and 1, you will notice that these fractions are not evenly distributed. There are more representable numbers closer to 0 and fewer as you approach 1.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>[Unity] Encoding Data into Pixels (Part 1): CG Shaders</title>
      <dc:creator>Daniel Chou Rainho</dc:creator>
      <pubDate>Sun, 04 Jun 2023 16:32:38 +0000</pubDate>
      <link>https://dev.to/alpenglow/unity-encoding-data-into-pixels-hlsl-shaders-50a7</link>
      <guid>https://dev.to/alpenglow/unity-encoding-data-into-pixels-hlsl-shaders-50a7</guid>
      <description>&lt;p&gt;For a project, I want to implement a feature which allows the picking of gameobjects by color. I was first introduced to this technique &lt;a href="http://www.opengl-tutorial.org/miscellaneous/clicking-on-objects/picking-with-an-opengl-hack/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I want pixel perfect object selection, and this approach is relatively straightforward. All it involves is painting each object in a distinct color, this color being a unique ID associated to each object. Then, we can simply see what color, and thus what object the player is pointing at.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ideation
&lt;/h2&gt;

&lt;p&gt;We create a new &lt;em&gt;Universal Renderer Data&lt;/em&gt; since we want to color each gameobject in a different color, but don't want this effect to be visible on the main camera. We want this effect to be exclusive to a secondary camera which we will use only for the color picking action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Locate the &lt;em&gt;Universal Render Pipeline Asset&lt;/em&gt; used in your project. (To identify this one you can open &lt;em&gt;Project Settings&lt;/em&gt; and then navigate to the &lt;em&gt;Graphics&lt;/em&gt; section.)&lt;/li&gt;
&lt;li&gt;Add a new field to its &lt;em&gt;Renderer List&lt;/em&gt; under the &lt;em&gt;Rendering&lt;/em&gt; section.&lt;/li&gt;
&lt;li&gt;Create a new &lt;em&gt;Universal Renderer Data&lt;/em&gt; asset.
(Create -&amp;gt; Rendering -&amp;gt; URP Universal Renderer)&lt;/li&gt;
&lt;li&gt;See its details in the &lt;em&gt;Inspector&lt;/em&gt;, and then click on &lt;em&gt;Add Render Feature&lt;/em&gt;, selecting the option &lt;em&gt;Render Objects (Experimental)&lt;/em&gt;. This one in particular allows us to apply the same material to every gameobject in our scene.&lt;/li&gt;
&lt;li&gt;Inside the section for the newly created &lt;em&gt;Render Feature&lt;/em&gt;, under the &lt;em&gt;Filters&lt;/em&gt; section, select the &lt;em&gt;Everything&lt;/em&gt; option for the &lt;em&gt;Layer Mask&lt;/em&gt; field. In this case, I will want to paint every single &lt;em&gt;GameObject&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Inside the section for the newly created &lt;em&gt;Render Feature&lt;/em&gt;, click on &lt;em&gt;Overrides&lt;/em&gt;. A &lt;em&gt;Material&lt;/em&gt; field should be visible.&lt;/li&gt;
&lt;li&gt;Create a new &lt;em&gt;Material&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Create a new &lt;em&gt;Shader&lt;/em&gt;, we will use this one later to color each gameobject in a unique color.&lt;/li&gt;
&lt;li&gt;Assign the &lt;em&gt;Shader&lt;/em&gt; to the newly created &lt;em&gt;Material&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Assign the newly created &lt;em&gt;Material&lt;/em&gt; to the &lt;em&gt;Material&lt;/em&gt; field in the &lt;em&gt;Render Feature&lt;/em&gt; section.&lt;/li&gt;
&lt;li&gt;Assign the &lt;em&gt;Universal Renderer Data&lt;/em&gt; to the previously created &lt;em&gt;Renderer List&lt;/em&gt; field, created in step 2.&lt;/li&gt;
&lt;li&gt;Inspect your secondary camera.&lt;/li&gt;
&lt;li&gt;Select the newly created &lt;em&gt;Renderer&lt;/em&gt; from the &lt;em&gt;Renderer&lt;/em&gt; field in the &lt;em&gt;Rendering&lt;/em&gt; section.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Hello World Shader
&lt;/h2&gt;

&lt;p&gt;If you have followed along the steps above, recreating the &lt;em&gt;Renderer&lt;/em&gt;, I have added this small section that tests if you did the setup correctly.&lt;/p&gt;

&lt;p&gt;To test the correctness of the setup, you can use the following &lt;em&gt;Shader&lt;/em&gt; code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Shader "Custom/UniqueColoring" {
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata {
                float4 vertex : POSITION;
            };

            struct v2f {
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v) {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target {
                // always return red color
                return fixed4(1.0, 0.0, 0.0, 1.0);
            }
            ENDCG
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything was setup correctly, any &lt;em&gt;GameObject&lt;/em&gt; you see through the second camera should now be red.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bits &amp;amp; Bits
&lt;/h2&gt;

&lt;p&gt;As of right now, our &lt;em&gt;Shader&lt;/em&gt; only returns the color red for each fragment, which is determined by the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;return fixed4(1.0, 0.0, 0.0, 1.0);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We want to store a number in at least one of the color channels, say the red one for example.&lt;/p&gt;

&lt;p&gt;The fragment &lt;em&gt;Shader&lt;/em&gt; can be defined to return either a &lt;em&gt;fixed4&lt;/em&gt;, &lt;em&gt;half4&lt;/em&gt;, or a &lt;em&gt;float4&lt;/em&gt; data structure, which represent respectively, 4 11-bit numbers, 4 16-bit numbers, and 4 32-bit numbers. [&lt;a href="https://docs.unity3d.com/Manual/SL-DataTypesAndPrecision.html" rel="noopener noreferrer"&gt;Source&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;To maximize the amount of data we can encode in the color, will choose to go with our fragment &lt;em&gt;Shader&lt;/em&gt; returning a &lt;em&gt;float4&lt;/em&gt; data structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  32-bits
&lt;/h2&gt;

&lt;p&gt;The following section is very important to make sure everything works as expected.&lt;/p&gt;

&lt;p&gt;We need to pay attention that when the fragment &lt;em&gt;Shader&lt;/em&gt; returns 4 32-bit values, that any other functions these are passed to, do not make us lose any precision by converting the values to a data structure of lower precision.&lt;/p&gt;

&lt;p&gt;Specifically, for reading colors I will be using my approach from my &lt;a href="https://dev.to/alpenglow/unity-fast-pixel-reading-part-2-asyncgpureadback-4kgn"&gt;last post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The following outlines the entire pass from the &lt;em&gt;Shader&lt;/em&gt; to the final reading:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;em&gt;Shader&lt;/em&gt; returns 4 32-bit values.&lt;/li&gt;
&lt;li&gt;The image of my second camera is rendered into my &lt;em&gt;Render Texture&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;My &lt;em&gt;Compute Shader&lt;/em&gt; reads a pixel from the &lt;em&gt;Render Texture&lt;/em&gt;, and stores it in a buffer.&lt;/li&gt;
&lt;li&gt;A script I have reads the value from the buffer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Something that I missed early on when developing this solution is the precision value of the color channels for the &lt;em&gt;Render Texture&lt;/em&gt;, which by default, are set to a very low precision value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best &lt;em&gt;Render Texture&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;A &lt;em&gt;Render Texture&lt;/em&gt; object has many properties for its &lt;em&gt;RenderTextureFormat&lt;/em&gt; enumeration.&lt;/p&gt;

&lt;p&gt;Here's a screenshot from &lt;a href="https://docs.unity3d.com/ScriptReference/RenderTextureFormat.html" rel="noopener noreferrer"&gt;the official documentation&lt;/a&gt; including some of them:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fli5rmijznwdv4wudvkc2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fli5rmijznwdv4wudvkc2.png" alt="Image description" width="800" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our case we select the last option visible in the screenshot above: "ARGBFloat", making each color channel 32-bits, such that we preserve the precision level from the &lt;em&gt;Shader&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In my case, I am not creating the &lt;em&gt;Render Texture&lt;/em&gt; programmatically. I select the &lt;em&gt;R32G32B32A32_SFLOAT&lt;/em&gt; option under the &lt;em&gt;Color Format&lt;/em&gt; field.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3324ygz9jx99fm98az4z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3324ygz9jx99fm98az4z.png" alt="Image description" width="455" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In any case, we need to make sure that there is no loss in precision, and that at each step we preserve the 4 32-bit values without making any change to them.&lt;/p&gt;

&lt;p&gt;In line with another great &lt;a href="https://medium.com/@philippchristoph/pixel-perfect-selection-using-shaders-16070d3094d" rel="noopener noreferrer"&gt;article&lt;/a&gt;:&lt;br&gt;
&lt;em&gt;Additional Considerations&lt;/em&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We use point filtering, since we don't want anti-aliasing or filtering.&lt;/li&gt;
&lt;li&gt;We don't need MipMaps.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;Skybox&lt;/em&gt; should just be a solid color, filled with 0s as can be seen below:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpuvffny8drkxv937e2e5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpuvffny8drkxv937e2e5.png" alt="Image description" width="446" height="148"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How much is 32-bits?
&lt;/h2&gt;

&lt;p&gt;In a 32-bit system, each bit can represent two possible values: 0 or 1. Since there are 32 bits in total, we can calculate the number of possible values by raising 2 to the power of 32:&lt;/p&gt;

&lt;p&gt;2^32 = 4,294,967,296&lt;/p&gt;

&lt;p&gt;Therefore, there are 4,294,967,296 different values that can be represented in 32 bits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Everything should work now.&lt;br&gt;
Try changing the &lt;em&gt;Shader&lt;/em&gt;'s color to have something like &lt;em&gt;0.5&lt;/em&gt; or &lt;em&gt;0.001&lt;/em&gt; in its red color channel, to then see if this is indeed the value output in the console (assuming you are logging every reading).&lt;/p&gt;

</description>
      <category>unity3d</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>[Unity] Fast Pixel Reading (Part 2): AsyncGPUReadback</title>
      <dc:creator>Daniel Chou Rainho</dc:creator>
      <pubDate>Tue, 30 May 2023 19:55:54 +0000</pubDate>
      <link>https://dev.to/alpenglow/unity-fast-pixel-reading-part-2-asyncgpureadback-4kgn</link>
      <guid>https://dev.to/alpenglow/unity-fast-pixel-reading-part-2-asyncgpureadback-4kgn</guid>
      <description>&lt;p&gt;From my previous post, I felt a bit disappointed with the results.&lt;/p&gt;

&lt;p&gt;Although my approach for reading pixels was significantly faster, in the end, the performance hit on the running fps count was approximately the same.&lt;/p&gt;

&lt;p&gt;Hence this second post, in which I build on the previous post to create a solution that leads to a &lt;strong&gt;negligible&lt;/strong&gt; drop from the baseline fps count for a default empty scene, as compared to the optimized approach from my previous post, which leads to a baseline drop of &lt;strong&gt;22.89%&lt;/strong&gt;, all while using the optimized &lt;em&gt;Compute Shader&lt;/em&gt; from my previous post.&lt;/p&gt;

&lt;h1&gt;
  
  
  AsyncGPUReadback
&lt;/h1&gt;

&lt;p&gt;The following implementation is an adaptation of my code from the previous post, being an implementation of the &lt;em&gt;ReadPixels&lt;/em&gt; method using a &lt;em&gt;Compute Shader&lt;/em&gt; for optimized performance.&lt;/p&gt;

&lt;p&gt;In particular, in my new approach, I use the &lt;em&gt;AsyncGPUReadback&lt;/em&gt; method, which allows me to read data back from the GPU in a non-blocking way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections.Generic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine.Rendering&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PixelReader2&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;RenderTexture&lt;/span&gt; &lt;span class="n"&gt;inputTexture&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Texture2D&lt;/span&gt; &lt;span class="n"&gt;texture2D&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;ComputeShader&lt;/span&gt; &lt;span class="n"&gt;ReadFirstPixel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;ComputeBuffer&lt;/span&gt; &lt;span class="n"&gt;outputBuffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;kernelID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;texture2D&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Texture2D&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inputTexture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inputTexture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TextureFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RGBA32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Create the output buffer&lt;/span&gt;
        &lt;span class="n"&gt;outputBuffer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ComputeBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Get the kernel ID of the ReadFirstPixel function&lt;/span&gt;
        &lt;span class="n"&gt;kernelID&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ReadFirstPixel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FindKernel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ReadFirstPixel"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Set the input texture and output buffer to the shader&lt;/span&gt;
        &lt;span class="n"&gt;ReadFirstPixel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetTexture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kernelID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"inputTexture"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inputTexture&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;ReadFirstPixel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kernelID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"outputBuffer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;outputBuffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;StartCoroutine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;ReadPixel&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;IEnumerator&lt;/span&gt; &lt;span class="nf"&gt;ReadPixel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Dispatch the compute shader&lt;/span&gt;
        &lt;span class="n"&gt;ReadFirstPixel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kernelID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Request the data from the GPU to the CPU&lt;/span&gt;
        &lt;span class="n"&gt;AsyncGPUReadbackRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AsyncGPUReadback&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputBuffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Wait for the request to complete&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hasError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GPU readback error detected."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Extract the color components from the output array&lt;/span&gt;
            &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;outputArray&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetData&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;Color&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;outputArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;outputArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;outputArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnDestroy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Release the output buffer&lt;/span&gt;
        &lt;span class="n"&gt;outputBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test Setup
&lt;/h2&gt;

&lt;p&gt;For evaluating performance I use the following script, which logs the average fps count over the first 30 seconds of running the game.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AverageFPSCounter&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;frameCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;startTime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;frameCount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;frameCount&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;startTime&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="m"&gt;30f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;averageFPS&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;frameCount&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Average FPS over 10 seconds: "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;averageFPS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Optionally, you can stop the script from updating further.&lt;/span&gt;
            &lt;span class="n"&gt;enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Performance Evaluation
&lt;/h2&gt;

&lt;p&gt;FPS Baseline Drop from &lt;strong&gt;57.53fps&lt;/strong&gt; to&lt;br&gt;
old implementation: &lt;strong&gt;22.89% (44.36fps)&lt;/strong&gt;&lt;br&gt;
new implementation: &lt;strong&gt;-0.83% (58.01fps)&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>unity3d</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>[Unity] Fast Pixel Reading (Part 1): Render Textures &amp; Compute Shaders</title>
      <dc:creator>Daniel Chou Rainho</dc:creator>
      <pubDate>Wed, 24 May 2023 19:44:55 +0000</pubDate>
      <link>https://dev.to/alpenglow/unity-fast-pixel-reading-cbc</link>
      <guid>https://dev.to/alpenglow/unity-fast-pixel-reading-cbc</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;For a project, I needed to call &lt;em&gt;ReadPixels&lt;/em&gt; every single frame, i.e. in the Update function.&lt;/p&gt;

&lt;p&gt;As it turns out, this is a very costly operation, because it involves communicating information from the GPU to the CPU.&lt;/p&gt;

&lt;p&gt;The following post details how I improved the speed at which I get information from the GPU to the CPU from &lt;strong&gt;0.257ms&lt;/strong&gt; to &lt;strong&gt;0.075ms&lt;/strong&gt;, an improvement of 70%.&lt;/p&gt;

&lt;h2&gt;
  
  
  Foreword
&lt;/h2&gt;

&lt;p&gt;For this post I replicate my findings from a Unity project I built from scratch so that it can be replicated easily if wanted.&lt;/p&gt;

&lt;p&gt;The following are the steps I took, starting in Unity Hub.&lt;br&gt;
I also include simple performance evaluations at every step.&lt;/p&gt;

&lt;p&gt;With "baseline" I shall refer to the fps count level measured in my "3D (URD)" Unity scene, before having done anything to it after creating it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;For evaluating performance I use the following script, which logs the average fps count over the first 10 seconds of running the game.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AverageFPSCounter&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;frameCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;startTime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;frameCount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;frameCount&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;startTime&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="m"&gt;10f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;averageFPS&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;frameCount&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Average FPS over 10 seconds: "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;averageFPS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Optionally, you can stop the script from updating further.&lt;/span&gt;
            &lt;span class="n"&gt;enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create a new Unity project using the "3D (URP)" template. (I'm using Unity version 2021.3.16f1)&lt;/li&gt;
&lt;li&gt;Create a new "Camera" game object.&lt;/li&gt;
&lt;li&gt;Remove the "Audio Listener" component.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Running the game now with 2 active cameras in the scene naturally tanks performance by quite a bit.&lt;/p&gt;

&lt;p&gt;Performance Evaluation: &lt;strong&gt;Baseline drop of 43%&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  RenderTexture
&lt;/h2&gt;

&lt;p&gt;In my case I just want to read the color of pixels from a separate camera, which does not need to be rendered to the player, which is why I just render it into a &lt;em&gt;RenderTexture&lt;/em&gt;, wich is a lot more efficient.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a &lt;em&gt;RenderTexture&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Assign it to the "Output Texture" of the secondary camera.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Performance Evaluation: &lt;strong&gt;Baseline drop of 11%&lt;/strong&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  ReadPixel
&lt;/h2&gt;

&lt;p&gt;I created a simple script that reads the color of the first pixel from the &lt;em&gt;RenderTexture&lt;/em&gt;, and does this every single frame.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections.Generic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PixelReader&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;RenderTexture&lt;/span&gt; &lt;span class="n"&gt;_renderTexture&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Texture2D&lt;/span&gt; &lt;span class="n"&gt;texture2D&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;texture2D&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Texture2D&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_renderTexture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_renderTexture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TextureFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RGBA32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;ReadFirstPixel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ReadFirstPixel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Copy the RenderTexture's pixels to the Texture2D&lt;/span&gt;
        &lt;span class="n"&gt;RenderTexture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_renderTexture&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;texture2D&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadPixels&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Rect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_renderTexture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_renderTexture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;texture2D&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Apply&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Get the first pixel's color&lt;/span&gt;
        &lt;span class="n"&gt;Color&lt;/span&gt; &lt;span class="n"&gt;firstPixelColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;texture2D&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetPixel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Attach the script to the secondary camera.&lt;/li&gt;
&lt;li&gt;Drag previously created &lt;em&gt;RenderTexture&lt;/em&gt; into the visible "Render Texture" field.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Performance evaluation: &lt;strong&gt;Baseline drop of 23%&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Line-by-Line Code Performance Evaluation
&lt;/h2&gt;

&lt;p&gt;Executing the &lt;em&gt;ReadFirstPixel&lt;/em&gt; function 100 times, the average runtime for it is &lt;strong&gt;0.257ms&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I also made separate evaluations for all lines within the &lt;em&gt;ReadFirstPixel&lt;/em&gt; function. The number in brackets next to the line number indicates the average runtime for the line across 100 runs.&lt;/p&gt;

&lt;p&gt;Line 1 (0.011ms):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;RenderTexture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_renderTexture&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Line 2 (&lt;strong&gt;0.213ms&lt;/strong&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;texture2D&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadPixels&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Rect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_renderTexture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_renderTexture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Line 3 (0.064ms):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;texture2D&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Apply&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Line 4 (0.007ms):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;Color&lt;/span&gt; &lt;span class="n"&gt;firstPixelColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;texture2D&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetPixel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conclusion: As expected, &lt;em&gt;ReadPixels&lt;/em&gt; is indeed the function that takes the longest to execute. Let's try to optimize this!&lt;/p&gt;

&lt;h2&gt;
  
  
  Compute Shader
&lt;/h2&gt;

&lt;p&gt;Instead of using &lt;em&gt;ReadPixels&lt;/em&gt;, we will be creating a &lt;em&gt;Compute Shader&lt;/em&gt; to transfer the color data from the GPU to the CPU in a faster way.&lt;/p&gt;

&lt;p&gt;The following is my Compute Shader.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#pragma kernel ReadFirstPixel

// Input texture
Texture2D&amp;lt;float4&amp;gt; inputTexture;

// Output buffer
#pragma bind_buffer(name: outputBuffer, binding: 0)

RWStructuredBuffer&amp;lt;float4&amp;gt; outputBuffer;

[numthreads(1, 1, 1)]
void ReadFirstPixel(uint3 id : SV_DispatchThreadID)
{
    // Read the color of the first pixel
    float4 color = inputTexture.Load(int3(0, 0, 0));

    // Store the color in the output buffer
    outputBuffer[0] = color;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following is my new version of the &lt;em&gt;PixelReader&lt;/em&gt; script, which now uses the compute shader.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections.Generic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PixelReader&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;RenderTexture&lt;/span&gt; &lt;span class="n"&gt;inputTexture&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Texture2D&lt;/span&gt; &lt;span class="n"&gt;texture2D&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SerializeField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;ComputeShader&lt;/span&gt; &lt;span class="n"&gt;ReadFirstPixel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;ComputeBuffer&lt;/span&gt; &lt;span class="n"&gt;outputBuffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;kernelID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;texture2D&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Texture2D&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inputTexture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inputTexture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TextureFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RGBA32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Create the output buffer&lt;/span&gt;
        &lt;span class="n"&gt;outputBuffer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ComputeBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Get the kernel ID of the ReadFirstPixel function&lt;/span&gt;
        &lt;span class="n"&gt;kernelID&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ReadFirstPixel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FindKernel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ReadFirstPixel"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Set the input texture and output buffer to the shader&lt;/span&gt;
        &lt;span class="n"&gt;ReadFirstPixel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetTexture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kernelID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"inputTexture"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inputTexture&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;ReadFirstPixel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kernelID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"outputBuffer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;outputBuffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;FastReadPixel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;FastReadPixel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Dispatch the compute shader&lt;/span&gt;
        &lt;span class="n"&gt;ReadFirstPixel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kernelID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Read the result from the output buffer&lt;/span&gt;
        &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;outputArray&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="n"&gt;outputBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputArray&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Extract the color components from the output array&lt;/span&gt;
        &lt;span class="n"&gt;Color&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;outputArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;outputArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;outputArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnDestroy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Release the output buffer&lt;/span&gt;
        &lt;span class="n"&gt;outputBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Performance evaluation: &lt;strong&gt;Baseline drop of 25%&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Line-by-Line Code Performance Evaluation
&lt;/h2&gt;

&lt;p&gt;Executing the new &lt;em&gt;FastReadPixel&lt;/em&gt; function 100 times, the average runtime for it is &lt;strong&gt;0.075ms&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I also made separate evaluations for all lines within the &lt;em&gt;FastReadPixel&lt;/em&gt; function. The number in brackets next to the line number indicates the average runtime for the line across 100 runs.&lt;/p&gt;

&lt;p&gt;Line 1 (0.005ms):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;ReadFirstPixel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kernelID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Line 2 (0.001ms):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;outputArray&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Line 3 (&lt;strong&gt;0.073ms&lt;/strong&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;outputBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputArray&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Line 4 (0.001ms):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;Color&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;outputArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;outputArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;outputArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>unity3d</category>
      <category>gamedev</category>
    </item>
  </channel>
</rss>
