Search Unity

Modifying existing shader(create array of brightness values)

Discussion in 'Shaders' started by eco_bach, Nov 28, 2015.

  1. eco_bach

    eco_bach

    Joined:
    Jul 8, 2013
    Posts:
    1,601
    I have this shader which creates a pixellate effect when applied to a Quad. My goal is to create an array of brightness values for each 'cell' in this pixellate effect. Unfortunately I am still going thru beginner shader tutorials and am a bit of a shader noob.
    Is it possible to create this array WITHIN the actual shader and then access this array from my C# code?
    If I understand anything its that the more work done by the shader, the better. Here is the shader I am using>


    Shader "Custom\Pixelate"
    {
    Properties
    {
    //_CellSize ("Cell Size", Vector) = (0.02, 0.02, 0, 0)
    _CellSize ("Cell Size", Vector) = (0.5, 0.5, 0, 0)
    }
    SubShader {
    Tags { "RenderType"="Opaque" "Queue" = "Transparent" }
    LOD 200

    GrabPass { "_PixelationGrabTexture"}

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

    struct v2f {
    float4 pos : SV_POSITION;
    float4 grabUV : TEXCOORD0;
    };

    float4 _CellSize;

    v2f vert(appdata_base v) {
    v2f o;
    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    o.grabUV = ComputeGrabScreenPos(o.pos);
    return o;
    }

    sampler2D _PixelationGrabTexture;

    float4 frag(v2f IN) : COLOR
    {
    float2 steppedUV = IN.grabUV.xy/IN.grabUV.w;
    steppedUV /= _CellSize.xy;
    steppedUV = round(steppedUV);
    steppedUV *= _CellSize.xy;
    return tex2D(_PixelationGrabTexture, steppedUV);
    }
    ENDCG
    }
    }
    }
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    What's your end goal?

    As I understand your question you're looking to get the final brightness values for every "cell" after they've been rendered back in C#. My first response to that is you'll need to use ReadPixels, and my second response is don't do that, there's probably a way to never go back to C#.

    If you really want to get data back from a shader to use in C# you'll need to use a render texture, and you'll want it to be as low resolution as possible. In the case of your pixelate shader the real answer is don't use it, just create another camera assigned a render texture with the resolution matching the number of cells you want and render the grab texture as a blit on that camera. The result will be a low res version of the original texture. Then do a ReadPixels to copy the contents of the render texture to a texture2d which you can call GetPixels on to access the contents.

    Likely any processing you want to do on that array in C# should be done in shaders instead though, which is why I'm asking what your end goal is.
     
    Last edited: Nov 28, 2015
  3. eco_bach

    eco_bach

    Joined:
    Jul 8, 2013
    Posts:
    1,601
    Thanks!. Food for thought. Never considered using a render texture.
    My end goal is to take the brightness values, representing the cells in the pixellated image, and use them to drive quad rotations. So if there are say, 500 cells in the pixellated image, there would be 500 corresponding Quads. Kind of hard to describe.
    Came across another version of a Pixellate shader effect which will allow me to simplify things a bit by applying to the camera via OnRenderImage
    https://github.com/keijiro/unity-facecensor/tree/Unity4/Assets/Pixelation
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    That's definitely something you don't want to do in C# unless you have to and something you can easily do with a vertex shader being fed the scene texture with out the pixelate pass. The pixelate shader is completely unnecessary, and is actually not useful for the effect you want.

    You need a mesh with your 500 quads (which you could generate initially from C#) with one of the UVs sets used to store the position of the center of that quad in the mesh. A vertex shader can then use that to sample the initial grab texture and rotate the quad based on the brightness.
     
  5. eco_bach

    eco_bach

    Joined:
    Jul 8, 2013
    Posts:
    1,601
    bgolus, thanks!! Yes that seems like it would be the ULTIMATE solution but WAY beyond my limited shader writing skills at the moment.

    Which is why I have to settle on a shader, C# script hybrid solution. Also working in the Unity IDE will give me more flexibility in working with other devs.
    But am going to try your render texture option also as an alternative to the shader approach.
    However, confused by your comment 'another camera assigned a render texture with the resolution matching the number of cells you want'
    How do you change the resolution of just one camera?

    In my tests so far with ReadPixels, I have been able to achieve performance above 50fps by simply using a smaller Rect area... since I'm pixellating the webcamTexture in the first place from a HD webcam, I only need a resolution of no more than 160 px wide by 90 px tall. (see green area in image below)

    ps
    I'm going thru the 'Noob to Pro' shader series online. Any other tutorials you'd recommend?
    readPixel_area.jpg
     
    Last edited: Nov 29, 2015
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    The resolution is set by the render texture itself, not the camera. When you create a render texture you must define the resolution. Really you don't even need the other camera, and you can do the luminosity calculation in a shader to optimize further.

    public Texture2D lumTexture;
    public RenderTexture rt;
    public Material lumMaterial;

    void Start()
    {
    rt = RenderTexture(160, 90, 0, RenderTextureFormat.A8, true);
    lumTexture = new Texture2D(160, 90, TextureFormat.Alpha8, false)
    }

    void YourFunction()
    {
    ...
    Graphics.Blit(webCamTexture, rt, lumMaterial);
    lumTexture.ReadPixels(rt, new Rect(0, 0, 160, 90), 0, 0, false);
    Color32[] lum = lumTexture.GetPixels32(); // lum is stored in the alpha
    ...
    }

    The material needs to be a simple vert frag shader that sets the output alpha to the luminosity. This page has a good shader: http://www.alanzucconi.com/2015/07/08/screen-shaders-and-postprocessing-effects-in-unity3d/
    Just change line 25 to something like result = lum.rrrr;
     
  7. eco_bach

    eco_bach

    Joined:
    Jul 8, 2013
    Posts:
    1,601
    bgolus, thanks!