4
\$\begingroup\$

I'm trying to write into a Computebuffer in the fragment shader, but it doesn't work. Its pretty hard to get the Depth-Value in the Compute Buffer, so it would be so easy to just use the Pixel/Fragment Shader instead the ComputeShader. But it doesn't seem to work.

Here is what this code is doing: First I'm creating a new Computebuffer and writing into a Computebuffer inside my C# code (a red screen)

After it's been written, I'm reading that buffer from a shader (works)

Next I'm using another shader to fill the buffer with some gradient. This should work - but it doesn't work.

When I'm reading the buffer again - I'm still seeing the red screen.

Does anyone here have an idea why? And how I can get to write things into the Computebuffer while I'm in the Fragment Shader?

My Code: (bufferscript.cs) (bind to Main Camera to test)

using UnityEngine;
using System.Collections;
using System.IO;

[ExecuteInEditMode]
public class bufferScript : MonoBehaviour
{
    public bool writeBufferOn;
    public bool readBufferOn;    
    public ComputeBuffer myBuffer;
    public Material writeBufferMat;
    public Material readBufferMat;

    void Update()
    {
        if (readBufferMat == null)
        {
            readBufferMat = Resources.Load<Material>("readBufferMat") as Material;
        }
        if (readBufferMat.shader == null)
        {
            readBufferMat.shader = Resources.Load<Shader>("readBufferShader") as Shader;
        }
        if (writeBufferMat == null)
        {
            writeBufferMat = Resources.Load<Material>("writeBufferMat") as Material;
        }
        if (writeBufferMat.shader == null)
        {
            writeBufferMat.shader = Resources.Load<Shader>("writeBufferShader") as Shader;
        }
        if (myBuffer == null)
        {            
            // initial fill buffer with red pixels
            myBuffer = new ComputeBuffer(Camera.main.pixelWidth * Camera.main.pixelHeight, 3*sizeof(float), ComputeBufferType.Default);
            float[] v = new float[Camera.main.pixelWidth * Camera.main.pixelHeight * 3];
            for (int c = 0; c < Camera.main.pixelWidth * Camera.main.pixelHeight * 3; c+=3)
            {
                v[c+0] = 1.0f;
                v[c+1] = 0.0f;
                v[c+2] = 0.0f;
            }
            myBuffer.SetData(v);
        }
    }   


    void OnRenderImage(RenderTexture source, RenderTexture destination)
    {        
        if (writeBufferOn == true)
        {
            Graphics.ClearRandomWriteTargets();
            writeBufferMat.SetPass(0);
            writeBufferMat.SetInt("_width", Camera.main.pixelWidth);
            writeBufferMat.SetInt("_height", Camera.main.pixelHeight);
            writeBufferMat.SetBuffer("_myBuffer", myBuffer);
            Graphics.SetRandomWriteTarget(1, myBuffer);
            Graphics.Blit(source, destination, writeBufferMat);
            Graphics.ClearRandomWriteTargets();

            // expecting red/blue to be 0 and green with gradient from 0 to 1 across screen width
            float[] v = new float[Camera.main.pixelWidth * Camera.main.pixelHeight * 3];
            myBuffer.GetData(v);
            if (v[3 * 0 + 0] != 0.0f)
            {
                print("Still red, write to compute buffer failed!");
            }
        }
        if (readBufferOn == true)
        {
            Graphics.ClearRandomWriteTargets();
            readBufferMat.SetPass(0);
            readBufferMat.SetInt("_width", Camera.main.pixelWidth);
            readBufferMat.SetInt("_height", Camera.main.pixelHeight);
            readBufferMat.SetBuffer("_myBuffer", myBuffer);
            Graphics.Blit(source, destination, readBufferMat);
        }
    }

    //garbage collector extra 
    void OnDisable()
    {
        if (myBuffer != null)
            myBuffer.Dispose();
        myBuffer = null;
    }
}

My Shader (readBufferShader)

Shader "custom/readBufferShader" 
{
    SubShader
    {

        Pass
        {
            CGPROGRAM
            #include "UnityCG.cginc"
            #pragma target 5.0
            #pragma vertex vert
            #pragma fragment frag           

            struct v2f
            {
                float4 pos : SV_POSITION;
                float4 uv : TEXCOORD0;
                float4 scrPos : TEXCOORD1;
            };

            //Vertex Shader
            v2f vert(appdata_base v)
            {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                o.scrPos = ComputeScreenPos(o.pos);
                o.uv = float4(v.texcoord.xy, v.texcoord.z, 1);
                return o;
            }

            //Fragment Shader
            int _width;
            int _height;
            StructuredBuffer<float3> _myBuffer : register(u1);

            half4 frag(v2f i) : COLOR
            {
                int x = int(i.scrPos.x * _width);
                int y = int(i.scrPos.y * _height);
                float3 value = _myBuffer[(y * _width) + x]; // reading the buffer works
                return half4(value, 1);             
            }

            ENDCG
        }
    }   
}

My other Shader (writeBufferShader)

Shader "custom/writeBufferShader" 
{
    SubShader
    {

        Pass
        {
            CGPROGRAM
            #include "UnityCG.cginc"            
            #pragma target 5.0
            #pragma vertex vert
            #pragma fragment frag           

            struct v2f
            {
                float4 pos : SV_POSITION;
                float4 uv : TEXCOORD0;
                float4 scrPos : TEXCOORD1;
            };

            //Vertex Shader
            v2f vert(appdata_base v)
            {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);                
                o.scrPos = ComputeScreenPos(o.pos);
                o.uv = float4(v.texcoord.xy, v.texcoord.z, 1);
                return o;
            }

            //Fragment Shader
            int _width;
            int _height;
            RWStructuredBuffer<float3> _myBuffer : register(u1);

            half4 frag(v2f i) : COLOR
            {   
                int x = int(i.scrPos.x * _width);
                int y = int(i.scrPos.y * _height);
                _myBuffer[(y * _width) + x] = float3(0, i.scrPos.x, 0); // buffer should be written here, but nothing happens!
                return half4(1,1,1,1);
            }

            ENDCG
        }   
    }
}
\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

Well- it doesnt seem to work. Im not sure. After weeks, i gave up and I did a workaround. My Solution was:

  1. Graphics.Blit() everything you need from the pixel shader into a RenderTexture
  2. Put the RenderTexture inside a ComputeShader (Texture2D)
  3. Inside the ComputeShader you can finally write into a buffer.

anyway, if anyone has a better solution for this problem- i would be glad to hear it.

\$\endgroup\$
1
\$\begingroup\$

Try to add Graphics.SetRandomWriteTarget(1, myComputeBuffer, true); in C# after you create your ComputeBuffer. The "1" represents the target index ie u1.

The other important part you already got right: RWStructuredBuffer<MyStruct> _MyBuffer : register(u1);

https://docs.unity3d.com/ScriptReference/Graphics.SetRandomWriteTarget.html

When you do StructuredBuffer<MyStruct> _MyBuffer : register(t1); then it's a read only buffer and it works. But a RWStructuredBuffer, requires SetRandomWriteTarget to work at all in a non-compute-shader. This is all Unity API magic which in some ways is convenient but at the same time doesn't help you figure it out, or figure the actual DirectX API out.

\$\endgroup\$

You must log in to answer this question.

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