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, 1);

毕竟更多的运用场合是用来做刮刮卡或者擦除的识别。只需要检测mask图片。

上代码:

Shader "Hidden/FooShader"
{Properties{}SubShader{Blend One Onetags{"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)0;half y = floor(vid / _ImageSize.x);half x = (vid - y * _ImageSize.x) / _ImageSize.x;y = y / _ImageSize.y;o.vertex = 0;float4 image_col = tex2Dlod(_Image, half4(x,y,0,0));if (all(image_col.rgb == half3(0, 0, 1)))//if (all(image_col.rgb == half3(0, 1, 1)))    /*error*/
                {o.color = 1;}else{o.color = 0;}return o;}[maxvertexcount(4)]void geom(point v2f vertElement[1], inout TriangleStream<v2f> triStream){if (vertElement[0].color.r <= 0) return;float size = 10;float4 v1 = vertElement[0].vertex + float4(-size, -size, 0, 0);float4 v2 = vertElement[0].vertex + float4(-size, size, 0, 0);float4 v3 = vertElement[0].vertex + float4(size, -size, 0, 0);float4 v4 = vertElement[0].vertex + float4(size, size, 0, 0);v2f r = (v2f)0;r.vertex = mul(UNITY_MATRIX_VP, v1);r.color = vertElement[0].color;triStream.Append(r);r.vertex = mul(UNITY_MATRIX_VP, v2);r.color = vertElement[0].color;triStream.Append(r);r.vertex = mul(UNITY_MATRIX_VP, v3);r.color = vertElement[0].color;triStream.Append(r);r.vertex = mul(UNITY_MATRIX_VP, v4);r.color = vertElement[0].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(64, 64);for (int x = 0; x < blueTex.width; x++)for (int y = 0; 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(0);var tempRT = RenderTexture.GetTemporary(16, 16, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB, 1);tempRT.filterMode = FilterMode.Point;tempRT.autoGenerateMips = false;tempRT.anisoLevel = 0;tempRT.wrapMode = TextureWrapMode.Clamp;var cacheRT = RenderTexture.active;RenderTexture.active = tempRT;Graphics.DrawProcedural(MeshTopology.Points, blueTex.width * blueTex.height, 1);var tex2D = new Texture2D(16, 16, TextureFormat.ARGB32, false, false);tex2D.wrapMode = TextureWrapMode.Clamp;tex2D.anisoLevel = 0;tex2D.filterMode = FilterMode.Point;tex2D.ReadPixels(new Rect(0, 0, 16, 16), 0, 0);var firstPixel = tex2D.GetPixel(0, 0);Debug.Log("firstPixel: " + firstPixel);RenderTexture.active = cacheRT;RenderTexture.ReleaseTemporary(tempRT);}}
}

Foo.cs

跑了一下代码之后我发现了三个问题,也是没解决的问题,一个是计算结果有误差

o.color = float4(0.05, 0, 0, 0);

输出是0.05结果却有一些出入。

特别是当返回颜色小于0.1之后,我尝试改变图像格式或者RT等参数依旧没能解决

第二个问题是开启透明后,透明图片的叠加是有上限的,毕竟深度有限,堆叠二十多层后,后面层会丢失。

第三个问题是传入图片尺寸过大直接导致带宽爆炸,以至于unity直接假死了,512x512的图片就是26万多的像素要处理,也就是26万多的顶点。

第三个问题很好解决,控制图片尺寸+让单个顶点采样更多像素即可。

对于第一个问题,目前还不需要太精确所以没解决但也能用。第二个问题可以用一些方法来缓解

比如在顶点shader中增加运算量,把返回值分散到rgba四个通道上去。

uint roll = (roll_width + roll_height) % 4;if (roll == 0)result = float4(GAIN_VALUE, 0, 0, 0);if (roll == 1)result = float4(0, GAIN_VALUE, 0, 0);if (roll == 2)result = float4(0, 0, GAIN_VALUE, 0);if (roll == 3)result = float4(0, 0, 0, GAIN_VALUE);

把更多的像素遍历放入顶点中,这样处理图片的顶点数量是原大小/n:

v2f vert(uint vid : SV_VertexID)
{v2f o = (v2f)0;o.vertex = 0;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 = 0; rx < GRID_SIZE_X; rx++){for (half ry = 0; ry < GRID_SIZE_Y; ry++){half xx = x + rx;half yy = y + ry;float4 r = Statistics_sample(_Image, _Rec_Color, half4(xx, yy, 0, 0), image_size);o.color += r;}}//一个顶点处理多个像素return o;
}

3.测试结果

最终达到了一个比较不错的结果,我把相关函数封装成了一个类。

我写了一个涂抹效果demo来测试一下,它通过识别白色像素的数量来判断是否为全部涂完:

工程文件我丢在了github上: https://github.com/hont127/Image-Rec-Base-unity-shader-

通过这个小Trick其实可以在像素里返回更多的信息,简单的场合这么还是比较方便的,当然一些复杂的情况分块或者配合computer shader来做其实更合适。

