
(投影:Projector)UnityProjector投影器原理以及优化
很久很久以前,做过⼀个离线Mesh切割⽅式的Decay效果 适合场景景观布置,批次合并等,但运⾏时性能较差,这次我们来玩玩运⾏时投
影器。
先上成平图
测试效果图, 图中的裤⼦上投影了⼀个眼睛
那么投影的原理是什么呢。。。 那么请看下⾯这张
这张图左下⾓就是投影器看到的景象,投影贴图“眼睛” 充满了整个投影器的视野,那么原理就呼之⽽出了。
在正常渲染裤⼦的顶点时,顺便变换到投影器的屏幕空间,然后再渲染裤⼦的⽚段处理函数中将位于投影器屏幕空间的像素都换成眼睛即
可。
渲染裤⼦的Shader
1Shader "Unlit/ProjectorShader"
2{
3 Properties
4 {
5 _MainTex ("Texture", 2D) = "white" {}
6 }
7 SubShader
8 {
9 Tags { "RenderType"="Opaque" }
10
11 Pass
12 {
13 CGPROGRAM
14 #pragma vertex vert
15 #pragma fragment frag
16
17 #include ""
18
19 struct appdata
20 {
21 float4 vertex : POSITION;
22 float2 uv : TEXCOORD0;
23 };
24
25 struct v2f
26 {
27 float2 uv : TEXCOORD0;
28 float4 projectorUV : TEXCOORD1;
29 float4 vertex : SV_POSITION;
29 float4 vertex : SV_POSITION;
30 };
31
32 float4x4 _ProjectorP;
33 float4x4 _ProjectorV;
34 float4x4 _ProjectorVP;
35 sampler2D _ProjectorTex;
36 sampler2D _ProjectorFallOut;
37 sampler2D _MainTex;
38 float4 _MainTex_ST;
39
40 v2f vert (appdata v)
41 {
42 v2f o;
43
44 = UnityObjectToClipPos();
45 = TRANSFORM_TEX(, _MainTex);
46
47 float4x4 propMVP = mul(_ProjectorVP, unity_ObjectToWorld);
48 float4 projProjPos = mul(propMVP, );
49
50 projProjPos = ComputeScreenPos(projProjPos);
51 torUV = projProjPos;
52
53 return o;
54 }
55
56 fixed4 frag (v2f i) : SV_Target
57 {
58 // sample the texture
59 fixed4 col = tex2D(_MainTex, );
60
61 fixed4 projectorCol = tex2Dproj(_ProjectorTex, torUV);
62
63 // tex2Dproj = xyz/ w
64 fixed4 projectorFallOutCol = tex2Dproj(_ProjectorFallOut, torUV);
65
66 projectorCol *= projectorFallOutCol;
67
68 += ;
69
70
71 return col;
72 }
73 ENDCG
74 }
75 }
76}
⾃定义投影器的CS脚本
1using tions;
2using c;
3using UnityEngine;
4
5[ExecuteInEditMode]
5[ExecuteInEditMode]
6public class CustomProjector : MonoBehaviour {
7 public Camera ProjectorCam;
8 public Texture2D Tex;
9 public Texture2D FallOut;
10 public Material Mat;
11
12 private void Start()
13 {
14
15 }
16
17 private void Update()
18 {
19 Matrix4x4 P = ProjectionMatrix(tionMatrix, fal);
20 Matrix4x4 V = oCameraMatrix;
21 rix("_ProjectorV", V);
22 rix("_ProjectorP", P);
23 rix("_ProjectorVP", P * V);
24 ture("_ProjectorTex", Tex);
25 ture("_ProjectorFallOut", FallOut);
26 }
27}
范例中的代码借⽤了Unity的相机,实际上并不需要相机,仅仅是借⽤了相机的投影矩阵和世界空间矩阵⽽已。
很多项⽬组在制作移动端游戏时,都使⽤Projector来制作主⾓的投影,虽然⽐起ShadowMap是优化了许多,但是实际上只要和Projector
碰撞到物件其DC 都会翻倍, 对于我来说,这还是不可接受的。
⽽使⽤上⾯范例的代码,可以让DC不翻倍,但是并不通⽤, 因为受Projector影响的物体都需要定制Shader.
Unity⾃带Projector会翻倍的原因主要也是通⽤性,跨平台,使⽤⽅便, 因此它的原理是
1.找到所有和Projector有碰撞的MeshRenderer
2.使⽤Projector的材质球,将MeshRenderer的顶点再渲染⼀遍,并贴图
也因此被投影的物体⽆法触发动态合批
那么问题来,有没有⼀种⽅案,既可以保证通⽤性,不需要定制被投影⽬标的Shader,⼜可以使DC不翻倍呢? 答案是:有的, 但是有代价
代价1:需要使⽤深度图
代价2:DC不会翻倍,但是总共的DC为,被投影物体数量 + 1. 既物体⾃带的DC + 1 * (Projector数量) 其实代价2根本不算个事
说搞就搞
1.开启相机的深度渲染
extureMode |= ;
2.创建⼀个表⽰投影器范围的⽹格,我搞了个Cube Mesh
3.创建Cube Mesh对应的相关矩阵,因为是Cube 因此创建的投影为正交投影, 当然,如果也可以使⽤透视投影。
1BoxCollider collider = ponent
2 this.m_size = .x / 2;
3 this.m_nearClip = -.x / 2;
好了,准备⼯作做完了。开始渲染吧。
4 this.m_farClip = .x / 2;
⾸先是将投影器覆盖的区域,采样出当前屏幕空间的深度,类似这样的效果
5 this.m_aspect = 1;
6
7 Matrix4x4 projector = default(Matrix4x4);
8 projector = (-m_aspect * m_size, m_aspect * m_size, -m_size, m_size, m_nearClip, m_farClip);
9
10 m_worldToProjector = projector * oLocalMatrix;
11
12 MeshRenderer mr = ponent
13 rix("_WorldToProjector", m_worldToProjector);
要实现这样的效果,就是讲顶点变换到投影平⾯,并将坐标变换到UV值域下
⼤概这样
1vert part
2Pos = ComputeScreenPos();
3
4fragment part
5fixed4 screenPos = Pos;
6 = / screenPos.w;
7float depth = tex2D(_CameraDepthTexture, screenPos).r;
好了,现在我们有了深度,下⼀部就是讲当前像素的深度还原回该深度对应的世界坐标了
只需要两部矩阵变换
1.从屏幕空间变换到相机空间 unity_CameraInvProjection
2.从相机空间变换到世界空间 unity_MatrixInvV
有了世界坐标后,就可以将该坐标变换到Projector的控件,就是准备⼯作中的 _WorldToProjector
变换到Projector空间后,还记得范例上的投影器的全部视野就是需要投射的贴图范围吗?因此这⾥要做UV值域的变换
1//变换到⾃定义投影器投影空间
2fixed4 projectorPos = mul(_WorldToProjector, worldSpacePos);
3projectorPos /= projectorPos.w;
4
5fixed2 projUV = * 0.5 + 0.5; //变换到uv坐标系
⼤功告成! 你可能会好奇,为什么多了⼀个遮罩贴图? 虽然你讲投影器视野内的像素部分都贴了投影贴图,但是是野外的像素怎么办?这
6fixed4 col = tex2D(_ProjectorTex, projUV); //采样投影贴图
个时候就需要遮罩图抹掉,因此遮罩图的纹理设置要设置为Clamp,保证边缘像素为拉伸且外侧的Alpha为0
7fixed4 mask = tex2D(_ProjectorTexMask, projUV); //采样遮罩贴图
8 = lerp(fixed3(1, 1, 1), , (1 - mask.r)); //融合

本文发布于:2023-05-22 20:36:12,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/zhishi/a/1684758973174119.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:(投影:Projector)UnityProjector投影器原理以及优化.doc
本文 PDF 下载地址:(投影:Projector)UnityProjector投影器原理以及优化.pdf
| 留言与评论(共有 0 条评论) |