泡灵泉修仙啦Unity基于后效的动态雾——UnityShader学习笔记

自言自语

惯例啰嗦一通。最近身体疲惫,睡眠不足。所以学习效率也极其低下。好在也算是今天又结束了一章节的学习。来交作业了。很简单。就是之前的后效雾加了个噪声贴图。。。没啥难点了 效果么 跟修仙游戏是的哈哈。 我自己早年前画的角色站在了灵海里 感受灵力慢慢变黄。。。。 嗯 还行吧 自己学习过程中的一些作业凑在一起 形成的画面 对自己也算是一个激励 继续努力~

效果

在这里插入图片描述

C#部分

就是加了几个变量。。一张贴图。。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[ExecuteInEditMode]
public class LinearFogWithDepthTexture : PostProcessWangBase
{

    public float fogStart = 0f;
    public float fogEnd =100f;

    [Range(0f, 10f)]
    public float fogDensity = 1f;

    [ColorUsage(true, true)]
    public Color fogColor = Color.white;

    public Texture NoiseTex;
    [Range(-0.5f,0.5f)]
    public float xNoiseSpeed = 0f;
    [Range(-0.5f,0.5f)]
    public float yNoiseSpeed = 0f;
    [Range(0,10f)]
    public float NoiseAmount = 0.1f;



    public Shader fogShader;
    private Material omaterial = null;
    public Material fogMaterial
    {
        get
        {
            if (omaterial == null)
            {
                omaterial = CheckShaderAndMaterial(fogShader, omaterial);
            }
            return omaterial;
        }
    }
 // 因为需要相机的一些参数作为计算 所以需要声明两个相机
 //第一个相机为Camera组件
    private Camera myCamera ;
    public Camera fogCamera
    {
        get
        {
            if (myCamera == null)
            {
                myCamera = GetComponent<Camera>();
            }
            return myCamera;
        }
    }
//第二个相机为相机的位置旋转等Transform参数
    private Transform myCameraTrans;
    public Transform fogCameraTrans
    {
        get
        {
            if(myCameraTrans == null )
            {
                myCameraTrans = fogCamera.transform;
            }
            return myCameraTrans;  
        }
    }
    //在激活可用状态下 获得深度值 
    private void OnEnable()
    {
        myCamera.depthTextureMode  |= DepthTextureMode.Depth;
    }

    protected void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if(fogMaterial == null )
        {
            Graphics.Blit(source, destination);
        }
        else
        {
            //声明一个单位矩阵用来存放后边需要用到的相应射线向量
            Matrix4x4 frustumCorners = Matrix4x4.identity;
            //获得相机的near值
            float near = fogCamera.nearClipPlane;
            // 通过  near*fov/2 公式来计算出近裁剪平面中心点到顶部的半高值   其中 Mathf.Deg2rad 为 角度转弧度 进行值的计算
            float heightHalf = near * (fogCamera.fieldOfView / 2 * Mathf.Deg2Rad);
            //用相机的向上方向的单位矢量 乘以半高值 得到 中心点到顶部的 向量
            Vector3 upCenter = fogCameraTrans.up * heightHalf;
            //同理 得到中心点到右边中点的向量 这里乘以的相机的宽高比 即已知高度 和宽高比 算出向右向量的值 再乘以向右方向的单位向量
            Vector3 rightCenter = fogCameraTrans.right * heightHalf*fogCamera.aspect;

            //根据向量的加减法 算出相应的四个角的向量 再通过相似三角形边长比相等定律 算出匹配的4个角相应射线
            //难点就在这里的理解和计算  花个视锥体的图 就一目了然 
            Vector3 TL = near*fogCameraTrans.forward + upCenter - rightCenter;

            //TL.magnitude 是指TL的模也就是TL的长度 根据相似三角形边长比相等 求出一个比值 可以通用用来计算TL射线到像素点的最终欧式距离 distance
            //********************************
            // deapth/distance = near/|TL|
            // deapth = distance*near/|TL|
            // distance = deapth*|TL|/near
            // scaler = |TL|/|near|
            // distance = scaler*depth 这一步是在shader中进行的 shader中可以获得深度值 用于重构当前像素的世界坐标以深度关联的表示
            // 好吧推导到这里真的不理解了 已经得到了 scaler为什么下边还要去计算 rayTL等射线长度呢? 直接去shader中乘以深度值不就算出了坐标偏移量了么  现在除了少了一个方向因素就行 那么方向因素 只需要乘以单位向量就行.
            // 这么一写又想明白了 所以 才有下边 归一化过后的单位向量 乘以 scaler的操作  所以要进行归一化操作
            //进行归一化操作,TL射线的单位向量
            TL.Normalize();
            float scaler = TL.magnitude / near;
            Vector3 rayTL = TL * scaler;

            Vector3 TR = near * fogCameraTrans.forward + upCenter + rightCenter;
            TR.Normalize();
            Vector3 rayTR = TR * scaler;

            Vector3 BL = near * fogCameraTrans.forward - upCenter - rightCenter;
            BL.Normalize();
            Vector3 rayBL = BL * scaler;

            Vector3 BR = near * fogCameraTrans.forward- upCenter + rightCenter;
            BR.Normalize();
            Vector3 rayBR = BR * scaler;

            //把射线存储在单位矩阵中 存储的射线信息是透视投影下的 注意ID顺序要和shader中的index判定一致
            frustumCorners.SetRow(0, BL);
            frustumCorners.SetRow(1, BR);
            frustumCorners.SetRow(2, TL);
            frustumCorners.SetRow(3, TR);
            //传递参数到shader
            fogMaterial.SetFloat("_FogStart", fogStart);
            fogMaterial.SetFloat("_FogEnd", fogEnd);
            fogMaterial.SetFloat("_FogDensity", fogDensity/10f);
            fogMaterial.SetColor("_FogColor", fogColor);
            fogMaterial.SetTexture("_NoiseTex", NoiseTex);
            fogMaterial.SetFloat("_NoiseAmount", NoiseAmount);
            fogMaterial.SetFloat("_XnoiseSpeed", xNoiseSpeed);
            fogMaterial.SetFloat("_YnoiseSpeed", yNoiseSpeed);
            //传递存储射线的矩阵到shader
            fogMaterial.SetMatrix("_FrustumMatrix", frustumCorners);
            //通过才shader的材质球对图像做处理 输出
            Graphics.Blit(source, destination, fogMaterial);
            
        }
    }

}

