通过一个小Trick实现shader的像素识别/统计操作
2018/12/14日补充:后来发现compute shader里用AppendStructuredBuffer可以解决这类问题,请看这里:https://www.cnblogs.com/hont/p/10122129.html
1.简介
在日常开发中会遇到诸如判断某张图的某颜色像素百分比占多少的问题,由于gpu运算并行的原因并不能对其进行累加操作。网上一些针对此类问题
的做法是将一张大图分成多个小块逐步处理并逐步合并,保留关键像素的向下采样:
但我在思考一种更简便的方法,于是想到在顶点shader里做判断检测,在像素shader里获取结果这样一个形式:
用一组顶点去读单个像素,判断失败的顶点坐标提交到屏幕外,而判断成功的顶点坐标放在屏幕内。
最后在CPU中获取是否有屏幕内顶点这样一个结果,来进行简单的识别操作。
而在开启透明之后,还可以用透明度叠加来获取更复杂的结果。
2.实践
首先实践结果并没有想象的那么好,因为如果纯用三角面来做顶点部分的判断未免太费效率了。
所以我改成了传入顶点判断并生成面的方式,并且缩小了传入图片的像素大小。
Graphics.DrawProcedural(MeshTopology.Points, blueTex.width * blueTex.height, );
毕竟更多的运用场合是用来做刮刮卡或者擦除的识别。只需要检测mask图片。
上代码:
Shader "Hidden/FooShader"
{
Properties
{
}
SubShader
{
Blend One One tags
{
"Queue" = "Transparent"
"RenderType" = "Transparent"
} Pass
{
CGPROGRAM
#pragma target 4.0
#pragma vertex vert
#pragma geometry geom
#pragma fragment frag #include "UnityCG.cginc" struct v2f
{
float4 color : COLOR;
float4 vertex : SV_POSITION;
}; sampler2D _Image;
float4 _ImageSize; v2f vert(uint vid : SV_VertexID)
{
v2f o = (v2f); half y = floor(vid / _ImageSize.x);
half x = (vid - y * _ImageSize.x) / _ImageSize.x;
y = y / _ImageSize.y; o.vertex = ; float4 image_col = tex2Dlod(_Image, half4(x,y,,)); if (all(image_col.rgb == half3(, , )))
//if (all(image_col.rgb == half3(0, 1, 1))) /*error*/
{
o.color = ;
}
else
{
o.color = ;
} return o;
} [maxvertexcount()]
void geom(point v2f vertElement[], inout TriangleStream<v2f> triStream)
{
if (vertElement[].color.r <= ) return; float size = ; float4 v1 = vertElement[].vertex + float4(-size, -size, , );
float4 v2 = vertElement[].vertex + float4(-size, size, , );
float4 v3 = vertElement[].vertex + float4(size, -size, , );
float4 v4 = vertElement[].vertex + float4(size, size, , ); v2f r = (v2f); r.vertex = mul(UNITY_MATRIX_VP, v1);
r.color = vertElement[].color;
triStream.Append(r); r.vertex = mul(UNITY_MATRIX_VP, v2);
r.color = vertElement[].color;
triStream.Append(r); r.vertex = mul(UNITY_MATRIX_VP, v3);
r.color = vertElement[].color;
triStream.Append(r); r.vertex = mul(UNITY_MATRIX_VP, v4);
r.color = vertElement[].color;
triStream.Append(r);
} fixed4 frag(v2f i) : SV_Target
{
return i.color;
}
ENDCG
}
}
}
FooShader.shader
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering; namespace Hont
{
public class Foo : MonoBehaviour
{
void Start()
{
var blueTex = new Texture2D(, );
for (int x = ; x < blueTex.width; x++)
for (int y = ; y < blueTex.height; y++)
blueTex.SetPixel(x, y, Color.blue);
blueTex.Apply(); var mat = new Material(Shader.Find("Hidden/FooShader"));
mat.SetTexture("_Image", blueTex);
mat.SetVector("_ImageSize", new Vector4(blueTex.width, blueTex.height));
mat.SetPass();
var tempRT = RenderTexture.GetTemporary(, , , RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB, );
tempRT.filterMode = FilterMode.Point;
tempRT.autoGenerateMips = false;
tempRT.anisoLevel = ;
tempRT.wrapMode = TextureWrapMode.Clamp;
var cacheRT = RenderTexture.active;
RenderTexture.active = tempRT;
Graphics.DrawProcedural(MeshTopology.Points, blueTex.width * blueTex.height, );
var tex2D = new Texture2D(, , TextureFormat.ARGB32, false, false);
tex2D.wrapMode = TextureWrapMode.Clamp;
tex2D.anisoLevel = ;
tex2D.filterMode = FilterMode.Point;
tex2D.ReadPixels(new Rect(, , , ), , );
var firstPixel = tex2D.GetPixel(, );
Debug.Log("firstPixel: " + firstPixel);
RenderTexture.active = cacheRT;
RenderTexture.ReleaseTemporary(tempRT);
}
}
}
Foo.cs
跑了一下代码之后我发现了三个问题,也是没解决的问题,一个是计算结果有误差
o.color = float4(0.05, , , );
输出是0.05结果却有一些出入。
特别是当返回颜色小于0.1之后,我尝试改变图像格式或者RT等参数依旧没能解决
第二个问题是开启透明后,透明图片的叠加是有上限的,毕竟深度有限,堆叠二十多层后,后面层会丢失。
第三个问题是传入图片尺寸过大直接导致带宽爆炸,以至于unity直接假死了,512x512的图片就是26万多的像素要处理,也就是26万多的顶点。
第三个问题很好解决,控制图片尺寸+让单个顶点采样更多像素即可。
对于第一个问题,目前还不需要太精确所以没解决但也能用。第二个问题可以用一些方法来缓解
比如在顶点shader中增加运算量,把返回值分散到rgba四个通道上去。
uint roll = (roll_width + roll_height) % ; if (roll == )
result = float4(GAIN_VALUE, , , ); if (roll == )
result = float4(, GAIN_VALUE, , ); if (roll == )
result = float4(, , GAIN_VALUE, ); if (roll == )
result = float4(, , , GAIN_VALUE);
把更多的像素遍历放入顶点中,这样处理图片的顶点数量是原大小/n:
v2f vert(uint vid : SV_VertexID)
{
v2f o = (v2f); o.vertex = ; half2 image_size = half2(GRID_SIZE_X * LOOP_IMAGE_SIZE_X, GRID_SIZE_Y * LOOP_IMAGE_SIZE_Y); half y = floor(vid / LOOP_IMAGE_SIZE_X);
half x = (vid - y * LOOP_IMAGE_SIZE_X) / LOOP_IMAGE_SIZE_X;
y = y / LOOP_IMAGE_SIZE_Y;
//将vid转化为x,y坐标 for (half rx = ; rx < GRID_SIZE_X; rx++)
{
for (half ry = ; ry < GRID_SIZE_Y; ry++)
{
half xx = x + rx;
half yy = y + ry; float4 r = Statistics_sample(_Image, _Rec_Color, half4(xx, yy, , ), image_size); o.color += r;
}
}
//一个顶点处理多个像素 return o;
}
3.测试结果
最终达到了一个比较不错的结果,我把相关函数封装成了一个类。
我写了一个涂抹效果demo来测试一下,它通过识别白色像素的数量来判断是否为全部涂完:
工程文件我丢在了github上: https://github.com/hont127/Image-Rec-Base-unity-shader-
通过这个小Trick其实可以在像素里返回更多的信息,简单的场合这么还是比较方便的,当然一些复杂的情况分块或者配合computer shader来做其实更合适。
通过一个小Trick实现shader的像素识别/统计操作的更多相关文章
- 一个小 Trick
平方变两次 一个状态 \(S\) 有一个贡献,所有状态 \(S\) 组成集合 \(U\) . 然后我们要统计下面这个东西 \[ans=\sum_{S\in U}f^2(S) \] 然后我们就可以看作是 ...
- Python2 下 Unicode 的一个小bug
关于Python的编码问题已经是老生常谈了,此处主要是介绍一个罕见的问题,也算是Python2的一个bug了(Python3不会有此问题). 在有时候我们去爬取网页或者调用一些第三方库获取文本的时候, ...
- 教你用webgl快速创建一个小世界
收录待用,修改转载已取得腾讯云授权 作者:TAT.vorshen Webgl的魅力在于可以创造一个自己的3D世界,但相比较canvas2D来说,除了物体的移动旋转变换完全依赖矩阵增加了复杂度,就连生成 ...
- 一些计数小Trick
一些计数小Trick 虽然说计数问题如果不是特别傻逼的话想做出来基本随缘. 但是掌握一些基本的计数方法还是十分有必要的. 想到了就更新. 1. 对于排列的DP问题,一般是不能够按照位置一个一个放的,一 ...
- 语法上的小trick
语法上的小trick 构造函数 虽然不写构造函数也是可以的,但是可能会开翻车,所以还是写上吧.: 提供三种写法: 使用的时候只用: 注意,这里的A[i]=gg(3,3,3)的"gg&qu ...
- file_put_contens小trick
file_put_contents tricks 0x01 trick1 来自于P神的实例: <?php $text = $_GET['text']; if(preg_match('[<& ...
- 像素 PIXEL 图片的基本单位 像素非常小 图片是成千上万的像素组成 显示/屏幕分辨率 (DPI 屏幕分辨率)
像素 PIXEL 图片的基本单位 像素非常小 图片是成千上万的像素组成 显示/屏幕分辨率 (DPI 屏幕分辨率) 图像分辨率 (PPI) 1920*1080是像素点长度1920个像素点 X1080个像 ...
- Python multiprocessing 基础使用和小trick
最近进行数据预处理时(噪声插入),单进程严重影响实验周期,故学习了multiprocessing并发执行不同数据集的处理,加快执行效率.现于此进行一些简单记录以供日后参考. 1. 基础: From m ...
- Redola.Rpc 的一个小目标
Redola.Rpc 的一个小目标 Redola.Rpc 的一个小目标:20000 tps. Concurrency level: 8 threads Complete requests: 20000 ...
随机推荐
- spring_AOP
例子代码 理解AOP AOP为Aspect Oriented Programming的缩写,意为:面向切面编程.大概意思就是在原有源代码的基础上,增加功能,而又不修改原有的代码. 术语 切面(Aspe ...
- 1613. 最高频率的IP
Description Given a string[] lines, each line will have an ip address , find the ip address with the ...
- VBA中FIND方法的使用说明zz
Find 方法在区域中查找特定信息. 语法 表达式.Find(What, After, LookIn, LookAt, SearchOrder, SearchDirection, MatchCase, ...
- java 分布式与集群的区别和联系(转)
本文主要介绍了java分布式与集群的区别和联系,具有很好的参考价值,下面跟着小编一起来看下吧 一.先说区别: 一句话:分布式是并联工作的,集群是串联工作的. 1.分布式是指将不同的业务分布在不同的地方 ...
- Your ApplicationContext is unlikely to start due to a @ComponentScan of the default
问题:** WARNING ** : Your ApplicationContext is unlikely to start due to a @ComponentScan of the defau ...
- 【DWM1000】 code 解密4一 ANCHOR 二进宫testapprun_s
上面我们的代码分析到ANCHOR 调用了一次testapprun_s,但是后面退出后发现还是满足while 条件,逼不得已还得再次调用testapprun_s.testapprun_s 也就是这样一点 ...
- BZOJ4970 : [ioi2004]empodia 障碍段
通过两遍单调栈求出每个点作为最小值往右延伸到$g[i]$,作为最大值往左延伸到$f[i]$. 那么一个区间$[i,j]$可行当且仅当$g[i]\geq j$.$f[j]\leq i$且$i-a[i]= ...
- Moo University - Financial Aid [POJ2010] [堆]
题意: 在C头牛里选N头牛,每头牛需要花掉一定经费ai才能得到一定得bi分,在不超过经费F的情况下,使得N头牛的得分中位数最大.(1 <= N <= 19,999,奇数) (N <= ...
- Yii2 组件
组件的命名空间: 响应组件: yii\web\Response Yii预定义的HTTP异常组件: yii\web\BadRequestHttpException: //状态码 400. yii\web ...
- js加密php解密---jsencrypt
原理:javascript加密PHP解密: 完全依赖openssl: 一. openssl 是干嘛的 它集成了众多密码算法及实用工具 rsa加密流程:(今天只讲众多加密方式中的一种) 1. 在当前文件 ...