Unity制作扫描效果

一个笔试中遇到的题目,完成一个以主角为中心的扫描效果。下图是我完成的效果图。
在这里插入图片描述
主要功能是按下v键,以场景中的角色或者摄像机为中心发出扫描圈,路过的有效目标被高亮且透视。使用了三个脚本和一个Shader完成效果。

脚本

实现思路是ScanCenter脚本监测按键,并且完成切换摄像机和判断是否激活或者重置扫描器。激活的过程中将此脚本所在Object的世界坐标传入扫描器。

void Update()
{
	if (Input.GetKeyDown(KeyCode.V))
	{
    if (scanCamera.enabled == false)
    {
        mainCamera.enabled = false;
        scanCamera.enabled = true;
        Scanner.CallScan(transform.position);
    }
    else {
        mainCamera.enabled = true;
        scanCamera.enabled = false;
        Scanner.reset();
    }
	}
}

Target脚本实现目标材质和高亮材质的切换。

public void highlight()
{
rend.material = targetMaterial;
}

//重置材质
public void recover()
{
rend.material = baseMaterial;
}

void Start()
{
rend = GetComponent<Renderer>();
rend.enabled = true;
baseMaterial = rend.materials[0];
}

Scanner脚本用于获取摄像机的深度+法线纹理,并且计算指向摄像机的四个角的向量,传入Shader。这个部分在冯乐乐的《Unity Shader入门级精要》中全局雾效的部分有详细说明。Shader获取到向量后。再经过计算反推出片元所在的世界坐标。
以下是计算指向摄像机近裁/远裁平面四个角的归一向量的计算方式,计算结果由四维矩阵_FrustumCorners传入Shader:

//传入着色器的属性
material.SetVector("_WorldSpaceScannerPos", scanPosition);
material.SetFloat("_ScanDistance", ScanDistance);

Matrix4x4 frustumCorners = Matrix4x4.identity;

//计算摄像机指向摄像机空间四个角的归一化向量
float camFar = _camera.farClipPlane;
float camFov = _camera.fieldOfView;
float camAspect = _camera.aspect;

float fovWHalf = camFov * 0.5f;

Vector3 toRight = _camera.transform.right * Mathf.Tan(fovWHalf * Mathf.Deg2Rad) * camAspect;
Vector3 toTop = _camera.transform.up * Mathf.Tan(fovWHalf * Mathf.Deg2Rad);

Vector3 topLeft = (_camera.transform.forward - toRight + toTop);
float camScale = topLeft.magnitude * camFar;

topLeft.Normalize();
topLeft *= camScale;

Vector3 topRight = (_camera.transform.forward + toRight + toTop);
topRight.Normalize();
topRight *= camScale;

Vector3 bottomRight = (_camera.transform.forward + toRight - toTop);
bottomRight.Normalize();
bottomRight *= camScale;

Vector3 bottomLeft = (_camera.transform.forward - toRight - toTop);
bottomLeft.Normalize();
bottomLeft *= camScale;

frustumCorners.SetRow(0, topLeft);
frustumCorners.SetRow(1, topRight);
frustumCorners.SetRow(2, bottomRight);
frustumCorners.SetRow(3, bottomLeft);
material.SetMatrix("_FrustumCorners", frustumCorners);

自定义一个GraphicsBlit让Shader可以通过索引访问该矩阵以求出interpolatedRay,也就是摄像机指向顶点的方向向量。

// 自定义 GraphicsBlit, 将上文四个角设置为顶点的z轴索引,shader中直接提取顶点的vertex.z就可以得到索引。
material.SetTexture("_MainTex", source);
RenderTexture.active = dest;

GL.PushMatrix();
GL.LoadOrtho();

material.SetPass(0);

GL.Begin(GL.QUADS);

GL.MultiTexCoord2(0, 0.0f, 0.0f);
GL.Vertex3(0.0f, 0.0f, 3.0f);

GL.MultiTexCoord2(0, 1.0f, 0.0f);
GL.Vertex3(1.0f, 0.0f, 2.0f);

GL.MultiTexCoord2(0, 1.0f, 1.0f);
GL.Vertex3(1.0f, 1.0f, 1.0f);

GL.MultiTexCoord2(0, 0.0f, 1.0f);
GL.Vertex3(0.0f, 1.0f, 0.0f);

GL.End();
GL.PopMatrix();

Shader

Shader主要用于使用Scanner脚本传入的参数反推片元世界坐标并用图片纹理作出扫描效果(一维纹理)

v2f vert(input v)
{
v2f o;
half index = v.vertex.z;
v.vertex.z = 0.1;

o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv.xy;
o.uv_depth = v.uv.xy;

#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
    o.uv.y = 1 - o.uv.y;
#endif              

o.interpolatedRay = _FrustumCorners[(int)index];

return o;
}

half4 frag (v2f i) : SV_Target
{
half4 col = tex2D(_MainTex, i.uv);
//获取深度值并且反推出像素点的世界坐标
float linearDepth = Linear01Depth(DecodeFloatRG(tex2D(_CameraDepthTexture, i.uv_depth)));
float3 worldPos = _WorldSpaceCameraPos + (linearDepth * i.interpolatedRay.xyz);

half4 sc = half4(0, 0, 0, 0);

//扫描特效
float dist = distance(worldPos, _WorldSpaceScannerPos);
if (dist < _ScanDistance && dist > _ScanDistance - _ScanWidth && linearDepth < 1)
{
    float diff = 1 - (_ScanDistance - dist) / (_ScanWidth);
    sc =  tex2D(_ScanTex, float2(diff,diff));
    sc = _ScanColor*sc.a*_Alpha;
}

return col + sc;
}

Shader的顶点着色器中计算出interpolatedRay作为摄像机指向顶点的方向向量传入片元着色器。片元着色器将interpolatedRay和摄像机深度纹理采集到的深度值相乘获得摄像机指向片元的向量,加上摄像机坐标及是片元的位置坐标。
得到位置坐标即可判断是否有扫描特效。

Unity是一款功能强大的游戏开发引擎,它提供了丰富的工具和功能来制作各种不同的游戏效果,包括立体效果。 首先,Unity提供了灵活的3D建模工具,可以创建各种形状的物体。开发者可以通过其内置的编辑器或者外部的3D建模软件来设计和建立游戏场景中的立体物体。这些物体可以是简单的几何体,也可以是复杂的模型,甚至可以使用粒子系统创建出逼真的特效。 其次,Unity还提供了强大的光照和渲染系统,可以给立体物体添加各种灯光效果,例如阴影、反射和折射等。通过设置不同的光源和材质属性,可以调整立体物体的表面光泽和颜色,使其看起来更加真实和具有立体感。 另外,Unity还支持使用各种不同的着色器(shader)来控制立体物体的外观。着色器可以用来处理立体物体的纹理贴图、法线贴图、透明度、反射等效果,通过调整着色器的参数可以实现各种想要的立体效果。 此外,Unity还提供了强大的物理引擎,可以模拟立体物体之间的碰撞和受力情况。利用物理引擎,开发者可以实现立体物体之间的真实互动,例如球体滚动、物体堆叠等。 总之,Unity提供了丰富的功能和工具,可以轻松地实现立体效果。开发者可以利用其灵活的建模工具、强大的光照和渲染系统、多样的着色器和物理引擎来制作出各种带有立体效果的游戏场景和物体,为玩家呈现出一个更加逼真和立体的游戏世界。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值