需要shader中纹理的平均值和最大值



我有一个RenderTexture,需要计算它的平均和最大像素值。RenderTexture是RFloat格式,所以只有一个组件。而且它现在不使用mipmap

经过一番搜索,似乎最好的方法是减少纹理,这意味着运行一个着色器,其输入是纹理大小,输出是输入大小的一半。我们可以对4个相邻像素进行采样,并从中计算最大值,并将值写入输出。

    sampler2D _MainTex;
    float4 _MainTex_ST;
    float4 _MainTex_TexelSize;
    v2f vert(appdata v)
    {
        v2f o;
        o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
        o.uv = TRANSFORM_TEX(v.uv, _MainTex);
        return o;
    }
    float4 frag (v2f i) : SV_Target
    {
        float dx = _MainTex_TexelSize.x*0.5;
        float maximum = tex2D(_MainTex, i.uv + float2(-dx,-dx));
        maximum = max(maximum, tex2D(_MainTex, i.uv + float2(dx,-dx)));
        maximum = max(maximum, tex2D(_MainTex, i.uv + float2(-dx,dx)));
        maximum = max(maximum, tex2D(_MainTex, i.uv + float2(dx,dx)));
        return maximum;
    }

下一个迭代获得前一个迭代的输出,并写入一个渲染纹理,它是其输入的一半,意思是原始纹理的四分之一。所以大小是1024 512256…以此类推,直到1 × 1的纹理这就给出了最大值。下面是一些显示CPU端的代码:

        private RenderTexture Reduce(RenderTexture diff, Material mat)
    {
        int reductions = POT.GetPowerFromNum(diff.width);
        var rtList = new List<RenderTexture>();
        int smallRes = diff.width/2;
        var bigRT = diff;
        for (int i = 0; i < reductions; i++)
        {
            var smallRT = RenderTexture.GetTemporary(smallRes, smallRes, 0, diff.format);
            rtList.Add(smallRT);
            GpuUtils.Blit(bigRT,smallRT,mat);
            bigRT = smallRT;
            smallRes >>= 1;
        }
        for (int i = 0; i < reductions-1; i++)
        {
            RenderTexture.ReleaseTemporary(rtList[i]);
        }
        return rtList[reductions-1];
    }

这里diff是原始渲染纹理,GetPowerFromNum计算2的幂,如101024,而mat是着色器的材质。

对于平均着色器,您可以对平均着色器执行相同的操作,而不是max或简单地为渲染纹理启用mipmaps并读取最高的mipmap级别。

最新更新