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的像素识别/统计操作的更多相关文章

  1. 一个小 Trick

    平方变两次 一个状态 \(S\) 有一个贡献,所有状态 \(S\) 组成集合 \(U\) . 然后我们要统计下面这个东西 \[ans=\sum_{S\in U}f^2(S) \] 然后我们就可以看作是 ...

  2. Python2 下 Unicode 的一个小bug

    关于Python的编码问题已经是老生常谈了,此处主要是介绍一个罕见的问题,也算是Python2的一个bug了(Python3不会有此问题). 在有时候我们去爬取网页或者调用一些第三方库获取文本的时候, ...

  3. 教你用webgl快速创建一个小世界

    收录待用,修改转载已取得腾讯云授权 作者:TAT.vorshen Webgl的魅力在于可以创造一个自己的3D世界,但相比较canvas2D来说,除了物体的移动旋转变换完全依赖矩阵增加了复杂度,就连生成 ...

  4. 一些计数小Trick

    一些计数小Trick 虽然说计数问题如果不是特别傻逼的话想做出来基本随缘. 但是掌握一些基本的计数方法还是十分有必要的. 想到了就更新. 1. 对于排列的DP问题,一般是不能够按照位置一个一个放的,一 ...

  5. 语法上的小trick

    语法上的小trick 构造函数 虽然不写构造函数也是可以的,但是可能会开翻车,所以还是写上吧.: 提供三种写法: ​ 使用的时候只用: 注意,这里的A[i]=gg(3,3,3)的"gg&qu ...

  6. file_put_contens小trick

    file_put_contents tricks 0x01 trick1 来自于P神的实例: <?php $text = $_GET['text']; if(preg_match('[<& ...

  7. 像素 PIXEL 图片的基本单位 像素非常小 图片是成千上万的像素组成 显示/屏幕分辨率 (DPI 屏幕分辨率)

    像素 PIXEL 图片的基本单位 像素非常小 图片是成千上万的像素组成 显示/屏幕分辨率 (DPI 屏幕分辨率) 图像分辨率 (PPI) 1920*1080是像素点长度1920个像素点 X1080个像 ...

  8. Python multiprocessing 基础使用和小trick

    最近进行数据预处理时(噪声插入),单进程严重影响实验周期,故学习了multiprocessing并发执行不同数据集的处理,加快执行效率.现于此进行一些简单记录以供日后参考. 1. 基础: From m ...

  9. Redola.Rpc 的一个小目标

    Redola.Rpc 的一个小目标 Redola.Rpc 的一个小目标:20000 tps. Concurrency level: 8 threads Complete requests: 20000 ...

随机推荐

  1. 001.CDN概述

    一 互联网应用质量概述 1.1 互联网应用质量 互联网应用质量指标--QoE,其主要指标: 服务成功率:指用户所请求的服务成功完成的几率. 服务建立时间:指从服务请求到服务呈现所花费的时间,并且会因为 ...

  2. Spring Boot 静态资源访问原理解析

    一.前言 springboot配置静态资源方式是多种多样,接下来我会介绍其中几种方式,并解析一下其中的原理. 二.使用properties属性进行配置 应该说 spring.mvc.static-pa ...

  3. TensorFlow下利用MNIST训练模型并识别自己手写的数字

    最近一直在学习李宏毅老师的机器学习视频教程,学到和神经网络那一块知识的时候,我觉得单纯的学习理论知识过于枯燥,就想着自己动手实现一些简单的Demo,毕竟实践是检验真理的唯一标准!!!但是网上很多的与t ...

  4. sql关于对一个字段同时满足多条件判断来筛选查询

    表所有数据 查询userName为abc或xyz的 以下为本菜鸟项目中遇到的问题: 背景: /**  * wangjie 180629  *   * 学生需要查询四种可能的消息  * 1.班级管理员发 ...

  5. ES6中bind(this)用法说明

    在使用 React 中的 Class extends写法,如果 onClick 绑定一个方法就需要 bind(this),如果使用React.createClass 方法就不需要 解析: React. ...

  6. BZOJ2255 : [Swerc2010]Palindromic DNA

    考虑2-SAT建图,设$a[i][0..1]$表示$i$变不变,$b[i][0..1]$表示$i$是下降还是上升. 首先相邻的不能同时动,说明$a[i]$和$a[i+1]$里最多选一个. 对于$x$和 ...

  7. [POJ]代码托运站

    这里暂时是空的

  8. DP专题:划分数问题

    一.这个专题有什么用 练练DP 练练组合数学 ...... 二.正题 此类问题有如下几种形态: 1. 将n划分成若干正整数之和的划分数.2. 将n划分成k个正整数之和的划分数.3. 将n划分成最大数不 ...

  9. Eclipse 设置Web测试的浏览器

    Window->Preferences->General->Web Browser->选择Use external web browser->选择Default syst ...

  10. cout.setf()

    cout用来实现格式输出,类似于C语言中通过printf(). cout.setf()的作用是通过设置格式标志来控制输出形式,如,其中ios_base::fixed表示:用正常的记数方法显示浮点数(与 ...