基于屏幕后期的线性高度雾实现学习——UnityShader学习笔记


自言自语

磨磨唧唧拖了好几天 才算把这个搞得稍微明白点了。 各种计算也弄懂了。。 大概90%吧。。
今天实在太困了就先传个图。明天把代码和注释写好再补上吧。。 加油啊 坚持啊 老骚年~
·························································································································
2021.2.26 凌晨 更新对之前理解不详细的地方 添加到注释C#的注释中

效果

在这里插入图片描述
在这里插入图片描述

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 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);
            fogMaterial.SetColor("_FogColor", fogColor);
            //传递存储射线的矩阵到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;

		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);
			//将雾的值乘以系数 并限制在01区间 方便纹理渲染采样
			fogDensity = saturate (fogDensity*_FogDensity);

			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
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值