《Unity着色器圣经》10.0.2 | Our first Compute shader.

目录索引

译文

继续使用USB_simple_color_CS,我们将需要一个3D对象用来表现颜色、纹理和UV坐标的变化。我们将在本练习的场景中添加一个quad,并确保其居中,其位置和旋转设置为“零”。

在我们的项目中,我们将创建一个名为USBSimpleColorController.cs的C#脚本。我们将使用它作为计算着色器的控制器。由于我们将在对象的材质上编写纹理,因此必须将脚本直接指定给3D对象。

图片[1]-《Unity着色器圣经》10.0.2 | Our first Compute shader.-软件开发学习笔记
(Fig. 10.0.2a. The USBSimpleColorController script has been assigned to the Quad we have in the scene).

打开这个脚本,我们将看到如下代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class USBSimpleColorController : MonoBehaviour
{
    // Start is called before the first frame update.
    void Start()
    {
    }
    // Update is called once per frame
    void Update()
    {
    }
}

我们可以看到,它对应于C#脚本的默认结构。它包括start和update,以便于理解。

我们将首先在程序中添加一个全局公共变量来连接计算着色器。我们将称之为m_shader。

public class USBSimpleColorController : MonoBehaviour
{
    public ComputeShader m_shader;
    ...
}

假设我们将在Quad的材质上写入纹理,我们将需要宽度和高度的尺寸。纹理最常见的大小是二次方的值,例如128、256、512、1024等。因此,我们为纹理声明一个RenderTexture类型的公共变量,为其维度声明一个整数值。我们将使用256表示宽度和高度。

public class USBSimpleColorController : MonoBehaviour
{
    public ComputeShader m_shader;
    public RenderTexture m_mainTex;
    int m_texSize = 256;
    ...
}

在上一个示例中,我们声明了一个名为m_mainTex的纹理,并定义了其维度(m_texSize)。它的性质与_MainTex属性有关,我们经常使用该属性来确定着色器类型“.shader”的属性。它建议稍后在_MainTex上写入变量m_MainTex以进行颜色显示。我们只需要定义一个变量,就可以编写Quad材质的纹理。

public class USBSimpleColorController : MonoBehaviour
{
    public ComputeShader m_shader;
    public RenderTexture m_mainTex;
    int m_texSize = 256;
    Renderer m_rend;
    ...
}

稍后我们将使用变量m_rend来存储与对象关联的材质的“渲染器”组件。相反,变量m_mainTex将用于写入我们将在计算着色器中生成的颜色。

在继续解释之前,我们将保存已添加的代码,并转到Unity inspector,在其变量中指定计算着色器。

图片[2]-《Unity着色器圣经》10.0.2 | Our first Compute shader.-软件开发学习笔记
(Fig. 10.0.2b The Compute shader has been assigned in the m_shader variable from the Unity Inspector).

我们返回到脚本,并在Start方法中初始化纹理。为此,我们将把纹理的高度和宽度使用变量m_texSize赋值。

public class USBSimpleColorController : MonoBehaviour
{
    public ComputeShader m_shader;
    public RenderTexture m_mainTex;
    int m_texSize = 256;
    Renderer m_rend;

    void Start ()
    {
        // initialize the texture.
        m_mainTex = new RenderTexture(m_texSize, m_texSize, 0, RenderTextureFormat.ARGB32);
    }
    ...
}

RenderTexture类的构造函数最多有七个参数;然而,我们只需要其中的四个,纹理就可以正常工作。前两个参数对应于纹理的宽度和高度、深度缓冲区,最后是纹理配置(32位RGBA)。现在,我们只需要使选项“randomwriting”为true,并创建纹理即可。

public class USBSimpleColorController : MonoBehaviour
{
    public ComputeShader m_shader;
    public RenderTexture m_mainTex;
    int m_texSize = 256;
    Renderer m_rend;

