【发布时间】:2016-10-29 07:07:03
【问题描述】:
我有一个 RenderTexture,需要计算它的平均和最大像素值。 RenderTexture 是 RFloat 格式,所以只有一个组件。而且它现在不使用 mipmap。
【问题讨论】:
我有一个 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,512,256,... 等等,直到 1 x 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计算1024的2的幂,mat是shader的材质。
对于平均而言,可以对平均着色器而不是 max 执行相同的操作,或者只是为渲染纹理启用 mipmap 并读取最高的 mipmap 级别。
【讨论】: