涂涂乐 通过shader实现目标区域正交图片提取,移动端可用
2017-09-28 16:16
645 查看
其实目的是从相机得到的透视图片中,得到目标图片的正交效果图。通过高通插件
和 shader实现透视变换(其实就是为了获得一张正交图片),也有多种实现方式,有些只能在pc端使用,这里只介绍一种可以在pc和移动端都正常实现的方式。
实现思路:
1、首先要有一个shader处理脚本传入的原始图片四个顶底坐标,在vert方法中通过UV坐标调整和差值运算得到目标位置,再通过一世界坐标-》裁剪坐标-》屏幕坐标的变换,最终得到视口坐标内的uv信息,在从frag方法中通过vert计算出来的uv信息从原始图片(相机得到的图片)获取像素,
从而获取识别图中所需区域的正交图。具体算法见 AdjustTextureShader 脚本。在项目中创建一个材质球命名为AdjustTextureMaterial,shader选择为
AdjustTextureShader 。
2、通过在ImageTarget上挂四个空物体,位置拖放到需要截取区域的对应四个角,通过脚本传给shader作为uv变换的原始参数。
3、写一个脚本,用来传递ImageTarget上的四个位置信息给shader做处理。具体见GetTextureByShader脚本。
4、创建一个Plane 命名为
TargetObj,翻转x轴翻转90度 调整合适的大小,因为需要整个画面固定在屏幕上固定位置,所以把这个TargetObj挂在ar相机下,并把ar相机的world
center设置为camera。
5、给4步骤创建的TargetObj的材质球换成步骤1创建的材质球,挂上步骤3写的脚本,把ImageTarget下的四个顶点位置信息拖入脚本暴露出的对应接口中。
6、在ImageTarget的脚本DefaultTrackableEventHandler脚本中添加一个公共变量TargetObj,用于相机识别状态改变的时候
显示和关闭这个物体,把步骤4创建的物体,拖入该公共变量。
Shader "Custom/AdjustTextureShader" {
Properties{
_MainTex("Base (RGB)", 2D) = "white" {}
_UvpointTL("point1", Vector) = (0 , 0 , 0 , 0)
_UvpointBL("point2", Vector) = (0 , 0 , 0 , 0)
_UvpointTR("point3", Vector) = (0 , 0 , 0 , 0)
_UvpointBR("point4", Vector) = (0 , 0 , 0 , 0)
}
SubShader{
Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" }
LOD 200
Pass{
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
//_MainTex_ST.xy存储的是缩放,_MainTex_ST.zw存储的是偏移
float4 _MainTex_ST;
float4 _UvpointTL;
float4 _UvpointBL;
float4 _UvpointTR;
float4 _UvpointBR;
struct v2f {
float4 pos : SV_POSITION;//裁剪空间的顶点坐标
float2 uv : TEXCOORD0;//第一组纹理坐标
float4 fixedPos : TEXCOORD2;//第三组纹理坐标
};
/*
默认顶点坐标参数结构体
struct appdata_base {
float4 vertex : POSITION;//位置
float3 normal : NORMAL;//法线
float4 texcoord : TEXCOORD0;//纹理坐标
};
*/
v2f vert(appdata_base v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
o.uv.x = o.uv.x;
o.uv.y = 1 - o.uv.y;
float4 top = lerp(_UvpointTL, _UvpointTR, o.uv.x);
float4 bottom = lerp(_UvpointBL, _UvpointBR, o.uv.x);
float4 fixedPos = lerp(bottom, top, o.uv.y);
//UNITY_MATRIX_VP:用于将顶点/矢量从世界空间变换到裁剪空间
//ComputeScreenPos:裁剪空间坐标转换为屏幕空间坐标
o.fixedPos = ComputeScreenPos(mul(UNITY_MATRIX_VP, fixedPos));
return o;
}
float4 frag(v2f i) : COLOR
{
//i.fixedPos.xy / i.fixedPos.w得到视口空间中的坐标,视口坐标范围[(0,0),(1,1)]
//i.fixedPos是屏幕坐标,齐次除法后得到视口坐标
return tex2D(_MainTex, i.fixedPos.xy / i.fixedPos.w);
}
ENDCG
}
}
}
和 shader实现透视变换(其实就是为了获得一张正交图片),也有多种实现方式,有些只能在pc端使用,这里只介绍一种可以在pc和移动端都正常实现的方式。
实现思路:
1、首先要有一个shader处理脚本传入的原始图片四个顶底坐标,在vert方法中通过UV坐标调整和差值运算得到目标位置,再通过一世界坐标-》裁剪坐标-》屏幕坐标的变换,最终得到视口坐标内的uv信息,在从frag方法中通过vert计算出来的uv信息从原始图片(相机得到的图片)获取像素,
从而获取识别图中所需区域的正交图。具体算法见 AdjustTextureShader 脚本。在项目中创建一个材质球命名为AdjustTextureMaterial,shader选择为
AdjustTextureShader 。
2、通过在ImageTarget上挂四个空物体,位置拖放到需要截取区域的对应四个角,通过脚本传给shader作为uv变换的原始参数。
3、写一个脚本,用来传递ImageTarget上的四个位置信息给shader做处理。具体见GetTextureByShader脚本。
4、创建一个Plane 命名为
TargetObj,翻转x轴翻转90度 调整合适的大小,因为需要整个画面固定在屏幕上固定位置,所以把这个TargetObj挂在ar相机下,并把ar相机的world
center设置为camera。
5、给4步骤创建的TargetObj的材质球换成步骤1创建的材质球,挂上步骤3写的脚本,把ImageTarget下的四个顶点位置信息拖入脚本暴露出的对应接口中。
6、在ImageTarget的脚本DefaultTrackableEventHandler脚本中添加一个公共变量TargetObj,用于相机识别状态改变的时候
显示和关闭这个物体,把步骤4创建的物体,拖入该公共变量。
Shader "Custom/AdjustTextureShader" {
Properties{
_MainTex("Base (RGB)", 2D) = "white" {}
_UvpointTL("point1", Vector) = (0 , 0 , 0 , 0)
_UvpointBL("point2", Vector) = (0 , 0 , 0 , 0)
_UvpointTR("point3", Vector) = (0 , 0 , 0 , 0)
_UvpointBR("point4", Vector) = (0 , 0 , 0 , 0)
}
SubShader{
Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" }
LOD 200
Pass{
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
//_MainTex_ST.xy存储的是缩放,_MainTex_ST.zw存储的是偏移
float4 _MainTex_ST;
float4 _UvpointTL;
float4 _UvpointBL;
float4 _UvpointTR;
float4 _UvpointBR;
struct v2f {
float4 pos : SV_POSITION;//裁剪空间的顶点坐标
float2 uv : TEXCOORD0;//第一组纹理坐标
float4 fixedPos : TEXCOORD2;//第三组纹理坐标
};
/*
默认顶点坐标参数结构体
struct appdata_base {
float4 vertex : POSITION;//位置
float3 normal : NORMAL;//法线
float4 texcoord : TEXCOORD0;//纹理坐标
};
*/
v2f vert(appdata_base v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
o.uv.x = o.uv.x;
o.uv.y = 1 - o.uv.y;
float4 top = lerp(_UvpointTL, _UvpointTR, o.uv.x);
float4 bottom = lerp(_UvpointBL, _UvpointBR, o.uv.x);
float4 fixedPos = lerp(bottom, top, o.uv.y);
//UNITY_MATRIX_VP:用于将顶点/矢量从世界空间变换到裁剪空间
//ComputeScreenPos:裁剪空间坐标转换为屏幕空间坐标
o.fixedPos = ComputeScreenPos(mul(UNITY_MATRIX_VP, fixedPos));
return o;
}
float4 frag(v2f i) : COLOR
{
//i.fixedPos.xy / i.fixedPos.w得到视口空间中的坐标,视口坐标范围[(0,0),(1,1)]
//i.fixedPos是屏幕坐标,齐次除法后得到视口坐标
return tex2D(_MainTex, i.fixedPos.xy / i.fixedPos.w);
}
ENDCG
}
}
}
using UnityEngine; using Vuforia; public class GetTextureByShader : MonoBehaviour { public Transform posTL; public Transform posTR; public Transform posBL; public Transform posBR; Camera cam; private RenderTexture renderTexture; ImageTargetBehaviour targetBehaviour; void Start() { targetBehaviour = GameObject.Find("ImageTarget").GetComponent<ImageTargetBehaviour>(); //GetComponentInParent<ImageTargetBehaviour>(); gameObject.layer = 31; } void Renderprepare() { if (!cam) { GameObject go = new GameObject("__cam"); cam = go.AddComponent<Camera>(); go.transform.parent = transform.parent; cam.hideFlags = HideFlags.HideAndDontSave; } cam.CopyFrom(Camera.main); cam.depth = 0; cam.cullingMask = 31; if (!renderTexture) { renderTexture = new RenderTexture(Screen.width, Screen.height,-50); } cam.targetTexture = renderTexture; cam.Render(); GetComponent<Renderer>().material.SetTexture("_MainTex", renderTexture); } void OnWillRenderObject() { if (!targetBehaviour || targetBehaviour.ImageTarget == null) return; Vector3 targetAnglePointTL = posTL.position; Vector3 targetAnglePointBL = posBL.position; Vector3 targetAnglePointTR = posTR.position; Vector3 targetAnglePointBR = posBR.position; Renderprepare(); //设置Shader参数 GetComponent<Renderer>().material.SetVector("_UvpointTL", new Vector4(targetAnglePointTL.x, targetAnglePointTL.y, targetAnglePointTL.z, 1f)); GetComponent<Renderer>().material.SetVector("_UvpointBL", new Vector4(targetAnglePointBL.x, targetAnglePointBL.y, targetAnglePointBL.z, 1f)); GetComponent<Renderer>().material.SetVector("_UvpointTR", new Vector4(targetAnglePointTR.x, targetAnglePointTR.y, targetAnglePointTR.z, 1f)); GetComponent<Renderer>().material.SetVector("_UvpointBR", new Vector4(targetAnglePointBR.x, targetAnglePointBR.y, targetAnglePointBR.z, 1f)); } void OnDestroy() { if (renderTexture) DestroyImmediate(renderTexture); if (cam) DestroyImmediate(cam.gameObject); } }
相关文章推荐
- Android学习研究(五)通过BitmapShader实现圆形图片
- C#通过正则表达式实现提取网页中的图片
- 【Unity】用Shader实现图片的区域遮罩,支持半透明,实现地图动态上色功能
- iphone中模糊图片的指定区域的实现
- Android 自定义View 使用ShapeDrawable加BitmapShader实现图片局部放大效果
- (可视区域加载)图片懒加载实现原理
- Android BitmapShader 实战 实现圆形、圆角图片
- 用Python实现通过哈希算法检测图片重复的教程
- 通过使用GDI+和CImage类配合实现针对目标窗体的子控件进行截图
- Asp.net MVC防止图片盗链的实现方法,通过自定义RouteHandler来操作
- 快速实现JS图片懒加载(可视区域加载)示例代码
- HTML5 JavaScript实现图片文字识别与提取
- 通过heartbeat 2.X 实现高可用lamp
- SilvetLight 实现的一个上传图片时功能动态裁剪部分区域和缩放图片的功能
- AIX通过HACMP实现 WebSphere Message Broker V6.1 的高可用配置
- PHP实现对图片的反色处理功能【测试可用】
- Android通过ScaleGestureDetector实现图片缩放
- [PHP]移动端网页如何使用JqueryMobile+PHP实现上传图片的功能
- 通过Robocopy+DOS 命令+Windows排程实现自动备份(将特定文件/目录备份至自动创建的以年月日命名的目标目录)
- Android 通过软引用实现图片缓存,防止内存溢出