    void Start ()
    {
        m_mainTex= new RenderTexture(m_texSize, m_texSize, 0, RenderTextureFormat.ARGB32);
        // enable random writing
        m_mainTex.enableRandomWrite = true;
        // let’s create the texture
        m_mainTex.Create();
    }
    ...
}

由于线程组无法相互同步,因此我们无法确定哪个texel将首先写入纹理。因此,在创建之前,我们必须使用enableRandomWrite函数。最后,“Create”函数创建纹理;事实上,我们可以根据Unity的官方文件找到以下文本。

The RenderTexture constructor does not actually create the texture. By default, the texture is created the first time it is activated. The texture is created in advance by calling the Create function.

图片[3]-《Unity着色器圣经》10.0.2 | Our first Compute shader.-软件开发学习笔记
(Fig. 10.0.2c Compute shaders can write texels arbitrarily on a texture).

以下过程将允许C#脚本和计算着色器之间的通信。我们将首先将渲染器组件(特定于四边形材质)保存在我们之前创建的“rend”变量中。

void Start ()
{
    m_mainTex= new RenderTexture(m_texSize, m_texSize, 0, RenderTextureFormat.ARGB32);
    m_mainTex.enableRandomWrite = true;
    m_mainTex.Create();

    // get the material’s renderer component
    m_rend = GetComponent<Renderer>();
    // make the object visible
    m_rend.enabled = true;
}

到目前为止,我们已经创建了纹理,但它没有任何特定的颜色。因此,我们接下来必须做的是将其发送到“计算”着色器,为其指定颜色或设计,然后将其重新指定给使用“四边形”的材质,以便从场景中可见。为此,我们可以使用“ComputeShader.SetTexture”函数。

void Start ()
{
    m_mainTex= new RenderTexture(m_texSize, m_texSize, 0, RenderTextureFormat.ARGB32);
    m_mainTex.enableRandomWrite = true;
    m_mainTex.Create();

    m_rend = GetComponent<Renderer>();
    m_rend.enabled = true;
    // send the texture to the Compute shader
    m_shader.SetTexture(0, "Result", m_mainTex);
}


上图函数m_shader.SetTexture(0)中的第一个参数对应于我们在计算着色器中使用的第一个内核索引。

// Each kernel tells which function to compile;
// you can have many kernels
#pragma kernel CSMain

// Create a RenderTexture with enableRandomWrite and set it
// with cs.SetTexture
RWTexture2D <float4> Result;

[numthreads(8, 8, 1)]
void CSMain (uint3 id : SV_DispatchThreadID) { … }

USB_simple_color_CS着色器只有一个名为CSMain的内核,默认情况下已添加该内核。这个内核占用索引“零”,因为它是程序中唯一的一个。计算着色器可以有多个内核,每个内核都有一个自动指定的id。

#pragma kernel CSMain        // id 0
#pragma kernel CSFunction01  // id 1
#pragma kernel CSFunction02  // id 2


“SetTexture”函数中的第二个参数对应于Compute shader中缓冲区变量的名称。默认情况下,它被称为“Result”,是一个具有读/写功能的2D RGBA纹理(float4)。

RWTexture2D <float4> Result;

最后,函数中的第三个参数对应于我们将在缓冲区变量上写入的纹理;在我们的例子中,它被称为m_mainTex。我们将在CSMain函数的计算着色器中处理此变量,我们可以通过对方法执行的操作来证实这一点。