通过一个小Trick实现shader的像素识别/统计操作相关推荐

  1. TCP BBR失速控制的一个小trick一个小patch

    昨晚凌晨抵达深圳,今早时间有限,于是就长话短说,但无论如何还是会有一些输出的,这次关于BBR.我曾经说过,我是不需要太多睡眠的,凌晨3点睡,照样早上6点起,应该可以把华为的人熬到cusi吧-不得而知了 ...

  2. 让Transformer的推理速度提高4.5倍,这个小trick还能给你省十几万

    点击上方"视学算法",选择加"星标"或"置顶" 重磅干货,第一时间送达 丰色 发自 凹非寺 量子位 报道 | 公众号 QbitAI 最近,N ...

  3. 如何做一个小程序商城,需要多少钱

    如何做一个小程序商城,需要多少钱? 操作像玩消消乐那么简单! 费用是一年1998元至3498元.免300元认证费.买两年用四年. 日均花费不到2元就能搭建一个小程序商城. 下面分享的是自建小程序商城的 ...

  4. python手机版做小游戏代码大全-Python大牛手把手教你做一个小游戏,萌新福利!...

    原标题:Python大牛手把手教你做一个小游戏,萌新福利! 引言 最近python语言大火,除了在科学计算领域python有用武之地之外,在游戏.后台等方面,python也大放异彩,本篇博文将按照正规 ...

  5. html class和id,css教程之样式表的基本语法(二) class(类)和id的一个小实例

    class(类)和id的一个小实例 在上一节中我们了解了如何为特定的标签定义样式,例如我们利用 "h1{font-size: 12px;}"将页面内所有的标题1的字体大小改为了12 ...

  6. Nicetools:定一个小目标,先做它个1000个工具

    http://www.nicetool.net/ ▲ 写的时候是879个工具,成稿的时候是886个,看来小目标不远了 测评工具虽迟但到.今天这个网站算是一个小集合,宣传语为用完即走,触手可及,意在说明 ...

  7. 位运算相关题目-一些小trick 1bit代表独立数字 求只出现一次的数字 无进位n进制数 n(-n) Boyer-Moore 投票算法 n(n-1)

    二进制位方法 集合的每个元素,都有可以选或不选,用二进制的位来表示,0表示不选,1表示选自.0x1 << nums.size()-1 的每一位就代表了集合中每个元素都选用.这里由于集合中每 ...

  8. 用 Python 做了一个小姐姐跳舞的词云视频

    关注公众号:[小张Python],为你准备了 50+ 本Python 精品电子书籍 与 50G + 优质视频学习资料,后台回复关键字:1024 即可获取:如果对博文内容有什么疑问,公号后台添加作者[个 ...

  9. 浅谈CTF中各种花式绕过的小trick

    文章目录 浅谈CTF中各种花式绕过的小trick 前言 md5加密bypass 弱比较绕过 方法一:0e绕过 方法二:数组绕过 强比较绕过 方法:数组绕过 md5碰撞绕过 方法:使用Fastcoll生 ...

最新文章

  1. python三十八:re模块
  2. Tensorflow修改张量特定位置元素的值
  3. jquery 设置style:display 其实很方便的哦
  4. 牛客网软件测试机考题库,为什么编程题本地测试通过,可是提交时却总报错。求大神们指点。...
  5. 邱锡鹏,这是Transformer最全综述
  6. 华为5102路由器虚拟服务器,华为路由WS5102怎么设置wifi中继功能
  7. 【海康威视】WPF客户端二次开发:【5】Chrome浏览器调用客户端程序 链接参数处理 —— 前端encodeURIComponent编码,后端UrlDecode解码
  8. Linux:进程间的相互作用(模拟两个进程,一个存钱,另一个取钱),进程共享内存,进程的互斥,进程加锁,c++和c实现
  9. Android人脸检测功能和检测特效
  10. 大学生无线耳机怎么选?内行推荐四款高性价比蓝牙耳机
  11. 二十九岁,刚读完了财富启蒙读物《小狗钱钱》
  12. RF信号下采样/矩阵下采样(附python实现代码)
  13. 如何制定客户留存策略_14个成功的客户留存策略举例
  14. 微信网页端软键盘收起点击事件失效
  15. css3 火焰文字,Css3字体做出火焰效果的实现步骤
  16. Proxifier使用教程
  17. 使用getElementsByTagName()和namedItem()获取特定元素
  18. 什么计算机玩游戏好,电脑玩游戏什么配置好
  19. vb获取服务器文件路径,vb打开ftp服务器文件路径
  20. 联想预装linux,联想ThinkPad开始预装Linux

热门文章

  1. python学习总结----异常处理
  2. 堆栈应用(三):火车车厢重排
  3. 怎样追求心仪的女孩子
  4. 条码扫描二维码扫描——ZXing android 源码简化
  5. 【C/C++】C++98基础上的C++11新特性
  6. docker安装tomcat下的日志查看
  7. 在线JWT Token解析解码
  8. Hbase+Phoenix使用总结
  9. php开发面试题---面试常用英语(你能介绍你自己吗?)
  10. Sql Server2005 Transact-SQL 新兵器学习总结之-PIVOT和UNPIVOT运算符