Created
March 11, 2020 07:48
-
-
Save stilllisisi/9530a61ba4699449bdc586f5caa41cbf to your computer and use it in GitHub Desktop.
【游戏-unity】Unity-Shader编程-模型实时阴影。在游戏开发中实时阴影最常见的方法是实时光照,但是这个会带来性能的问题,如果场景中模型比较多,采用实时光照DC会增加好几百,会造成渲染的压力。这里介绍Shader来现在的方案。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //第二个pass专门用来渲染阴影,power参数是颜色加强,因为关闭了实时灯光,模型会显得暗淡,这个参数跟阴影效果没太大关系。 | |
| //主要是将光照向量和当前模型的坐标传递给Shader,以便Shader实时计算出当前模型的阴影。 | |
| //C# 实现代码: | |
| using System.Collections; | |
| using System.Collections.Generic; | |
| using UnityEngine; | |
| public class PlayerShadow : MonoBehaviour | |
| { | |
| public Camera mCamera; | |
| public GameObject mLight; | |
| private List<Material> mMatList = new List<Material>(); | |
| private void Awake() | |
| { | |
| SkinnedMeshRenderer[] renderlist = GetComponentsInChildren<SkinnedMeshRenderer>(); | |
| foreach (var render in renderlist) | |
| { | |
| if (render == null) | |
| continue; | |
| foreach (var mt in render.materials) | |
| { | |
| if (mt.shader.name == "Custom/PlayerShadow") | |
| mMatList.Add(mt); | |
| } | |
| } | |
| } | |
| void Start() | |
| { | |
| mCamera = Camera.main; | |
| mLight = GameObject.Find("LightPosition").gameObject; | |
| } | |
| void Update() | |
| { | |
| UpdateShader(); | |
| } | |
| private void UpdateShader() | |
| { | |
| if (mLight == null) | |
| return; | |
| foreach (var mat in mMatList) | |
| { | |
| if (mat == null) | |
| continue; | |
| mat.SetVector("_WorldPos", transform.position); | |
| mat.SetVector("_ShadowProjDir", mLight.transform.forward); | |
| mat.SetVector("_ShadowPlane", new Vector4(0f, 0.4f, 0.0f, 0.0f)); | |
| mat.SetVector("_ShadowFadeParams", new Vector4(1f, 0.9f, 0.8f, 0.7f)); | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //下面用2D视角来分析,阴影产生原理: | |
| //我们将于光照方向当做照相机的视线,而与光照方向垂直的面,就是投影变换最后的盒子的正面。 | |
| //这意味着我们可以制作一个正交照相机,使得它与光照方向一致,再将地面模型的各个顶点投影到该照相机上,这时候照相机的纹理UV坐标范围是01。 | |
| //地面投影到了照相机,也将其坐标映射到01之间,由此与照相机的纹理一一对应,于是将照相机照出来的纹理叠加在地面上,就形成了阴影。 | |
| //shader程序实现: | |
| Shader "Custom/PlayerShadow" | |
| { | |
| Properties | |
| { | |
| _MainTex ("Texture", 2D) = "white" {} | |
| _ShadowInvLen ("ShadowInvLen", float) = 1.0 //0.4449261 | |
| _Power("Power", Range(0,2)) = 1.7 | |
| } | |
| SubShader | |
| { | |
| Tags{ "RenderType" = "Opaque" "Queue" = "Geometry+10" } | |
| LOD 100 | |
| Pass | |
| { | |
| CGPROGRAM | |
| #pragma vertex vert | |
| #pragma fragment frag | |
| // make fog work | |
| #pragma multi_compile_fog | |
| #include "UnityCG.cginc" | |
| struct appdata | |
| { | |
| float4 vertex : POSITION; | |
| float2 uv : TEXCOORD0; | |
| }; | |
| struct v2f | |
| { | |
| float2 uv : TEXCOORD0; | |
| UNITY_FOG_COORDS(1) | |
| float4 vertex : SV_POSITION; | |
| }; | |
| sampler2D _MainTex; | |
| float4 _MainTex_ST; | |
| fixed _Power; | |
| v2f vert (appdata v) | |
| { | |
| v2f o; | |
| o.vertex = UnityObjectToClipPos(v.vertex); | |
| o.uv = TRANSFORM_TEX(v.uv, _MainTex); | |
| UNITY_TRANSFER_FOG(o,o.vertex); | |
| return o; | |
| } | |
| fixed4 frag (v2f i) : SV_Target | |
| { | |
| // sample the texture | |
| fixed4 col = tex2D(_MainTex, i.uv) * _Power; | |
| // apply fog | |
| UNITY_APPLY_FOG(i.fogCoord, col); | |
| return col; | |
| } | |
| ENDCG | |
| } | |
| Pass | |
| { | |
| Blend SrcAlpha OneMinusSrcAlpha | |
| ZWrite Off | |
| Cull Back | |
| ColorMask RGB | |
| Stencil | |
| { | |
| Ref 0 | |
| Comp Equal | |
| WriteMask 255 | |
| ReadMask 255 | |
| //Pass IncrSat | |
| Pass Invert | |
| Fail Keep | |
| ZFail Keep | |
| } | |
| CGPROGRAM | |
| #pragma vertex vert | |
| #pragma fragment frag | |
| float4 _ShadowPlane; //渲染的地面 | |
| float4 _ShadowProjDir; //渲染的光源点,这里只是记录点的位置,用于计算阴影的投影 | |
| float4 _WorldPos; | |
| float _ShadowInvLen; //阴影的长度 | |
| float4 _ShadowFadeParams; //阴影渐变的参数 | |
| struct appdata | |
| { | |
| float4 vertex : POSITION; | |
| }; | |
| struct v2f | |
| { | |
| float4 vertex : SV_POSITION; | |
| float3 xlv_TEXCOORD0 : TEXCOORD0; | |
| float3 xlv_TEXCOORD1 : TEXCOORD1; | |
| }; | |
| v2f vert(appdata v) | |
| { | |
| v2f o; | |
| float3 lightdir = normalize(_ShadowProjDir); //单位化光照方向向量 | |
| float3 worldpos = mul(unity_ObjectToWorld, v.vertex).xyz; //模型坐标转成世界坐标 | |
| // _ShadowPlane.w = p0 * n // 平面的w分量就是p0 * n | |
| float distance = (_ShadowPlane.w - dot(_ShadowPlane.xyz, worldpos)) / dot(_ShadowPlane.xyz, lightdir.xyz); //计算阴影的长度 | |
| worldpos = worldpos + distance * lightdir.xyz; //影子的投影点 | |
| o.vertex = mul(unity_MatrixVP, float4(worldpos, 1.0)); | |
| o.xlv_TEXCOORD0 = _WorldPos.xyz; | |
| o.xlv_TEXCOORD1 = worldpos; //影子投影的点组成的纹理 | |
| return o; | |
| } | |
| float4 frag(v2f i) : SV_Target | |
| { | |
| float3 posToPlane_2 = (i.xlv_TEXCOORD0 - i.xlv_TEXCOORD1); | |
| float4 color; | |
| color.xyz = float3(0.0, 0.0, 0.0); | |
| color.w = (pow((1.0 - clamp(((sqrt(dot(posToPlane_2, posToPlane_2)) * _ShadowInvLen) - _ShadowFadeParams.x), 0.0, 1.0)), _ShadowFadeParams.y) * _ShadowFadeParams.z); //透明度渐变计算 | |
| return color; | |
| } | |
| ENDCG | |
| } | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://gameinstitute.qq.com/community/detail/128715