Unity3D Shader示例之—AR涂涂乐实现方法
Unity3D Shader示例之—AR涂涂乐项目实战 在上一篇中分析了涂涂乐的实现原理,下面来分析一下具体的实现方法。 动态的提取摄像机获取的图像颜色应用于模型就需要着色器来实现了,
//着色器命名,在编程器右键菜单中除了展示其他着色器,还会展示Samples>TextureSample
Shader "Samples/TextureSample" {
//属性定义,会在编辑器中显示
Properties {
//\[内部名称\](\[编辑器显示名\],类型) = 默认值
_MainTex("Base (RGB)", 2D) = "white" {}
_Uvpoint1("point1", Vector) = (0 , 0 , 0 , 0)
_Uvpoint2("point2", Vector) = (0 , 0 , 0 , 0)
_Uvpoint3("point3", Vector) = (0 , 0 , 0 , 0)
_Uvpoint4("point4", Vector) = (0 , 0 , 0 , 0)
}
SubShader {
//Queue 渲染队列,控制渲染的顺序,在渲染透明模型时需要注意
//"RenderType"="Transparent"渲染透明类型,控制渲染的类型
Tags { "Queue"="Transparent" "RenderType"="Transparent" }
//Level of Detail 为200
LOD 200
Pass{
//开启混合,并设置混合因子,源颜色(该片元产生的颜色)会乘以SrcFactor,而目标颜色(已经存在于颜色缓冲区的颜色)会乘以DstFactor,然后把两者相加后再存入颜色缓冲区
//混合后的颜色:DstColor = SrcAlpha x SrcColor + (1 - SrcAlpha) x DstColor
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 _Uvpoint1;
float4 _Uvpoint2;
float4 _Uvpoint3;
float4 _Uvpoint4;
//vertex to fragment 顶点传到片段的结构定义
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;
//UNITY\_MATRIX\_MVP:当前模型*观察*投影矩阵
//把顶点坐标转换到齐次裁剪坐标系下
//用于将顶点/方向矢量从模型空间变换到裁剪空间
o.pos = mul(UNITY\_MATRIX\_MVP,v.vertex);
//拿顶点的uv去和材质球的tiling和offset作运算,确保材质球里的缩放和偏移设置是正确的(v.texcoord就是模型顶点的uv)
//为给定的纹理计算其uv坐标,即根据mesh上的uv坐标(v.texcoord)来计算真正的纹理上对应的位置
//等同于 o.uv = v.texcoord.xy *\_MainTex\_ST.xy + \_MainTex\_ST.zw;
//对顶点纹理进行运算得到最终的纹理坐标
o.uv = TRANSFORM\_TEX(v.texcoord,\_MainTex);
//lerp(a,b,f)==(1-f)\*a + b\*f = a + (b-a)*f
//获取修正后的坐标
//根据当前的uv坐标获取在x方向和y方向的线性插值
float4 top = lerp(\_Uvpoint1, \_Uvpoint3, o.uv.x);
float4 bottom = lerp(\_Uvpoint2, \_Uvpoint4, o.uv.x);
//fixedPos为世界坐标
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是屏幕坐标,齐次除法后得到视口坐标
//tex2D 二维纹理查询 在一张贴图中对一个点进行采样的方法,返回一个float4
return tex2D(_MainTex, i.fixedPos.xy / i.fixedPos.w);
}
ENDCG
}
}
//FallBack "Diffuse"
}
在上面的着色器代码中,定义了一个纹理输入属性和和四个矢量属性。着色通过四个位置定义获取纹理的颜色采样。将此着色器应用于将要展示的涂涂乐模型。 着色器的输入就需要通过程序来动态提供了:
using UnityEngine;
using Vuforia;
public class Coloring3DBehaviour : MonoBehaviour
{
Camera cam;
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;
//一半的大小是(0.5,0.3)
//识别图大小是(512,326)
//targetBehaviour.GetSize()是ImageTarget在Unity场景的中单位(Unity scene units)大小(1,0.6)
//Vuforia默认的单位是米
Vector2 halfSize = targetBehaviour.GetSize() * 0.5f;
//获取世界坐标,识别图的4个拐角世界坐标
//图片(模型)坐标原点在中心点,因是二维的所以y为0,通过一半的大小参数得到4个角的局部坐标
Vector3 targetAnglePoint1 = transform.parent.TransformPoint(new Vector3(-halfSize.x, 0, halfSize.y));
Vector3 targetAnglePoint2 = transform.parent.TransformPoint(new Vector3(-halfSize.x, 0, -halfSize.y));
Vector3 targetAnglePoint3 = transform.parent.TransformPoint(new Vector3(halfSize.x, 0, halfSize.y));
Vector3 targetAnglePoint4 = transform.parent.TransformPoint(new Vector3(halfSize.x, 0, -halfSize.y));
Renderprepare();
//设置Shader参数
GetComponent<Renderer>().material.SetVector("_Uvpoint1", new Vector4(targetAnglePoint1.x, targetAnglePoint1.y, targetAnglePoint1.z, 1f));
GetComponent<Renderer>().material.SetVector("_Uvpoint2", new Vector4(targetAnglePoint2.x, targetAnglePoint2.y, targetAnglePoint2.z, 1f));
GetComponent<Renderer>().material.SetVector("_Uvpoint3", new Vector4(targetAnglePoint3.x, targetAnglePoint3.y, targetAnglePoint3.z, 1f));
GetComponent<Renderer>().material.SetVector("_Uvpoint4", new Vector4(targetAnglePoint4.x, targetAnglePoint4.y, targetAnglePoint4.z, 1f));
}
void OnDestroy()
{
if (renderTexture)
DestroyImmediate(renderTexture);
if (cam)
DestroyImmediate(cam.gameObject);
}
}
将上面的代码添加到涂涂乐模型上即可。代码比较简单,不再详述。 着色器和代码来自国内一家提供增强现实服务的公司,他们的AR SDK中有相应的示例,感兴趣可移步这里
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 xue_huashan@163.com
文章标题:Unity3D Shader示例之—AR涂涂乐实现方法
文章字数:1.4k
本文作者:max-xue
发布时间:2016-11-07, 11:22:39
最后更新:2019-11-09, 21:40:57
原始链接:http://blog.le-more.com/2016/11/07/u3d/ar-e6-b6-82-e6-b6-82/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。