在计算着色器 Unity 中从 RWTexture2D 读取数据

Reading data from RWTexture2D in compute shader Unity

我正在学习使用 Unity 计算着色器,但是在 运行将 RenderTexture 传递给计算着色器时我遇到了一个错误。基本上,有 2 个纹理被 t运行 传递,renderTexturecopyTex。步骤如下:

  1. copyTexrenderTexture 复制 Graphics.CopyTexture()
  2. 两者都被 t运行 提交给计算着色器,并且新的 renderTexture 将从 copyTex
  3. 计算出来
  4. 计算着色器完成,renderTexture用于显示,然后一切循环

但是由于某些原因,运行 之后传递给计算着色器的 copyTex 是空白的。

这是主要的 C# 代码:

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

public class ComputeShaderTest : MonoBehaviour
{
    public ComputeShader computeShader;
    public RenderTexture renderTexture;
    private RenderTexture copyTex;

    [SerializeField] private Image image;
    [SerializeField] private Vector2Int size = new Vector2Int(512, 512);

    private Texture2D tex;

    void Start()
    {
        InitializeRenderTexture();
        StartCoroutine(SlowTest());
    }

    private void InitializeRenderTexture()
    {
        if (!renderTexture)
        {
            renderTexture = new RenderTexture(size.x, size.y, 24);
            renderTexture.enableRandomWrite = true;
            renderTexture.Create();

            copyTex = new RenderTexture(size.x, size.y, 24);
            copyTex.enableRandomWrite = true;
            copyTex.Create();
        }

        //populate texture2d with all white and black patch in middle
        Vector2Int pos = new Vector2Int(size.x / 2, size.y / 2);
        tex = new Texture2D(size.x, size.y, TextureFormat.RGB24, false);
        for (int i = 0; i < size.x; i++)
        {
            for (int j = 0; j < size.y; j++)
            {
                tex.SetPixel(i, j, Color.white);
            }
        }
        for (int i = pos.x-10; i < pos.x+10; i++)
        {
            for (int j = pos.y-10; j < pos.y+10; j++)
            {
                tex.SetPixel(i, j, Color.black);
            }
        }
        tex.Apply();

        //copy to rendertexture
        RenderTexture.active = renderTexture;
        Graphics.Blit(tex, renderTexture);
    }

    IEnumerator SlowTest()
    {
        UpdateTexture2D();
        while(true)
        {
            yield return new WaitForSeconds(1f);
            TouchTest();        
            Debug.Log("Passed");    
            UpdateTexture2D();
        }        
    }

    private void UpdateTexture2D()
    {
        RenderTexture.active = renderTexture;

        tex.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0);
        tex.Apply();
        image.material.mainTexture = tex;
    }

    private void TouchTest()
    {
        Vector2Int pos = new Vector2Int(size.x/2, size.y/2);
        Graphics.CopyTexture(renderTexture, copyTex);

        computeShader.SetInt("posX", pos.x);
        computeShader.SetInt("posY", pos.y);
        computeShader.SetInt("resolution", size.x);
        computeShader.SetTexture(0, "Result", renderTexture);
        computeShader.SetTexture(0, "Copy", copyTex);

        computeShader.Dispatch(0, renderTexture.width / 8, renderTexture.height / 8, 1);
    }
}

计算着色器:

#pragma kernel CSMain

RWTexture2D<float4> Result;
RWTexture2D<float4> Copy;

int posX;
int posY;
int resolution;

int dx[9] = {-1, 1, 1, 0, 1, -1, -1, 0, 0};
int dy[9] = {1, 0, 1, 1, -1, 0, -1, -1, 0};

[numthreads(8,8,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    int x = id.x, y = id.y;
    if (x != 0 && x != resolution - 1 && y != 0 && y != resolution - 1)
    {
        if (Copy[id.xy].a > 0.5) //white
        {
            Result[id.xy] = float4(1, 0, 0, 1); //set to red
        }
    }
}

我可以断定 copyTex 是空白的,因为在复制之后,我用这段代码测试了它:

RenderTexture.active = copyTex;

tex.ReadPixels(new Rect(0, 0, copyTex.width, copyTex.height), 0, 0);
tex.Apply();
image.material.mainTexture = tex;

一切都很好;但是在计算着色器 运行 和 renderTexture 得到显示后,每个像素都是红色的(这意味着 if 语句被激活,并且所有这些像素之前都是白色的)。

因此无法操纵纹理来传输数据,我必须使用自定义 RWStructuredBuffer 来代替,否则我会搞砸什么地方?

更新:从这个问题 loading from RWTexture2D<float4> in a compute shader 中找到了关于 Load() 的信息。仍然无法从 Copy.

获取任何类型的信息

我发现使用 Load()[] 运算符不是问题,而是访问的属性。我用了Copy[id.xy].a。切换到 Copy[id.xy].x 一切正常。

float4 的这些属性在此处讨论:。基本上,.xyzw 等同于 .rgba,因此访问 a 实际上是访问纹理的 alpha。因为我的 alpha 总是 1.0,结果总是白色的。