I'm learning compute shader with Unity, but I've encountered a bug when transferring RenderTexture to the compute shader. Basically, there're 2 texture being transferred, `renderTexture` and `copyTex`. The steps are as follow:
1. `copyTex` copy from `renderTexture` with `Graphics.CopyTexture()`
2. Both are transferred to compute shader, and a new `renderTexture` would be calculated from `copyTex`
3. Compute shader finished, `renderTexture` is used to display, then everything looped again
But for some reason, the `copyTex` after transferred to the compute shader is blank.
This is the main C# code:
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);
}
}
And the compute shader:
#pragma kernel CSMain
RWTexture2D Result;
RWTexture2D 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
}
}
}
I can conclude the `copyTex` is blank because right after copy, I tested it with this piece of code:
RenderTexture.active = copyTex;
tex.ReadPixels(new Rect(0, 0, copyTex.width, copyTex.height), 0, 0);
tex.Apply();
image.material.mainTexture = tex;
and everything show up fine; but after the compute shader ran and the `renderTexture` got display, every pixels were red (which means the if statement activated, and all those pixels are previously white).
So texture can't be manipulated to transfer data and I have to use a custom `RWStructuredBuffer` instead or I mess up somewhere?
↧