I’m still struggling with ComputeShaders and ARGB2 textures. Any help anyone could give me would be appreciated.
Here is stripped down C# script and HLSL .compute code. As you can see, I create a Texture2D and a RenderTexture that are each ARGB32 format and of the same dimensions. The RenderTexture has enableRandomWrite=true . I put random Color pixels with alpha=1 in the Texture2D and also initially copy that data to the RenderTexture via Blit. After the Dispatch, in OnPostRender() I copy the RenderTexture back to the Texture2D via ReadPixels. I have confirmed that both of these textures render on the primitive as expected until I call Dispatch().
The ComputeShader has a Texture2D that the C# script assigns the ARGB32 Texture2D texture to and a RWTexture2D that the C# script assigns the ARGB32 RenderTexture to. All this compute shader does is assign the input uint’s to the output uint’s . But, the copy does not seem to work correctly and the eventual rendered texture has been converted to grayscale with transparent pixels, even though the original Texture2D initialization used alpha=1.
I have tried everything I can think of and just cannot understand what the ComputeShader is doing to my input data. I’ve confirmed that I can manually set the output pixel values in the compute shader and those display as expected. [Edit] Actually, I cannot set the output texture values manually when referenced as a a , but I can if they are referenced as a . I’ve tried to manually translate from uint to float4 via bit shifts, but that doesn’t work either. This still seems to be related to how ComputShaders treat ARGB32 RenderTexture’s.
I’ve reduced this down to the bare minimum even though what I really intend to do withe the ComputeShader will be more complex.
// note, this script must be attached to the main camera
using System;
using UnityEngine;
using System.Collections;
using Object = UnityEngine.Object;
using Random = UnityEngine.Random;
public class CameraTextureComputeShaderScript : MonoBehaviour {
public ComputeShader TextureComputeShader0; // this must be public so it can be set in the inspector!!!!!!!!!!!!!
protected int TextureCSMainKernel;
private RenderTexture outputLifeTexture1; // a random write texture for TextureCSMainKernel()
private Texture2D inputLifeTexture1; // a readable texture
int texWidth=1024;
int texHeight=1024;
GameObject primitive;
// Use this for initialization
void Start () {
if (TextureComputeShader0!=null)
{
TextureCSMainKernel = TextureComputeShader0.FindKernel ("TextureCSMainKernel");
inputLifeTexture1 = new Texture2D(texWidth, texHeight, TextureFormat.ARGB32, false, true);
inputLifeTexture1.name="inputLifeTexture1";
TextureComputeShader0.SetTexture (TextureCSMainKernel, "inputTex1", inputLifeTexture1);
Color[] pix = new Color[texWidth*texHeight]; // SetPixels takes Color[], rgba
Random.seed=12345;
for (int p=0; p<(texWidth*texHeight); ++p)
pix[p]= new Color(Random.value,Random.value,Random.value,1); // rgba
inputLifeTexture1.SetPixels (pix);
inputLifeTexture1.Apply ();
outputLifeTexture1 = new RenderTexture(texWidth, texHeight, 0, RenderTextureFormat.ARGB32,
RenderTextureReadWrite.Linear);
outputLifeTexture1.name="outputLifeTexture1";
outputLifeTexture1.enableRandomWrite=true;
outputLifeTexture1.Create (); // otherwise not created until first time it is set to active
TextureComputeShader0.SetTexture (TextureCSMainKernel, "outputTex1", outputLifeTexture1);
RenderTexture.active=outputLifeTexture1;
Graphics.Blit (inputLifeTexture1, outputLifeTexture1);
RenderTexture.active=null;
}
primitive=GameObject.CreatePrimitive(PrimitiveType.Plane);
primitive.renderer.castShadows=false;
primitive.renderer.receiveShadows=false;
primitive.transform.rotation= Quaternion.AngleAxis (90, Vector3.back);
primitive.transform.Rotate(new Vector3(90,0,0),Space.World);
primitive.transform.position= new Vector3(-20,10,0);
primitive.transform.localScale= new Vector3(1,1,1);
Material material= new Material(Shader.Find ("Unlit/Transparent"));
primitive.renderer.material=material;
primitive.renderer.material.color= Color.white;
primitive.renderer.material.renderQueue=4000; // force renderqueue to be after all other transparencies
primitive.renderer.material.mainTexture=outputLifeTexture1;
// primitive.renderer.material.mainTexture=inputLifeTexture1;
}
void Update () {
if (TextureComputeShader0 != null)
TextureComputeShader0.Dispatch (TextureCSMainKernel,texWidth/32,texHeight/32,1);
}
void OnPostRender()
{ // we are still in the render frame at this point
if (outputLifeTexture1 != null)
{
RenderTexture.active = outputLifeTexture1; // copy RenderTexture to Texture2D
inputLifeTexture1.ReadPixels(new Rect(0, 0, outputLifeTexture1.width, outputLifeTexture1.height), 0, 0);
inputLifeTexture1.Apply();
RenderTexture.active = null;
}
}
}
And now for the ComputeShader:
#pragma kernel TextureCSMainKernel
Texture2D<uint> inputTex1; // a readable Texture2D that was defined as ARGB32
RWTexture2D<uint> outputTex1; // a writable RenderTexture that was defined as ARGB32
// threads per group
[numthreads(32,32,1)]
void TextureCSMainKernel (uint3 id : SV_DispatchThreadID)
{
outputTex1[id.xy]=inputTex1[id.xy];
}