900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > Unity中用Shader实现镜子效果

Unity中用Shader实现镜子效果

时间:2023-07-19 00:41:12

相关推荐

Unity中用Shader实现镜子效果

做项目的时候要用到镜子,折腾了一下午,发现做镜子还是蛮快的,总结了一下网上的教程,大致有这么几种做法:

1、使用反射探针(Reflection Probe)

2、使用Render Texture

3、使用Shader着色器,我发现这种方法步骤比较少,而且重复使用起来也方便

下面就介绍一下这个方法,以及期间我踩过的坑;官方的教程在这里:MirrorReflection4

(我之所以说比较简单,是因为Shader方法可以用网上现成的代码,基本不用改

话不多说进入正题:

首先利用Shader做镜子,需要两段代码,一个是.shader着色器代码,另一个是C#脚本代码

着色器代码如下:

Mirror.shader

Shader "FX/MirrorReflection"{Properties{_MainTex ("Base (RGB)", 2D) = "white" {}[HideInInspector] _ReflectionTex ("", 2D) = "white" {}}SubShader{Tags { "RenderType"="Opaque" }LOD 100Pass {CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct v2f{float2 uv : TEXCOORD0;float4 refl : TEXCOORD1;float4 pos : SV_POSITION;};float4 _MainTex_ST;v2f vert(float4 pos : POSITION, float2 uv : TEXCOORD0){v2f o;o.pos = mul (UNITY_MATRIX_MVP, pos);o.uv = TRANSFORM_TEX(uv, _MainTex);o.refl = ComputeScreenPos (o.pos);return o;}sampler2D _MainTex;sampler2D _ReflectionTex;fixed4 frag(v2f i) : SV_Target{fixed4 tex = tex2D(_MainTex, i.uv);fixed4 refl = tex2Dproj(_ReflectionTex, UNITY_PROJ_COORD(i.refl));return tex * refl;}ENDCG}}}

C#脚本代码如下:

MirrorReflection.cs

using UnityEngine;using System.Collections;// This is in fact just the Water script from Pro Standard Assets,// just with refraction stuff removed.[ExecuteInEditMode] // Make mirror live-update even when not in play modepublic class MirrorReflection : MonoBehaviour{public bool m_DisablePixelLights = true;public int m_TextureSize = 256;public float m_ClipPlaneOffset = 0.07f;public LayerMask m_ReflectLayers = -1;private Hashtable m_ReflectionCameras = new Hashtable(); // Camera -> Camera tableprivate RenderTexture m_ReflectionTexture = null;private int m_OldReflectionTextureSize = 0;private static bool s_InsideRendering = false;// This is called when it's known that the object will be rendered by some// camera. We render reflections and do other updates here.// Because the script executes in edit mode, reflections for the scene view// camera will just work!public void OnWillRenderObject(){var rend = GetComponent<Renderer>();if (!enabled || !rend || !rend.sharedMaterial || !rend.enabled)return;Camera cam = Camera.current;if( !cam )return;// Safeguard from recursive reflections. if( s_InsideRendering )return;s_InsideRendering = true;Camera reflectionCamera;CreateMirrorObjects( cam, out reflectionCamera );// find out the reflection plane: position and normal in world spaceVector3 pos = transform.position;Vector3 normal = transform.up;// Optionally disable pixel lights for reflectionint oldPixelLightCount = QualitySettings.pixelLightCount;if( m_DisablePixelLights )QualitySettings.pixelLightCount = 0;UpdateCameraModes( cam, reflectionCamera );// Render reflection// Reflect camera around reflection planefloat d = -Vector3.Dot (normal, pos) - m_ClipPlaneOffset;Vector4 reflectionPlane = new Vector4 (normal.x, normal.y, normal.z, d);Matrix4x4 reflection = Matrix4x4.zero;CalculateReflectionMatrix (ref reflection, reflectionPlane);Vector3 oldpos = cam.transform.position;Vector3 newpos = reflection.MultiplyPoint( oldpos );reflectionCamera.worldToCameraMatrix = cam.worldToCameraMatrix * reflection;// Setup oblique projection matrix so that near plane is our reflection// plane. This way we clip everything below/above it for free.Vector4 clipPlane = CameraSpacePlane( reflectionCamera, pos, normal, 1.0f );//Matrix4x4 projection = cam.projectionMatrix;Matrix4x4 projection = cam.CalculateObliqueMatrix(clipPlane);reflectionCamera.projectionMatrix = projection;reflectionCamera.cullingMask = ~(1<<4) & m_ReflectLayers.value; // never render water layerreflectionCamera.targetTexture = m_ReflectionTexture;GL.SetRevertBackfacing (true);reflectionCamera.transform.position = newpos;Vector3 euler = cam.transform.eulerAngles;reflectionCamera.transform.eulerAngles = new Vector3(0, euler.y, euler.z);reflectionCamera.Render();reflectionCamera.transform.position = oldpos;GL.SetRevertBackfacing (false);Material[] materials = rend.sharedMaterials;foreach( Material mat in materials ) {if( mat.HasProperty("_ReflectionTex") )mat.SetTexture( "_ReflectionTex", m_ReflectionTexture );}// Restore pixel light countif( m_DisablePixelLights )QualitySettings.pixelLightCount = oldPixelLightCount;s_InsideRendering = false;}// Cleanup all the objects we possibly have createdvoid OnDisable(){if( m_ReflectionTexture ) {DestroyImmediate( m_ReflectionTexture );m_ReflectionTexture = null;}foreach( DictionaryEntry kvp in m_ReflectionCameras )DestroyImmediate( ((Camera)kvp.Value).gameObject );m_ReflectionCameras.Clear();}private void UpdateCameraModes( Camera src, Camera dest ){if( dest == null )return;// set camera to clear the same way as current cameradest.clearFlags = src.clearFlags;dest.backgroundColor = src.backgroundColor; if( src.clearFlags == CameraClearFlags.Skybox ){Skybox sky = src.GetComponent(typeof(Skybox)) as Skybox;Skybox mysky = dest.GetComponent(typeof(Skybox)) as Skybox;if( !sky || !sky.material ){mysky.enabled = false;}else{mysky.enabled = true;mysky.material = sky.material;}}// update other values to match current camera.// even if we are supplying custom camera&projection matrices,// some of values are used elsewhere (e.g. skybox uses far plane)dest.farClipPlane = src.farClipPlane;dest.nearClipPlane = src.nearClipPlane;dest.orthographic = src.orthographic;dest.fieldOfView = src.fieldOfView;dest.aspect = src.aspect;dest.orthographicSize = src.orthographicSize;}// On-demand create any objects we needprivate void CreateMirrorObjects( Camera currentCamera, out Camera reflectionCamera ){reflectionCamera = null;// Reflection render textureif( !m_ReflectionTexture || m_OldReflectionTextureSize != m_TextureSize ){if( m_ReflectionTexture )DestroyImmediate( m_ReflectionTexture );m_ReflectionTexture = new RenderTexture( m_TextureSize, m_TextureSize, 16 );m_ReflectionTexture.name = "__MirrorReflection" + GetInstanceID();m_ReflectionTexture.isPowerOfTwo = true;m_ReflectionTexture.hideFlags = HideFlags.DontSave;m_OldReflectionTextureSize = m_TextureSize;}// Camera for reflectionreflectionCamera = m_ReflectionCameras[currentCamera] as Camera;if( !reflectionCamera ) // catch both not-in-dictionary and in-dictionary-but-deleted-GO{GameObject go = new GameObject( "Mirror Refl Camera id" + GetInstanceID() + " for " + currentCamera.GetInstanceID(), typeof(Camera), typeof(Skybox) );reflectionCamera = go.camera;reflectionCamera.enabled = false;reflectionCamera.transform.position = transform.position;reflectionCamera.transform.rotation = transform.rotation;reflectionCamera.gameObject.AddComponent("FlareLayer");go.hideFlags = HideFlags.HideAndDontSave;m_ReflectionCameras[currentCamera] = reflectionCamera;} }// Extended sign: returns -1, 0 or 1 based on sign of aprivate static float sgn(float a){if (a > 0.0f) return 1.0f;if (a < 0.0f) return -1.0f;return 0.0f;}// Given position/normal of the plane, calculates plane in camera space.private Vector4 CameraSpacePlane (Camera cam, Vector3 pos, Vector3 normal, float sideSign){Vector3 offsetPos = pos + normal * m_ClipPlaneOffset;Matrix4x4 m = cam.worldToCameraMatrix;Vector3 cpos = m.MultiplyPoint( offsetPos );Vector3 cnormal = m.MultiplyVector( normal ).normalized * sideSign;return new Vector4( cnormal.x, cnormal.y, cnormal.z, -Vector3.Dot(cpos,cnormal) );}// Calculates reflection matrix around the given planeprivate static void CalculateReflectionMatrix (ref Matrix4x4 reflectionMat, Vector4 plane){reflectionMat.m00 = (1F - 2F*plane[0]*plane[0]);reflectionMat.m01 = ( - 2F*plane[0]*plane[1]);reflectionMat.m02 = ( - 2F*plane[0]*plane[2]);reflectionMat.m03 = ( - 2F*plane[3]*plane[0]);reflectionMat.m10 = ( - 2F*plane[1]*plane[0]);reflectionMat.m11 = (1F - 2F*plane[1]*plane[1]);reflectionMat.m12 = ( - 2F*plane[1]*plane[2]);reflectionMat.m13 = ( - 2F*plane[3]*plane[1]);reflectionMat.m20 = ( - 2F*plane[2]*plane[0]);reflectionMat.m21 = ( - 2F*plane[2]*plane[1]);reflectionMat.m22 = (1F - 2F*plane[2]*plane[2]);reflectionMat.m23 = ( - 2F*plane[3]*plane[2]);reflectionMat.m30 = 0F;reflectionMat.m31 = 0F;reflectionMat.m32 = 0F;reflectionMat.m33 = 1F;}}

下面说说该如何使用这两段代码:

1、首先我们搭建一个简单的场景,这里我放了一些小方块和球之类的

然后再创建一个平面,一会儿用来做镜子

2、在Project中创建一个Shader

然后重命名一下并打开,原来的代码可以全删掉,把上面的Mirror.Shader代码复制进去

保存,回到Unity

接下来新建一个脚本MirrorReflection,将上面MirrorReflection.cs中的脚本复制进去

注意:因为版本问题,脚本可能会有一些错误,我用的版本是.2.14,

这些错误多半是因为版本的更新导致的API改动,根据相应的修改提示,改用新的API,问题解决;

以下时候我改过后的截图:

3、在Project中新建一个材质Material,将这个材质的Shader设置成FX/MirrorReflection

4、将MirrorMaterial材质、MirrorReflection脚本 挂在Mirror也就是刚刚用来做镜子的平面上

完成以后的属性视图:

5、回到场景视图:

效果有了,就是镜子很模糊?

别急,刚刚的脚本里给我们提供了一个分辨率的设置参数,我们把它调大一点就好了

这里我设置了1024,这样一面清晰的镜子就完成了!

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