0
\$\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\$
  • \$\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 May 12 '18 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 May 12 '18 at 15:28
0
\$\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;
}
|improve this answer|||||
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.