1
\$\begingroup\$

I have a fairly simple requirement for a compute shader (DirectCompute through Unity). I have a 128x128 texture and I'd like to turn the red channel of that texture into a 1d array of floats. I need to do this very often so just doing a cpu-side for loop over each texel won't cut it.

Initialization:

    m_outputBuffer = new ComputeBuffer(m_renderTexture.width * m_renderTexture.height, sizeof(float));
    m_kernelIndex = m_computeShader.FindKernel("CSMain");

Here is the C# method:

/// <summary>
/// This method converts the red channel of the given RenderTexture to a
/// one dimensional array of floats of size width * height.
/// </summary>
private float[] ConvertToFloatArray(RenderTexture renderTexture)
{
    m_computeShader.SetTexture(m_kernelIndex, INPUT_TEXTURE, renderTexture);

    float[] result = new float[renderTexture.width * renderTexture.height];

    m_outputBuffer.SetData(result);
    m_computeShader.SetBuffer(m_kernelIndex, OUTPUT_BUFFER, m_outputBuffer);

    m_computeShader.Dispatch(m_kernelIndex, renderTexture.width / 8, renderTexture.height / 8, 1);

    m_outputBuffer.GetData(result);

    return result;
}

and the entire compute shader:

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

// Create a RenderTexture with enableRandomWrite flag and set it
// with cs.SetTexture
Texture2D<float4> InputTexture;
RWBuffer<float> OutputBuffer;

[numthreads(8, 8, 1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
    OutputBuffer[id.x * id.y] = InputTexture[id.xy].r;
}

the C# method returns an array of the expected size, and it usually sort-of-corresponds to what I expect. However, even if my input texture is uniformly red, there'll still be some zeroes.

\$\endgroup\$
2
  • \$\begingroup\$ What led you to the formula id.x * id.y? This will map every cell with x or y = 0 to the same array entry, which doesn't sound like what you're looking for. \$\endgroup\$
    – DMGregory
    Commented May 12, 2018 at 14:50
  • \$\begingroup\$ You’re right, I suppose it was a blind spot for me. I guess a more correct formula would be id.x * n + id.y, with n being the number of pixels in a row? Is there any way of accessing this number of pixels without explicitly passing it in? @DMGregory \$\endgroup\$
    – EmmetOT
    Commented May 12, 2018 at 15:28

1 Answer 1

1
\$\begingroup\$

Ok, thanks to some direction from @DMGregory I reconsidered and solved my own question. The answer was in two parts: I was combining the x and y coordinates (id.x and id.y) strangely, and I was using the wrong input semantic. (SV_GroupThreadID instead of SV_DispatchThreadID)

So here's the solution. I also flipped the y axis to match my intuition.

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

// Create a RenderTexture with enableRandomWrite flag and set it
// with cs.SetTexture
Texture2D<float4> InputTexture;
RWBuffer<float> OutputBuffer;

[numthreads(8, 8, 1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
    uint w, h;
    InputTexture.GetDimensions(w, h);

    OutputBuffer[id.x + id.y * w] = InputTexture[float2(id.x, h - 1 - id.y)].r;
}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .