我有一个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级别。