自言自语
惯例啰嗦一通。最近身体疲惫,睡眠不足。所以学习效率也极其低下。好在也算是今天又结束了一章节的学习。来交作业了。很简单。就是之前的后效雾加了个噪声贴图。。。没啥难点了 效果么 跟修仙游戏是的哈哈。 我自己早年前画的角色站在了灵海里 感受灵力慢慢变黄。。。。 嗯 还行吧 自己学习过程中的一些作业凑在一起 形成的画面 对自己也算是一个激励 继续努力~
效果
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);
//将深度值映射到0到1区间 线性空间 这样好做世界坐标空间下位置判断 此处为了使LinearEyeDepth有意义 则需要在CGINCLUDE 代码块中引用 "UnityCG.cginc"
float linearDepth = LinearEyeDepth(depth);
//得到世界空间下该点的深度值表示
float3 worldPos = _WorldSpaceCameraPos+linearDepth*i.interRay;
//根据线性高度雾的计算公式 获得C#代码中传输过来的参数 算出雾的值
float fogDensity =( _FogEnd-worldPos.y )/(_FogEnd-_FogStart);
//=============动态噪声扰动雾代码就加这些===============================
//利用时间给定一个X和Y的速度 注意变量为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时 没有雾
//将雾的值乘以系数 并限制在0到1区间 方便纹理渲染采样
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
}
总结
粘贴复制 真是轻松加愉快。。。 偷个懒 就不默写了。。。。。。 打完收工! 睡觉睡觉!!!