1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > Unity 3D 制作传送门 传送门视觉效果 传送物体

Unity 3D 制作传送门 传送门视觉效果 传送物体

时间:2019-01-15 14:51:10

相关推荐

Unity 3D 制作传送门 传送门视觉效果 传送物体

强大的传送门效果

注意,版本可用,其他版本未测试

物理效果:

无缝切换位置

主要Shader及代码(仅提供参考,无法使其单独运行)

Shader:

Shader "Custom/Portal"{Properties{_InactiveColour ("Inactive Colour", Color) = (1, 1, 1, 1)}SubShader{Tags {"RenderType"="Opaque" }LOD 100Cull OffPass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;};struct v2f{float4 vertex : SV_POSITION;float4 screenPos : TEXCOORD0;};sampler2D _MainTex;float4 _InactiveColour;int displayMask; // set to 1 to display texture, otherwise will draw test colourv2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.screenPos = ComputeScreenPos(o.vertex);return o;}fixed4 frag (v2f i) : SV_Target{float2 uv = i.screenPos.xy / i.screenPos.w;fixed4 portalCol = tex2D(_MainTex, uv);return portalCol * displayMask + _InactiveColour * (1-displayMask);}ENDCG}}Fallback "Standard" // for shadows}

Shader "Custom/Slice"{Properties{_Color ("Color", Color) = (1,1,1,1)_MainTex ("Albedo (RGB)", 2D) = "white" {}_Glossiness ("Smoothness", Range(0,1)) = 0.5_Metallic ("Metallic", Range(0,1)) = 0.0sliceNormal("normal", Vector) = (0,0,0,0)sliceCentre ("centre", Vector) = (0,0,0,0)sliceOffsetDst("offset", Float) = 0}SubShader{Tags {"Queue" = "Geometry" "IgnoreProjector" = "True" "RenderType"="Geometry" }LOD 200CGPROGRAM// Physically based Standard lighting model, and enable shadows on all light types#pragma surface surf Standard addshadow// Use shader model 3.0 target, to get nicer looking lighting#pragma target 3.0sampler2D _MainTex;struct Input{float2 uv_MainTex;float3 worldPos;};half _Glossiness;half _Metallic;fixed4 _Color;// World space normal of slice, anything along this direction from centre will be invisiblefloat3 sliceNormal;// World space centre of slicefloat3 sliceCentre;// Increasing makes more of the mesh visible, decreasing makes less of the mesh visiblefloat sliceOffsetDst;void surf (Input IN, inout SurfaceOutputStandard o){float3 adjustedCentre = sliceCentre + sliceNormal * sliceOffsetDst;float3 offsetToSliceCentre = adjustedCentre - IN.worldPos;clip (dot(offsetToSliceCentre, sliceNormal));// Albedo comes from a texture tinted by colorfixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;o.Albedo = c.rgb;// Metallic and smoothness come from slider variableso.Metallic = _Metallic;o.Smoothness = _Glossiness;o.Alpha = c.a;}ENDCG}FallBack "VertexLit"}

C#

using System.Collections;using System.Collections.Generic;using UnityEngine;public class Portal : MonoBehaviour {[Header ("Main Settings")]public Portal linkedPortal;public MeshRenderer screen;public int recursionLimit = 5;[Header ("Advanced Settings")]public float nearClipOffset = 0.05f;public float nearClipLimit = 0.2f;// Private variablesRenderTexture viewTexture;Camera portalCam;Camera playerCam;Material firstRecursionMat;List<PortalTraveller> trackedTravellers;MeshFilter screenMeshFilter;void Awake () {playerCam = Camera.main;portalCam = GetComponentInChildren<Camera> ();portalCam.enabled = false;trackedTravellers = new List<PortalTraveller> ();screenMeshFilter = screen.GetComponent<MeshFilter> ();screen.material.SetInt ("displayMask", 1);}void LateUpdate () {HandleTravellers ();}void HandleTravellers () {for (int i = 0; i < trackedTravellers.Count; i++) {PortalTraveller traveller = trackedTravellers[i];Transform travellerT = traveller.transform;var m = linkedPortal.transform.localToWorldMatrix * transform.worldToLocalMatrix * travellerT.localToWorldMatrix;Vector3 offsetFromPortal = travellerT.position - transform.position;int portalSide = System.Math.Sign (Vector3.Dot (offsetFromPortal, transform.forward));int portalSideOld = System.Math.Sign (Vector3.Dot (traveller.previousOffsetFromPortal, transform.forward));// Teleport the traveller if it has crossed from one side of the portal to the otherif (portalSide != portalSideOld) {var positionOld = travellerT.position;var rotOld = travellerT.rotation;traveller.Teleport (transform, linkedPortal.transform, m.GetColumn (3), m.rotation);traveller.graphicsClone.transform.SetPositionAndRotation (positionOld, rotOld);// Can't rely on OnTriggerEnter/Exit to be called next frame since it depends on when FixedUpdate runslinkedPortal.OnTravellerEnterPortal (traveller);trackedTravellers.RemoveAt (i);i--;} else {traveller.graphicsClone.transform.SetPositionAndRotation (m.GetColumn (3), m.rotation);//UpdateSliceParams (traveller);traveller.previousOffsetFromPortal = offsetFromPortal;}}}// Called before any portal cameras are rendered for the current framepublic void PrePortalRender () {foreach (var traveller in trackedTravellers) {UpdateSliceParams (traveller);}}// Manually render the camera attached to this portal// Called after PrePortalRender, and before PostPortalRenderpublic void Render () {// Skip rendering the view from this portal if player is not looking at the linked portalif (!CameraUtility.VisibleFromCamera (linkedPortal.screen, playerCam)) {return;}CreateViewTexture ();var localToWorldMatrix = playerCam.transform.localToWorldMatrix;var renderPositions = new Vector3[recursionLimit];var renderRotations = new Quaternion[recursionLimit];int startIndex = 0;portalCam.projectionMatrix = playerCam.projectionMatrix;for (int i = 0; i < recursionLimit; i++) {if (i > 0) {// No need for recursive rendering if linked portal is not visible through this portalif (!CameraUtility.BoundsOverlap (screenMeshFilter, linkedPortal.screenMeshFilter, portalCam)) {break;}}localToWorldMatrix = transform.localToWorldMatrix * linkedPortal.transform.worldToLocalMatrix * localToWorldMatrix;int renderOrderIndex = recursionLimit - i - 1;renderPositions[renderOrderIndex] = localToWorldMatrix.GetColumn (3);renderRotations[renderOrderIndex] = localToWorldMatrix.rotation;portalCam.transform.SetPositionAndRotation (renderPositions[renderOrderIndex], renderRotations[renderOrderIndex]);startIndex = renderOrderIndex;}// Hide screen so that camera can see through portalscreen.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.ShadowsOnly;linkedPortal.screen.material.SetInt ("displayMask", 0);for (int i = startIndex; i < recursionLimit; i++) {portalCam.transform.SetPositionAndRotation (renderPositions[i], renderRotations[i]);SetNearClipPlane ();HandleClipping ();if (!portalCam.orthographic)portalCam.Render ();if (i == startIndex) {linkedPortal.screen.material.SetInt ("displayMask", 1);}}// Unhide objects hidden at start of renderscreen.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;}void HandleClipping () {// There are two main graphical issues when slicing travellers// 1. Tiny sliver of mesh drawn on backside of portal// Ideally the oblique clip plane would sort this out, but even with 0 offset, tiny sliver still visible// 2. Tiny seam between the sliced mesh, and the rest of the model drawn onto the portal screen// This function tries to address these issues by modifying the slice parameters when rendering the view from the portal// Would be great if this could be fixed more elegantly, but this is the best I can figure out for nowconst float hideDst = -1000;const float showDst = 1000;float screenThickness = linkedPortal.ProtectScreenFromClipping (portalCam.transform.position);foreach (var traveller in trackedTravellers) {if (SameSideOfPortal (traveller.transform.position, portalCamPos)) {// Addresses issue 1traveller.SetSliceOffsetDst (hideDst, false);} else {// Addresses issue 2traveller.SetSliceOffsetDst (showDst, false);}// Ensure clone is properly sliced, in case it's visible through this portal:int cloneSideOfLinkedPortal = -SideOfPortal (traveller.transform.position);bool camSameSideAsClone = linkedPortal.SideOfPortal (portalCamPos) == cloneSideOfLinkedPortal;if (camSameSideAsClone) {traveller.SetSliceOffsetDst (screenThickness, true);} else {traveller.SetSliceOffsetDst (-screenThickness, true);}}var offsetFromPortalToCam = portalCamPos - transform.position;foreach (var linkedTraveller in linkedPortal.trackedTravellers) {var travellerPos = linkedTraveller.graphicsObject.transform.position;var clonePos = linkedTraveller.graphicsClone.transform.position;// Handle clone of linked portal coming through this portal:bool cloneOnSameSideAsCam = linkedPortal.SideOfPortal (travellerPos) != SideOfPortal (portalCamPos);if (cloneOnSameSideAsCam) {// Addresses issue 1linkedTraveller.SetSliceOffsetDst (hideDst, true);} else {// Addresses issue 2linkedTraveller.SetSliceOffsetDst (showDst, true);}// Ensure traveller of linked portal is properly sliced, in case it's visible through this portal:bool camSameSideAsTraveller = linkedPortal.SameSideOfPortal (linkedTraveller.transform.position, portalCamPos);if (camSameSideAsTraveller) {linkedTraveller.SetSliceOffsetDst (screenThickness, false);} else {linkedTraveller.SetSliceOffsetDst (-screenThickness, false);}}}// Called once all portals have been rendered, but before the player camera renderspublic void PostPortalRender () {foreach (var traveller in trackedTravellers) {UpdateSliceParams (traveller);}ProtectScreenFromClipping (playerCam.transform.position);}void CreateViewTexture () {if (viewTexture == null || viewTexture.width != Screen.width || viewTexture.height != Screen.height) {if (viewTexture != null) {viewTexture.Release ();}viewTexture = new RenderTexture (Screen.width, Screen.height, 0);// Render the view from the portal camera to the view textureportalCam.targetTexture = viewTexture;// Display the view texture on the screen of the linked portallinkedPortal.screen.material.SetTexture ("_MainTex", viewTexture);}}// Sets the thickness of the portal screen so as not to clip with camera near plane when player goes throughfloat ProtectScreenFromClipping (Vector3 viewPoint) {float halfHeight = playerCam.nearClipPlane * Mathf.Tan (playerCam.fieldOfView * 0.5f * Mathf.Deg2Rad);float halfWidth = halfHeight * playerCam.aspect;float dstToNearClipPlaneCorner = new Vector3 (halfWidth, halfHeight, playerCam.nearClipPlane).magnitude;float screenThickness = dstToNearClipPlaneCorner;Transform screenT = screen.transform;bool camFacingSameDirAsPortal = Vector3.Dot (transform.forward, transform.position - viewPoint) > 0;screenT.localScale = new Vector3 (screenT.localScale.x, screenT.localScale.y, screenThickness);screenT.localPosition = Vector3.forward * screenThickness * ((camFacingSameDirAsPortal) ? 0.5f : -0.5f);return screenThickness;}void UpdateSliceParams (PortalTraveller traveller) {// Calculate slice normalint side = SideOfPortal (traveller.transform.position);Vector3 sliceNormal = transform.forward * -side;Vector3 cloneSliceNormal = linkedPortal.transform.forward * side;// Calculate slice centreVector3 slicePos = transform.position;Vector3 cloneSlicePos = linkedPortal.transform.position;// Adjust slice offset so that when player standing on other side of portal to the object, the slice doesn't clip throughfloat sliceOffsetDst = 0;float cloneSliceOffsetDst = 0;float screenThickness = screen.transform.localScale.z;bool playerSameSideAsTraveller = SameSideOfPortal (playerCam.transform.position, traveller.transform.position);if (!playerSameSideAsTraveller) {sliceOffsetDst = -screenThickness;}bool playerSameSideAsCloneAppearing = side != linkedPortal.SideOfPortal (playerCam.transform.position);if (!playerSameSideAsCloneAppearing) {cloneSliceOffsetDst = -screenThickness;}// Apply parametersfor (int i = 0; i < traveller.originalMaterials.Length; i++) {traveller.originalMaterials[i].SetVector ("sliceCentre", slicePos);traveller.originalMaterials[i].SetVector ("sliceNormal", sliceNormal);traveller.originalMaterials[i].SetFloat ("sliceOffsetDst", sliceOffsetDst);traveller.cloneMaterials[i].SetVector ("sliceCentre", cloneSlicePos);traveller.cloneMaterials[i].SetVector ("sliceNormal", cloneSliceNormal);traveller.cloneMaterials[i].SetFloat ("sliceOffsetDst", cloneSliceOffsetDst);}}// Use custom projection matrix to align portal camera's near clip plane with the surface of the portal// Note that this affects precision of the depth buffer, which can cause issues with effects like screenspace AOvoid SetNearClipPlane () {// Learning resource:// /lengyel/Lengyel-Oblique.pdfTransform clipPlane = transform;int dot = System.Math.Sign (Vector3.Dot (clipPlane.forward, transform.position - portalCam.transform.position));Vector3 camSpacePos = portalCam.worldToCameraMatrix.MultiplyPoint (clipPlane.position);Vector3 camSpaceNormal = portalCam.worldToCameraMatrix.MultiplyVector (clipPlane.forward) * dot;float camSpaceDst = -Vector3.Dot (camSpacePos, camSpaceNormal) + nearClipOffset;// Don't use oblique clip plane if very close to portal as it seems this can cause some visual artifactsif (Mathf.Abs (camSpaceDst) > nearClipLimit) {Vector4 clipPlaneCameraSpace = new Vector4 (camSpaceNormal.x, camSpaceNormal.y, camSpaceNormal.z, camSpaceDst);// Update projection based on new clip plane// Calculate matrix with player cam so that player camera settings (fov, etc) are usedportalCam.projectionMatrix = playerCam.CalculateObliqueMatrix (clipPlaneCameraSpace);} else {portalCam.projectionMatrix = playerCam.projectionMatrix;}}void OnTravellerEnterPortal (PortalTraveller traveller) {if (!trackedTravellers.Contains (traveller)) {traveller.EnterPortalThreshold ();traveller.previousOffsetFromPortal = traveller.transform.position - transform.position;trackedTravellers.Add (traveller);}}void OnTriggerEnter (Collider other) {var traveller = other.GetComponent<PortalTraveller> ();if (traveller) {OnTravellerEnterPortal (traveller);}}void OnTriggerExit (Collider other) {var traveller = other.GetComponent<PortalTraveller> ();if (traveller && trackedTravellers.Contains (traveller)) {traveller.ExitPortalThreshold ();trackedTravellers.Remove (traveller);}}/*** Some helper/convenience stuff:*/int SideOfPortal (Vector3 pos) {return System.Math.Sign (Vector3.Dot (pos - transform.position, transform.forward));}bool SameSideOfPortal (Vector3 posA, Vector3 posB) {return SideOfPortal (posA) == SideOfPortal (posB);}Vector3 portalCamPos {get {return portalCam.transform.position;}}void OnValidate () {if (linkedPortal != null) {linkedPortal.linkedPortal = this;}}}

Demo下载链接

链接: 点击下载

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。