一、思路
仍然需要屏幕后处理效果,和之前的雾效果实现基本一样(全局雾效计算),就是多算了一个噪声值混合雾效果的浓度 用来插值原始颜色和雾颜色。
二、具体实现代码:
摄像机脚本代码:
using UnityEngine;
using System.Collections;
public class FogWithNoise : PostEffectsBase {
public Shader fogShader;
private Material fogMaterial = null;
public Material material {
get {
fogMaterial = CheckShaderAndCreateMaterial(fogShader, fogMaterial);
return fogMaterial;
}
}
private Camera myCamera;
public Camera camera {
get {
if (myCamera == null) {
myCamera = GetComponent<Camera>();
}
return myCamera;
}
}
private Transform myCameraTransform;
public Transform cameraTransform {
get {
if (myCameraTransform == null) {
myCameraTransform = camera.transform;
}
return myCameraTransform;
}
}
[Range(0.1f, 3.0f)]
public float fogDensity = 1.0f; //雾的浓度
public Color fogColor = Color.white; //雾颜色
public float fogStart = 0.0f; //起始高度,我们使用的雾效果是基于高度的
public float fogEnd = 2.0f; //结束高度
public Texture noiseTexture; //噪声纹理
[Range(-0.5f, 0.5f)]
public float fogXSpeed = 0.1f; //噪声纹理在x、y轴的飘动速度
[Range(-0.5f, 0.5f)]
public float fogYSpeed = 0.1f;
[Range(0.0f, 3.0f)]
public float noiseAmount = 1.0f; //噪声程度,0的话就不应用噪声纹理,
void OnEnable() {
GetComponent<Camera>().depthTextureMode |= DepthTextureMode.Depth; //设置摄像机模式
}
void OnRenderImage (RenderTexture src, RenderTexture dest) { //屏幕处理函数
if (material != null) {
Matrix4x4 frustumCorners = Matrix4x4.identity; //存储近裁剪平面的4个角对应的变量
float fov = camera.fieldOfView; //和之前的雾效很多的相似一样
float near = camera.nearClipPlane;
float aspect = camera.aspect;
float halfHeight = near * Mathf.Tan(fov * 0.5f * Mathf.Deg2Rad);
Vector3 toRight = cameraTransform.right * halfHeight * aspect;
Vector3 toTop = cameraTransform.up * halfHeight;
Vector3 topLeft = cameraTransform.forward * near + toTop - toRight;
float scale = topLeft.magnitude / near;
topLeft.Normalize();
topLeft *= scale;
Vector3 topRight = cameraTransform.forward * near + toRight + toTop;
topRight.Normalize();
topRight *= scale;
Vector3 bottomLeft = cameraTransform.forward * near - toTop - toRight;
bottomLeft.Normalize();
bottomLeft *= scale;
Vector3 bottomRight = cameraTransform.forward * near + toRight - toTop;
bottomRight.Normalize();
bottomRight *= scale;
frustumCorners.SetRow(0, bottomLeft);
frustumCorners.SetRow(1, bottomRight);
frustumCorners.SetRow(2, topRight);
frustumCorners.SetRow(3, topLeft);
material.SetMatrix("_FrustumCornersRay", frustumCorners); //传入Shader中
material.SetFloat("_FogDensity", fogDensity);
material.SetColor("_FogColor", fogColor);
material.SetFloat("_FogStart", fogStart);
material.SetFloat("_FogEnd", fogEnd);
material.SetTexture("_NoiseTex", noiseTexture); //获得噪声纹理
material.SetFloat("_FogXSpeed", fogXSpeed);
material.SetFloat("_FogYSpeed", fogYSpeed);
material.SetFloat("_NoiseAmount", noiseAmount); //传入噪声影响程度
Graphics.Blit (src, dest, material);
} else {
Graphics.Blit(src, dest);
}
}
}
Shader代码:
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shaders Book/Chapter 15/Fog With Noise" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {} //属性变量
_FogDensity ("Fog Density", Float) = 1.0
_FogColor ("Fog Color", Color) = (1, 1, 1, 1)
_FogStart ("Fog Start", Float) = 0.0
_FogEnd ("Fog End", Float) = 1.0
_NoiseTex ("Noise Texture", 2D) = "white" {}
_FogXSpeed ("Fog Horizontal Speed", Float) = 0.1
_FogYSpeed ("Fog Vertical Speed", Float) = 0.1
_NoiseAmount ("Noise Amount", Float) = 1
}
SubShader {
CGINCLUDE
#include "UnityCG.cginc"
float4x4 _FrustumCornersRay; //得到位置后用于存储射线
sampler2D _MainTex;
half4 _MainTex_TexelSize;
sampler2D _CameraDepthTexture;
half _FogDensity;
fixed4 _FogColor;
float _FogStart;
float _FogEnd;
sampler2D _NoiseTex;
half _FogXSpeed;
half _FogYSpeed;
half _NoiseAmount;
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float2 uv_depth : TEXCOORD1;
float4 interpolatedRay : TEXCOORD2;
};
v2f vert(appdata_img 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;
if (v.texcoord.x < 0.5 && v.texcoord.y < 0.5) {
index = 0;
} else if (v.texcoord.x > 0.5 && v.texcoord.y < 0.5) {
index = 1;
} else if (v.texcoord.x > 0.5 && v.texcoord.y > 0.5) {
index = 2;
} else {
index = 3;
}
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
index = 3 - index;
#endif
o.interpolatedRay = _FrustumCornersRay[index];
return o;
}
fixed4 frag(v2f i) : SV_Target {
float linearDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth));
float3 worldPos = _WorldSpaceCameraPos + linearDepth * i.interpolatedRay.xyz;
//和之前一样通过射线方向和取到的获取到顶点位置
float2 speed = _Time.y * float2(_FogXSpeed, _FogYSpeed);
//得到偏移,注意时间
float noise = (tex2D(_NoiseTex, i.uv + speed).r - 0.5) * _NoiseAmount;
//采样噪声纹理,-0.5,再乘以_NoiseAmount得到最终的噪声值
float fogDensity = (_FogEnd - worldPos.y) / (_FogEnd - _FogStart);
fogDensity = saturate(fogDensity * _FogDensity * (1 + noise));
//模拟雾效浓度(将噪声值加入)
fixed4 finalColor = tex2D(_MainTex, i.uv);
finalColor.rgb = lerp(finalColor.rgb, _FogColor.rgb, fogDensity);
return finalColor;
}
ENDCG
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
FallBack Off
}
三、以上所有的噪声纹理是如何被制作出来的
噪声纹理也可以被认为是一种程序纹理,他们都是由计算机利用某些算法生成的,
Perlin噪声和Worley噪声是最常用的两种噪声。
Perlin噪声是可以用来生成一些比较自然的纹理,
Worley噪声通常用来模拟诸如石头、水、纸张等多孔噪声。
现在的很多软件,都提供了类似功能插件,用来帮助美术人员生成需要的噪声纹理。