3
\$\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\$
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\$
0
\$\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\$

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.