Part 2:
My answer above does work, but it still messes up the upper 2 bits due to float4 frag().
So, here is the solution to that, using compute shaders:
So, we need to find a way to fill the unsigned-integer-texture with uint4
Unity doesn’t seem to allow returning uint4
from frag()
in shader. It compiles but returns zeros.
Therefore, we can do it via compute shader. Create one and put this code:
// ComputeShaderExample.compute
#pragma kernel CSMain
// Declare the RWTexture with uint4 since we're working with unsigned integers
RWTexture2D<uint4> Result;
[numthreads(8, 8, 1)]
void CSMain (uint3 id : SV_DispatchThreadID) {
// Example unsigned integer values to write to the texture
uint4 values = uint4(255, 255, 255, 255);
// Writing the unsigned integer values to the texture
Result[id.xy] = values;
}
In your C# script, to populate the unsigned-integer-texture with values do:
[SerializeField] ComputeShader _myComputeShader;
RenderTexture texture = /*make or get your render texture here*/
texture.enableRandomWrite = true;//so that our compute shaders work fine with it.
int kernelHandle = _maskFilingShader.FindKernel("CSMain");
_myComputeShader.SetTexture(kernelHandle, "Result", texture, 0);
_myComputeShader.Dispatch(kernelHandle, texture.width/8, texture.height/8, 1);
Adjust the 8 and 1 depending on your hardware, to improve performance.
If you do, change it both in [numthreads(8, 8, 1)]
and also in c# .Dispatch()
However, the slightly tricky part is reading your unsigned-integer-texture in your shader.
From C#, assign your texture into material:
_myMaterial.SetTexture("_MyUIntegerTexture", texture);
_myMaterial.SetInt("_MyUIntegerTexture_Width", texture.width);
_myMaterial.SetInt("_MyUIntegerTexture_Height", texture.height);
And in shader do:
Texture2D<uint4> _MyUIntegerTexture;
int _MyUIntegerTexture_Width;
int _MyUIntegerTexture_Height;
fixed4 frag(v2f input) : SV_Target{
uint3 sampleCoord = uint3(input.uv.x*_MyUIntegerTexture_Width, input.y*_MyUIntegerTexture_Height, 0);
uint4 rgba128 = _MyUIntegerTexture.Load(sampleCoord);
}
Notice that we did Texture2D and not just Texture2D nor sampler2D. Otherwise it would return zeros.
Also, notice that we assigned result to uint4 rgba128 and didn’t convert to float or other types, to avoid losing the values in the process.
There are important details about SV_DispatchThreadID, SV_GroupID, SV_GroupThreadID
You can read about them here and check unity example here.
In many cases we can just use SV_DispatchThreadID
. For out-of-bounds checks see here
Or if that post ever gets removed, here is quick copy:
explanation
SV_DispatchThread ID (uint3)
Indices for which combined thread and thread group a compute shader is executing in. SV_DispatchThreadID is the sum of SV_GroupID * numthreads and GroupThreadID. It varies across the range specified in Dispatch and numthreads. For example, if Dispatch(2,2,2) is called on a compute shader with numthreads(3,3,3) SV_DispatchThreadID will have a range of 0…5 for each dimension.
SV_GroupID (uint3)
Indices for which thread group a compute shader is executing in. The indices are to the whole group and not an individual thread. Possible values vary across the range passed as parameters to Dispatch. For example calling Dispatch(2,1,1) results in possible values of 0,0,0 and 1,0,0.
Defines the group offset within a Dispatch call, per dimension of the dispatch call.
SV_GroupThreadID (uint3)
Indices for which an individual thread within a thread group a compute shader is executing in. SV_GroupThreadID varies across the range specified for the compute shader in the numthreads attribute. For example, if numthreads(3,2,1) was specified possible values for the SV_GroupThreadID input value have this range of values (0–2,0–1,0).
SV_GroupIndex (uint)
The “flattened” index of a compute shader thread within a thread group, which turns the multi-dimensional SV_GroupThreadID into a 1D value. SV_GroupIndex varies from 0 to (numthreadsX * numthreadsY * numThreadsZ) - 1.