How to pass information from one pixel shader pass to another?

6 months 1 week ago #1 by ArturoBandini
Hi there,
I already know that there is no such thing like "global variables", so there's no regular way to use the same variable or data storage between to different passes. I understood that there should be a "workaround" by using color values that are being passed over by putting the information into sampler/texture.

So I can write data into a texture (via RenderTarget) and I'm able to read that data later from within another pass. But that doesn't help, because I cannot determine to which pixel.xy my data is being written.

Pass1 reads color values from every pixel and calculates something you could call "brightness": b = (color.r + color.g + color.b) / 3
When all pixels are processed, I want to calculate the average brightness of the whole frame, meaning I add all b's from every pixel and divide it by number of total pixels.

In Pass2 I now would like to access the result of this calculation. How do I do that? Of course I can write the result for every pixels brightness into another sampler, but within a pass I'm not able to address a sepcific pixel (x,y) to store the information. I can read from any pixel, though, but I can only write information to the current pixel coordinates. Pixels don't "know" anything about the other pixels in the current frame. So I cannot sum up anything as I cannot access a specific pixel (for example px(0,0)) to store the information to or do some math. So in the following pass I will not be able to read the information I need.

Is there any way to do so? I think that Compute Shaders don't help here as well as I don't seem to be able to access color values of a sampler within a compute shader and vice versa.

Any ideas?
Thanks in advance. I hope there will be a solution, as I would need this kind of "information gathering" for a shader I'm currently working on ;)

Please Log in or Create an account to join the conversation.

6 months 1 week ago #2 by crosire
You can read from whichever pixel you want and you can specify how a pixel shader writes to a texture (setting render target to a texture with width 5 and height 10 means it will be executed 5 * 10 times, with the x/y coordinates in the SV_POSITION input).
So this is fairly simple to do:
texture AverageOfEachRowTex { Width = 1; Height = BUFFER_HEIGHT; };
sampler AverageOfEachRow { Texture = AverageOfEachRowTex; };

void AverageEachRowPS(in float4 vpos : SV_POSITION, out float4 average) {
    // vpos.x will always be 0, vpos.y will be between 0 and BUFFER_HEIGHT (since viewport has a width of 1 and height of BUFFER_HEIGHT, since render target is AverageOfEachRowTex)
    average = 0.0;
    for (int x = 0; x < BUFFER_WIDTH; ++x) {
        average += tex2Dfetch(ReShade::BackBuffer, int2(x, vpos.y));
    average /= BUFFER_WIDTH;

void UseAveragePS(in float4 vpos : SV_POSITION, in float2 texcoord : TEXCOORD, out float4 color) {
    color = tex2D(ReShade::BackBuffer, texcoord);
    color *= tex2Dfetch(AverageOfEachRow, int2(0, vpos.y)); // Multiply pixel color with average of the entire row

technique X {
    pass {
        VertexShader = PostProcessVS;
        PixelShader = AverageEachRowPS; // Is executed with a viewport of 1xBUFFER_HEIGHT (because render target is AverageOfEachRowTex)
        RenderTarget = AverageOfEachRowTex;
    pass {
        VertexShader = PostProcessVS;
        PixelShader = UseAveragePS; // Is executed with a viewport of BUFFER_WIDTHxBUFFER_HEIGHT (because render target is backbuffer)

With compute shaders it's similar, only that instead of the execute size being determinted by the dimension of the render target you specify the dimensions a compute shader is executed with via DispatchSizeX and DispatchSizeY pass states (since there is no explicit render target, you can write to any storage object and wherever in it). Can then use the index that is passed into the compute shader entry point to calculate coordinates to read from a sampler and a write index into a storage object how you see fit.

Please Log in or Create an account to join the conversation.

6 months 1 week ago #3 by ArturoBandini
Hey, that's great! It's exactly what I was looking for, and it works as expected! I think I've learned something today and now I'm a step further to understanding shader concepts.

Thanks a lot for your help. And also for your dedication to ReShade and all your hard work. I appreciate it very much :-)

Please Log in or Create an account to join the conversation.

6 months 1 week ago - 6 months 1 week ago #4 by brussell
Btw, if you want to write to specific pixels you can use sth. like the following (which draws a pixel at a specified coordinate):
float4 PS_DrawPixel(float4 pos : SV_Position, float2 texcoord : TEXCOORD) : SV_Target
float4 color = tex2Dlod(BackBuffer, float4(texcoord, 0, 0));
float4 pixelrgba = float4(255, 0, 0, 0);
float2 pixelpos = float2(960, 540);
if (floor(pos.x) == pixelpos.x && floor(pos.y) == pixelpos.y) { return pixelrgba; }
return color;
Last edit: 6 months 1 week ago by brussell.
The following user(s) said Thank You: ArturoBandini

Please Log in or Create an account to join the conversation.

6 months 1 week ago #5 by ArturoBandini
Thank you @brussel. That's really a good way to draw pixels.

And here's what I've been working on today (only possible with crosire's help ;)):


It features auto detection of current aspect ratio (with a simple heuristic). Calculation only lasts about 0.6 ms, so it can be part of every cycle (I've already got some ideas for better performance). It seems to be very reliable, so I'm going to integrate this into my All-in-One CRT shader (so I'm able to do round corners at the correct position). .
Btw: my today's shader also features a "freeze frame" function. You can freeze the current frame by just pressing space. Like this it's possible to examine a still picture and try out different shaders for a specific frame with varying parameters.


Please Log in or Create an account to join the conversation.