[numthreads(8, 8, 1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    // TODO: insert actual code here
    Result[id.xy] = float4(id.x & id.y, (id.x & 15)/15.0, (id.y & 15)/15.0, 0);
}

目前,我们不会详细介绍CSMain方法中的操作。我们将继续在场景中当前具有Quad的材质上实现纹理。为此,我们返回USBSimpleColorController脚本,并使用材质的SetTexture函数在_mainTex属性上传递纹理m_mainTex。

void Start ()
{
    m_mainTex= new RenderTexture(m_texSize, m_texSize, 0, RenderTextureFormat.ARGB32);
    m_mainTex.enableRandomWrite = true;
    m_mainTex.Create();

    m_rend = GetComponent<Renderer>();
    m_rend.enabled = true;

    m_shader.SetTexture(0, "Result", m_mainTex);

    // send the texture to the Quad’s material
    m_rend.material.SetTexture("_MainTex", m_mainTex);
}

默认情况下,Unity中的每个着色器都具有_MainTex属性,因此我们可以假设Quad材质也具有该属性。

到目前为止,这一进程几乎已经准备就绪;我们只需要生成线程组,这些线程组将处理我们正在创建的纹理的每个texel。为此,我们必须调用Dispatch函数。

void Start ()
{
    m_mainTex= new RenderTexture(m_texSize, m_texSize, 0, RenderTextureFormat.ARGB32);
    m_mainTex.enableRandomWrite = true;
    m_mainTex.Create();

    m_rend = GetComponent<Renderer>();
    m_rend.enabled = true;

    m_shader.SetTexture(0, "Result", m_mainTex);
    m_rend.material.SetTexture("_MainTex", m_mainTex);

    // generate the threads group to process the texture
    m_shader.Dispatch(0, m_texSize/8, m_texSize/8, 1);
}


函数中的第一个参数指的是内核;同样,由于我们只使用CSMain,我们将不得不在其中放置值“0”。以下三个值对应于我们将生成的用于处理纹理texel的网格(线程组)。第一个值对应于网格的列数,然后是行数,最后是维度数。正如我们已经知道的,m_texSize等于256;因此,如果我们把这个值除以8,我们将得到一个32 x 32 x 1的网格。

图片[4]-《Unity着色器圣经》10.0.2 | Our first Compute shader.-软件开发学习笔记
(Fig. 10.0.2d. Each block in the grid represents a group of threads).

在GPU编程中,需要执行的线程数量被划分为线程组网格,它在每个独立的计算单元上执行一个线程组。

线程同步的操作只能发生在同一组中,从而生成更高效的并行编程方法。GPU无法同步不同的线程组;我们无法控制处理它们的顺序。因此,它可以将这样的组发送到不同的计算单元。

在网格中生成的每个块都对应于一个线程组。现在,每组有多少线程?它的值由CSMain函数顶部的“numthreads”属性决定。

[numthreads(8, 8, 1)]
void CSMain (uint3 id : SV_DispatchThreadID) { … }

正如我们已经知道的,要计算一个组中的线程数,我们只需将列数、行数和维度(8*8*1)相乘。对于我们的配置,我们获得每组64个线程的数量。

图片[5]-《Unity着色器圣经》10.0.2 | Our first Compute shader.-软件开发学习笔记
(Fig. 10.0.2e. The semantics SV_GroupID corresponds to the index of a group to be executed in the Compute shader, numthreads refers to the total number of threads we will have for each group, and SV_GroupThreadID refers to the identifier (index) of each thread separately).

深入研究这个过程以理解语义SV_DispatchThreadID是至关重要的,它是CSMain方法中的一个参数;它对应于我们正在使用的每个组的线程数加上每个线程的索引的总和。

SV_DispatchThreadID = ([[(SV_GroupID) * (numthreads)] + (SV_GroupThreadID))

回到我们的USBSimpleColorController.cs程序,如果我们保存,回到Unity并按下“播放”按钮,我们可以看到该程序已经生成了一个纹理,并被动态分配给Quad。

图片[6]-《Unity着色器圣经》10.0.2 | Our first Compute shader.-软件开发学习笔记
(Fig. 10.0.2f. The texture corresponds to the graphical representation of the Sierpinski fractal).

我们在上图中看到的纹理是在CSMain函数中生成的,其创建过程非常简单。

[numthreads(8, 8, 1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    // TODO: insert actual code here
    Result[id.xy] = float4(id.x & id.y, (id.x & 15)/15.0, (id.y & 15)/15.0, 0);
}

为了理解,我们应该注意CSMain函数的输入参数。

语义SV_DispatchThreadID表示在计算着色器中执行的那些组合线程和线程组的索引;这意味着每个线程的标识都存储在类型为uint3(无符号整数)的id变量中。

与int变量不同,uint变量只有正数,从“零”开始。这是有意义的,因为每个线程组的索引从[0,0,0]开始,因此是uint3数据类型。

(uint3 id : SV_DispatchThreadID)

在id变量中找到的每个线程都在处理Result纹理的纹素。由于它是一个四维RGBA向量,我们可以返回一个纯色,例如绿色。

// return a green color
Result[id.xy] = float4(0, 1, 0, 1);
图片[7]-《Unity着色器圣经》10.0.2 | Our first Compute shader.-软件开发学习笔记
(Fig. 10.0.2g)


需要考虑的一个因素是我们将在计算着色器中使用的线程数量,因为它与我们将获得的最终结果直接相关。

在Dispatch函数的前面,我们将纹理的宽度和高度配置为8,以生成一个由32 x 32 x 1组线程组成的网格。为什么我们在运算中使用数字8作为除数?它与numthreads组件中配置的线程数有关。

让我们进行数学运算来理解这个概念:我们创建的纹理的宽度和高度等于256,如果我们把它除以8,结果是32。它的图形表示将等于八行或八列,每行或每列32个纹素。

图片[8]-《Unity着色器圣经》10.0.2 | Our first Compute shader.-软件开发学习笔记
(Fig. 10.0.2h)


要使结果再次等于256,我们必须乘以8;确切地说,这个数字被设置为numthreads属性中X和Y中的线程总数。如果我们将线程数更改为4[numthreads(4,4,1)],我们会注意到只有1/4的纹理在Quad中渲染。

图片[9]-《Unity着色器圣经》10.0.2 | Our first Compute shader.-软件开发学习笔记
(Fig. 10.0.2i. Color (1, 0, 0, 1))


当32乘以4时,就会出现这个因子,得到128,正好是我们生成的纹理的1/4。


原文对照

Continuing with USB_simple_color_CS, we will need a 3D object for color, texture, and UV coordinate. We will add a Quad to our scene for this exercise and ensure it is centered, with its position and rotation set to “zero”.
In our project, we will create a C# script called USBSimpleColorController.cs. We will use it as a controller for the Compute shader. Since we will write a texture on the object’s material, we will have to assign the script directly to the 3D object.

图片[1]-《Unity着色器圣经》10.0.2 | Our first Compute shader.-软件开发学习笔记
(Fig. 10.0.2a. The USBSimpleColorController script has been assigned to the Quad we have in the scene).

Once the program is opened, we will obtain the following structure:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class USBSimpleColorController : MonoBehaviour
{
    // Start is called before the first frame update.
    void Start()
    {
    }
    // Update is called once per frame
    void Update()
    {
    }
}

We can see that it corresponds to the default structure of a C# script. It includes start and updates functions frame by frame to facilitate understanding.
We will start by adding a global public variable to our program to connect the Compute shader. We will call it m_shader.

public class USBSimpleColorController : MonoBehaviour
{
    public ComputeShader m_shader;
    ...
}

Assuming that we will write a texture on the Quad’s material, we will need dimensions for width and height. The most common sizes for textures are values in powers of two, e.g., 128, 256, 512, 1024, etc. For that reason, we declare a public variable of type RenderTexture for the texture and an integer value for its dimensions. We will use 256 for both width and height.

public class USBSimpleColorController : MonoBehaviour
{
    public ComputeShader m_shader;
    public RenderTexture m_mainTex;
    int m_texSize = 256;
    ...
}

In the previous example, we declared a texture named m_mainTex and defined its dimension (m_texSize). Its nature is associated with the _MainTex property that we frequently use to determine properties for shader type “.shader”. It suggests writing the variable m_mainTex on _MainTex later for color display. We only need to define a variable that allows us to write the Quad’s material’s texture.

public class USBSimpleColorController : MonoBehaviour
{
    public ComputeShader m_shader;
    public RenderTexture m_mainTex;
    int m_texSize = 256;
    Renderer m_rend;
    ...
}

We will use the variable m_rend later to store the “Renderer” component of the material associated with the object. In contrast, the variable m_mainTex will be used to write the colors that we will generate in the Compute shader.
Before continuing with the explanation, we will save the code we have added and go to the Unity inspector to assign the Compute shader in its respective variable.

图片[2]-《Unity着色器圣经》10.0.2 | Our first Compute shader.-软件开发学习笔记
(Fig. 10.0.2b The Compute shader has been assigned in the m_shader variable from the Unity Inspector).



We return to our script and initialize the texture in the Start method. For this, we will use the variable m_texSize for both the texture’s height and width.

public class USBSimpleColorController : MonoBehaviour
{
    public ComputeShader m_shader;
    public RenderTexture m_mainTex;
    int m_texSize = 256;
    Renderer m_rend;

    void Start ()
    {
        // initialize the texture.
        m_mainTex = new RenderTexture(m_texSize, m_texSize, 0, RenderTextureFormat.ARGB32);
    }
    ...
}

The constructor of the RenderTexture class has up to seven arguments; however, we only need four of them for the texture to work correctly. The first two arguments correspond to the texture’s width and height, the depth buffer, and finally, the texture configuration (32-bit RGBA). Now we simply require enabling the random writing options and create the texture as such.

public class USBSimpleColorController : MonoBehaviour
{
    public ComputeShader m_shader;
    public RenderTexture m_mainTex;
    int m_texSize = 256;
    Renderer m_rend;

    void Start ()
    {
        m_mainTex= new RenderTexture(m_texSize, m_texSize, 0, RenderTextureFormat.ARGB32);
        // enable random writing
        m_mainTex.enableRandomWrite = true;
        // let’s create the texture
        m_mainTex.Create();
    }
    ...
}

Since thread groups cannot synchronize with each other, we cannot determine which texel will be written to the texture first. For that reason, we must use the enableRandomWrite function before creating it. Finally, the “Create” function creates the texture; in fact, we can fin“d the following text according to the official Unity documentation.

The RenderTexture constructor does not actually create the texture. By default, the texture is created the first time it is activated. The texture is created in advance by calling the Create function.

图片[3]-《Unity着色器圣经》10.0.2 | Our first Compute shader.-软件开发学习笔记
(Fig. 10.0.2c Compute shaders can write texels arbitrarily on a texture).



The following process will allow the communication between the C# script and the Compute shader. We will start by saving the Renderer component (specific to the Quad material) in the “rend” variable that we created previously.

void Start ()
{
    m_mainTex= new RenderTexture(m_texSize, m_texSize, 0, RenderTextureFormat.ARGB32);
    m_mainTex.enableRandomWrite = true;
    m_mainTex.Create();

    // get the material’s renderer component
    m_rend = GetComponent<Renderer>();
    // make the object visible
    m_rend.enabled = true;
}


We have the texture created up to this point, but it does not have any specific color. Therefore, what we must do next is to send it to the Compute shader, assign it a color or design and then re-assign it to the material that is using the Quad so that it is visible from the scene. To do this, we can use the “ComputeShader.SetTexture” function.

void Start ()
{
    m_mainTex= new RenderTexture(m_texSize, m_texSize, 0, RenderTextureFormat.ARGB32);
    m_mainTex.enableRandomWrite = true;
    m_mainTex.Create();

    m_rend = GetComponent<Renderer>();
    m_rend.enabled = true;
    // send the texture to the Compute shader
    m_shader.SetTexture(0, "Result", m_mainTex);
}


The first argument in the function (0) corresponds to the first kernel index we use in the Compute shader.

// Each kernel tells which function to compile;
// you can have many kernels
#pragma kernel CSMain

// Create a RenderTexture with enableRandomWrite and set it
// with cs.SetTexture
RWTexture2D <float4> Result;

[numthreads(8, 8, 1)]
void CSMain (uint3 id : SV_DispatchThreadID) { … }


The USB_simple_color_CS shader has only one kernel called CSMain, which has been added by default. This kernel occupies index “zero” since it is the only one we have in the program. A Compute shader can have multiple kernels, and each one has an automatically assigned id.

#pragma kernel CSMain        // id 0
#pragma kernel CSFunction01  // id 1
#pragma kernel CSFunction02  // id 2


The second argument in the “SetTexture” function corresponds to the name of the buffer variable in the Compute shader. By default, it is called “Result” and is a 2D RGBA texture (float4) with reading/write capability.

RWTexture2D <float4> Result;


Finally, the third argument in the function corresponds to the texture that we will write on the buffer variable; in our case, it is called m_mainTex. We will process this variable in the Compute shader within the CSMain function, we can corroborate this by the operation being performed into the method.

[numthreads(8, 8, 1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    // TODO: insert actual code here
    Result[id.xy] = float4(id.x & id.y, (id.x & 15)/15.0, (id.y & 15)/15.0, 0);
}


For the moment, we will not go into details about the operation in the CSMain method. For now, we will continue implementing the texture on the material that currently has the Quad in our scene. To do this, we return to the USBSimpleColorController script and pass the texture m_mainTex on the _MainTex property using the SetTexture function of the material.

void Start ()
{
    m_mainTex= new RenderTexture(m_texSize, m_texSize, 0, RenderTextureFormat.ARGB32);
    m_mainTex.enableRandomWrite = true;
    m_mainTex.Create();

    m_rend = GetComponent<Renderer>();
    m_rend.enabled = true;

    m_shader.SetTexture(0, "Result", m_mainTex);

    // send the texture to the Quad’s material
    m_rend.material.SetTexture("_MainTex", m_mainTex);
}


By default, each shader in Unity has the _MainTex property, so we can assume that the Quad material has it as well.
Up to this point, the process is almost ready; we only need to generate the groups of threads that will process each texel of the texture we are creating. For this, we must call the Dispatch function.

void Start ()
{
    m_mainTex= new RenderTexture(m_texSize, m_texSize, 0, RenderTextureFormat.ARGB32);
    m_mainTex.enableRandomWrite = true;
    m_mainTex.Create();

    m_rend = GetComponent<Renderer>();
    m_rend.enabled = true;

    m_shader.SetTexture(0, "Result", m_mainTex);
    m_rend.material.SetTexture("_MainTex", m_mainTex);

    // generate the threads group to process the texture
    m_shader.Dispatch(0, m_texSize/8, m_texSize/8, 1);
}


The first argument in the function refers to the kernel; again, since we are only using CSMain, we will have to place the value “zero” in it. The following three values correspond to the grid (groups of threads) that we will generate to process the texture texels. The first value corresponds to the number of columns that the grid will have, then the rows, and finally the number of dimensions. As we already know, m_texSize equals 256; therefore, if we divide that value into 8, we will obtain a grid of 32 x 32 x 1.

图片[4]-《Unity着色器圣经》10.0.2 | Our first Compute shader.-软件开发学习笔记
(Fig. 10.0.2d. Each block in the grid represents a group of threads).

In GPU programming, the number of threads desired for execution is divided into a thread group grid, and it executes A thread group on each independent compute unit.


The operation of thread synchronization can occur only for those within the same group, generating more efficient parallel programming methods. The GPU cannot synchronize different groups of threads; we have no control over the order in which they will be processed. For this reason, it can send such groups to different compute units.


Each of the blocks generated in the grid corresponds to a thread group. Now, how many threads does each group have? Its value is determined by the “numthreads” attribute at the top of the CSMain function.

[numthreads(8, 8, 1)]
void CSMain (uint3 id : SV_DispatchThreadID) { … }


As we already know, to calculate the number of threads within a group, we simply multiply the number of columns, by the number of rows, by the dimensions (8 * 8 * 1). For our configuration, we obtain the number of 64 threads for each group.

图片[5]-《Unity着色器圣经》10.0.2 | Our first Compute shader.-软件开发学习笔记
(Fig. 10.0.2e. The semantics SV_GroupID corresponds to the index of a group to be executed in the Compute shader, numthreads refers to the total number of threads we will have for each group, and SV_GroupThreadID refers to the identifier (index) of each thread separately).


It is essential to delve into this process to understand the semantics SV_DispatchThreadID, which is found as an argument in the CSMain method; it corresponds to the sum of the number of threads for each group we are using, plus the index of each thread.

SV_DispatchThreadID = ([[(SV_GroupID) * (numthreads)] + (SV_GroupThreadID))


Going back to our USBSimpleColorController.cs program, if we save, go back to Unity and press the “play” button, we can see that the program has generated a texture and is dynamically assigned to the Quad.

图片[6]-《Unity着色器圣经》10.0.2 | Our first Compute shader.-软件开发学习笔记
(Fig. 10.0.2f. The texture corresponds to the graphical representation of the Sierpinski fractal).


The texture we see in the figure above is being generated within the CSMain function, and its creation process is quite simple.

[numthreads(8, 8, 1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    // TODO: insert actual code here
    Result[id.xy] = float4(id.x & id.y, (id.x & 15)/15.0, (id.y & 15)/15.0, 0);
}

To understand, we should pay attention to the arguments of the CSMain function.


The semantics SV_DispatchThreadID represents the indices of those combined threads and thread groups executed in the Compute shader; this means that the identification of each thread is being stored in the id variable of type uint3 (unsigned integer).


Unlike an int variable, uint variables have only positive numbers, starting at “zero”. It makes sense since the indexes of each thread group begin at [0, 0, 0] hence the uint3 data type.

(uint3 id : SV_DispatchThreadID)


Each of the threads found in the id variable is processing the texels of the Result texture. Since it is a four-dimensional RGBA vector, we can return a solid color, e.g., green.

// return a green color
Result[id.xy] = float4(0, 1, 0, 1);
图片[7]-《Unity着色器圣经》10.0.2 | Our first Compute shader.-软件开发学习笔记
(Fig. 10.0.2g)


One factor to consider is the number of threads we will use in our Compute Shader, since it is directly related to the final result we will obtain.


Earlier in the Dispatch function, we configured the width and height of the texture divided into eight to generate a grid of 32 x 32 x 1 groups of threads. Why do we use the number eight as a divisor in operation? It is related to the configured number of threads in the numthreads component.


Let’s perform the mathematical operation to understand the concept: The texture we create has a width and height equal to 256 if we divide it into eight, results in 32. Its graphic representation would equal eight rows or columns of thirty-two texels each, one above the texture.

图片[8]-《Unity着色器圣经》10.0.2 | Our first Compute shader.-软件开发学习笔记
(Fig. 10.0.2h)


For the result to be equal to 256 again, we must multiply by eight; precisely, this number is set as the total number of threads in both X and Y in the numthreads attribute. If we change the number of threads to 4 [numthreads(4, 4, 1)], we will notice that only 1⁄4 of the texture is rendered in the Quad.

图片[9]-《Unity着色器圣经》10.0.2 | Our first Compute shader.-软件开发学习笔记
(Fig. 10.0.2i. Color (1, 0, 0, 1))


This factor occurs when multiplying 32 by 4, resulting in 128, precisely 1⁄4 of the texture we are generating.

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容