{前端
连接:https://github.com/Claymoreno1/Grass-Wind-VolumeLight-Sky-Chan
}git
题外话{
一个月没有搞Unity,由于考试的缘由,以后又由于某些缘由搞了一个星期的前端,感受uniapp真是个好东西,提升开发效率神器,同时也感受前端有点无聊(接触unity的缘由? )
}
在shadertoy上看到了一个天空https://www.shadertoy.com/view/Msdfz8,感受和塞尔达传说的很像,因而就抄了过来。
以前作的天空,用的Simplex 噪声,是用一个球形的Mesh来把摄像机包裹起来,云的形态很是好,可是看起来并不真实,由于天空须要给人一望无际的感受,此次直接使用屏幕后处理,经过深度图重建世界坐标,分别绘制天空颜色,太阳,云效果比较好。
关于如何把GLSL转换成shaderlab,在动态天空实现篇已经讲过,相关文章也不少
算法上核心要素就是:用坐标点相对于摄像机的位移向量来作各类计算。
好比,这里i.FCray为该像素点的位置相对于摄像机的位移向量,越高或越低,y^2值也就越大,咱们经过这个来控制颜色渐变,而后,若是i.FCray.y小于0,那么咱们就让该像素颜色趋向于_Skycolor2github
float3 col = _Skycolor*1.1-i.FCray.y*i.FCray.y*0.5; col=lerp(col,_Skycolor2,pow(1.0-max(i.FCray.y,0.0),3.0));
经过这种方法,咱们已经把天空的底色绘制出来了。
太阳的位置,咱们先给定一个向量_Sunpos,用i.FCray与这个向量点乘,若是结果>0那就说明,夹角小于90°,根据这一点进行绘制便可。
云的绘制相对比较复杂,先根据云层给定不一样的高度,再经过FBM噪声对颜色进行插值,此次并无实时计算噪声,直接对噪声纹理进行采样,可是有次听大佬说纹理比复杂计算还要费,这个纹理能够根据须要用伪随机数替代。这里的FBM我没有照搬shadertoy上,进行了一些简化,噪声这块彻底能够参照女神的谈谈噪声
如下是C#与shaderlab的所有代码
C#部分就是重建世界坐标模板代码web
using System.Collections; using System.Collections.Generic; using UnityEngine; [ExecuteInEditMode] public class Sky : MonoBehaviour { public Material skymat; private Camera thiscamera; private Transform thiscameratrans; private void OnEnable() { thiscamera = this.GetComponent<Camera>(); thiscameratrans = this.transform; thiscamera.depthTextureMode |= DepthTextureMode.Depth; } [ImageEffectOpaque] private void OnRenderImage(RenderTexture source, RenderTexture destination) { SetRay(); Graphics.Blit(source, destination,skymat); } private void SetRay() { Matrix4x4 frustumCorners = Matrix4x4.identity;//返回单位矩阵 float fov = thiscamera.fieldOfView; float near = thiscamera.nearClipPlane; float aspect = thiscamera.aspect; float halfHeight = near * Mathf.Tan(fov * 0.5f * Mathf.Deg2Rad); Vector3 toRight =thiscameratrans.right * halfHeight * aspect; Vector3 toTop = thiscameratrans.up * halfHeight; Vector3 topLeft = thiscameratrans.forward * near + toTop - toRight; float scale = topLeft.magnitude / near; topLeft.Normalize(); topLeft *= scale; Vector3 topRight = thiscameratrans.forward * near + toRight + toTop; topRight.Normalize(); topRight *= scale; Vector3 bottomLeft = thiscameratrans.forward * near - toTop - toRight; bottomLeft.Normalize(); bottomLeft *= scale; Vector3 bottomRight = thiscameratrans.forward * near + toRight - toTop; bottomRight.Normalize(); bottomRight *= scale; frustumCorners.SetRow(0, bottomLeft); frustumCorners.SetRow(1, bottomRight); frustumCorners.SetRow(2, topRight); frustumCorners.SetRow(3, topLeft); skymat.SetMatrix("_FrustumCornersRay", frustumCorners); skymat.SetMatrix("_UnityMatVP", frustumCorners); } }
shaderlab部分,主要也是混合噪声,已经注释了不少,主要难点就在云的把控算法
Shader "myshaders/postsky" { Properties { _NoiseTex("Texture", 2D) = "white" {} _Skycolor("_Skycolor",color)=(1,1,1,1) _Skycolor2("_Skycolor2",color)=(1,1,1,1) _Sunpos("_Sunpos",vector)=(-0.8,0.4,-0.3,0) _Cloudcolor("_Cloudcolor",color)=(1.0,0.95,1.0) _Cloudspd("_Cloudspd",float)=1 _Layer("_Layer",float)=3 } SubShader { Pass { ZTest Always Cull Off ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; float4 FCray:TEXCOORD1; }; sampler2D _NoiseTex; float4 _NoiseTex_TexelSize; float4x4 _FrustumCornersRay; float4x4 _UnityMatVP; //float4 _Windway; sampler2D _CameraDepthTexture; float4 _Skycolor; float4 _Skycolor2; float4 _Sunpos; float4 _Cloudcolor; float _Cloudspd; float _Layer; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; #if UNITY_UV_STARTS_AT_TOP if(_NoiseTex_TexelSize.y<0) o.uv.y=1-o.uv.y; #endif int index =0; if(v.uv.x<0.5&&v.uv.y<0.5){ index=0; }else if(v.uv.x>0.5&&v.uv.y<0.5){ index=1; }else if(v.uv.x>0.5&&v.uv.y>0.5){ index=2; }else{ index=3; } #if UNITY_UV_STARTS_AT_TOP if(_NoiseTex_TexelSize.y<0) index=3-index; #endif o.FCray=_FrustumCornersRay[index]; return o; } float FBM(float2 p,float t){ float2 f=0.0; float s=0.5; for(int i=0;i<5;i++){ p+=t;//时间偏移 t*=1.5;//速度不一样 f+=s*tex2D(_NoiseTex,p/512).x; p*=2.0; s*=0.5; } return f; } fixed4 frag (v2f i) : SV_Target { //float linearDepth=LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv)); //float3 worldPos=_WorldSpaceCameraPos+linearDepth*i.FCray.xyz; i.FCray=normalize(i.FCray); //-----天空 //距离越远颜色越深,*1.1是为了让颜色不要太深 float3 col = _Skycolor*1.1-i.FCray.y*i.FCray.y*0.5; //为了把下面的颜色剔除,给一个制定的颜色 col=lerp(col,_Skycolor2,pow(1.0-max(i.FCray.y,0.0),3.0)); //-----/天空 //-----太阳 //经过向量点乘,肯定太阳位置A.*b>0说明夹角小于90° float sundot=clamp(dot(i.FCray.xyz,normalize(_Sunpos.xyz)),0.0,1.0); //感性计算,位置越正,rgb越趋近于1 col+=0.25*float3(1.0,0.7,0.4)*pow(sundot,5.0); col+=0.25*float3(1.0,0.8,0.6)*pow(sundot,64.0); col+=0.2*float3(1.0,0.8,0.6)*pow(sundot,512.0); //-----/太阳 //-----云 float3 campos=_WorldSpaceCameraPos; float time=_Time.y*0.05*_Cloudspd; float3 c=i.FCray.xyz; for(int i=0;i<_Layer;i++){ //给不一样的层不一样的高度 float2 sc=campos.xz+c.xz*((i+3)*40000.0-campos.y)/c.y; //噪声插值混色 col=lerp(col,_Cloudcolor,0.5*smoothstep(0.425,0.89,FBM(0.00006*sc,time*(i+3)))); } //把下面的云剔除 col=lerp(col,_Skycolor2,pow(1.0-max(c.y,0.0),16.0)); //-----/云 return float4(col,1); } ENDCG } } }