Shader部分

也就只加了两三行。。

Shader "TNShaderPractise/ShaderPractise_LinearFogWithDepthTexture"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue"="Transparent"  }

        CGINCLUDE

		#include "UnityCG.cginc"

		float _FogStart;
		float _FogEnd;
		float _FogDensity;
		fixed4 _FogColor;
		float4x4 _FrustumMatrix;
		sampler2D _MainTex;
		//声明图素 作为平台差异判断
		half4 _MainTex_TexelSize;
		//固定变量名称来接收深度图
		sampler2D _CameraDepthTexture;
		sampler2D _NoiseTex;
		float _NoiseAmount;
		float _XnoiseSpeed;
		float _YnoiseSpeed;

		struct a2v 
		{
			float4 vertex : POSITION;
			float4 texcoord : TEXCOORD0;
		};

		struct v2f 
		{
			float4 pos : SV_POSITION;
			half2 uv : TEXCOORD1;
			half2 uv_depth : TEXCOORD2;
			float3 interRay: TEXCOORD3;
		};

		v2f vert(a2v v )
		{
			v2f o;
			o.pos = UnityObjectToClipPos(v.vertex);
			o.uv = v.texcoord;
			o.uv_depth = v.texcoord;
			 //平台差异判断 对图进行正确采样坐标修正
			#if UNITY_UV_STARTS_AT_TOP
			if 	 (_MainTex_TexelSize.y<0)
			o.uv_depth.y = 1-o.uv_depth.y;
			#endif

			int index = 0;
			 //根据屏幕坐标的区域空间氛围4个块 然后以OPENGL原则为准 判断哪个角对应哪个射线 
			if (v.vertex.x<0.5&&v.vertex.y<0.5)
			{
				index = 0;
			}
			else if(v.vertex.x>0.5&&v.vertex.y<0.5) 
			{
				index = 1;
			}
			else if(v.vertex.x<0.5&&v.vertex.y>0.5)
			{
				index = 2;
			}
			else 
			{
				index = 3;
			}
			//再进行平台判断, 如果是DX平台就 反向取矩阵的行
		   #if UNITY_UV_STARTS_AT_TOP
		   if (_MainTex_TexelSize.y<0)
			index = 3-index;
			#endif
			 //尊重该像素的射线对应的应该是传递过来的矩阵里的射线 
			o.interRay = _FrustumMatrix[index];

			return o;

			
		}

		fixed4 frag(v2f i)	:SV_Target
		{
		//采样深度图获得深度值
			float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv_depth);
			//将深度值映射到01区间 线性空间  这样好做世界坐标空间下位置判断    此处为了使LinearEyeDepth有意义 则需要在CGINCLUDE 代码块中引用 "UnityCG.cginc"
			float linearDepth = LinearEyeDepth(depth);
			//得到世界空间下该点的深度值表示
			float3 worldPos = _WorldSpaceCameraPos+linearDepth*i.interRay;
		   //根据线性高度雾的计算公式 获得C#代码中传输过来的参数 算出雾的值
			float fogDensity =( _FogEnd-worldPos.y )/(_FogEnd-_FogStart);

			//=============动态噪声扰动雾代码就加这些===============================

				 //利用时间给定一个XY的速度   注意变量为float2  float的则会丢失Y的速度
			float2 speed = _Time.y*float2(_XnoiseSpeed,_YnoiseSpeed);
				//然后用这个速度进行噪声图的UV采样 并对采样的图进行一个强度增强
				//书中-0.5 测试时为了在调整强度的时候更好的进行一个雾块形状的体现 但是我不喜欢这个效果  所有去掉了 -0.5这步 计算  无伤大雅
				//当然也可以把这个0.5当一个参数开放出来供调整 
			//float noiseSpeed =(tex2D(_NoiseTex,i.uv+speed).r-0.5) *_NoiseAmount;
			float noiseSpeed =tex2D(_NoiseTex,i.uv+speed).r *_NoiseAmount;
			   //然后用这个速度去跟雾的强度去进行计算
			   //1+noiseSpeed 是防止速度为0时 没有雾   
			   //将雾的值乘以系数 并限制在01区间 方便纹理渲染采样
			fogDensity = saturate (fogDensity*_FogDensity*(1+noiseSpeed));

 
			//=============动态噪声扰动雾代码就加这些===============================


			fixed4 col = tex2D(_MainTex,i.uv);

			
			//对雾的颜色和屏幕纹理图 以雾的浓度为标准进行插值
			fixed4 finalColor = lerp(col,_FogColor,fogDensity);

			//输出图像 呈现雾效
			return finalColor;
		}
		ENDCG

		Pass 
		{
			ZWrite Off ZTest Always Cull Off

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			ENDCG
		}
    }
	FallBack Off
}

总结

粘贴复制 真是轻松加愉快。。。 偷个懒 就不默写了。。。。。。 打完收工! 睡觉睡觉!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值