您的位置:首页 > 移动开发

涂涂乐 通过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
}
}
}
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);
}
}


 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: