您的位置:首页 > 移动开发 > Unity3D

Unity_Shader高级篇_12.3_后处理_屏幕水波效果

2018-03-29 11:28 573 查看
水波纹的原理其实比较简单,与其他后处理效果一样,vertex shader千篇一律,所有操作都有fragment shader完成。我们仔细观察一下上图中的效果,其实这种波纹并不是叠加上去的,而是波纹位置的图像被向外拉伸了,而这种拉伸的强度是按照类似sin函数的波形进行拉伸的。

(1)新建场景(Scene_12_8)。

(2)新建一个脚本(WaterWave .cs)。并拖拽到摄像机上。

(3)新建一个Unity Shader(Chapter12-WaterWave )。



using UnityEngine;
using System.Collections;

public class WaterWave : PostEffectsBase {

public Shader bloomShader;
private Material bloomMaterial = null;
public Material material
{
get
{
bloomMaterial = CheckShaderAndCreateMaterial(bloomShader, bloomMaterial);
return bloomMaterial;
}
}
//距离系数
public float distanceFactor = 60.0f;
//时间系数
public float timeFactor = -30.0f;
//sin函数结果系数
public float totalFactor = 1.0f;

//波纹宽度
public float waveWidth = 0.3f;
//波纹扩散的速度
public float waveSpeed = 0.3f;

private float waveStartTime;
private Vector4 startPos = new Vector4(0.5f, 0.5f, 0, 0);

void OnRenderImage(RenderTexture source, RenderTexture destination)
{
//计算波纹移动的距离,根据enable到目前的时间*速度求解
float curWaveDistance = (Time.time - waveStartTime) * waveSpeed;
//设置一系列参数
material.SetFloat("_distanceFactor", distanceFactor);
material.SetFloat("_timeFactor", timeFactor);
material.SetFloat("_totalFactor", totalFactor);
material.SetFloat("_waveWidth", waveWidth);
material.SetFloat("_curWaveDis", curWaveDistance);
material.SetVector("_startPos", startPos);
Graphics.Blit(source, destination, material);
}

void Update()
{
if (Input.GetMouseButton(0))
{
Vector2 mousePos = Input.mousePosition;
//将mousePos转化为(0,1)区间
startPos = new Vector4(mousePos.x / Screen.width, mousePos.y / Screen.height, 0, 0);
waveStartTime = Time.time;
}

}
}




Shader "Custom/Chapter12-WaterWave" {
Properties
{
_MainTex("Base (RGB)", 2D) = "white" {}
}

CGINCLUDE
#include "UnityCG.cginc"
uniform sampler2D _MainTex;
float4 _MainTex_TexelSize;
uniform float _distanceFactor;
uniform float _timeFactor;
uniform float _totalFactor;
uniform float _waveWidth;
uniform float _curWaveDis;
uniform float4 _startPos;

fixed4 frag(v2f_img i) : SV_Target
{
//DX下纹理坐标反向问题
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
_startPos.y = 1 - _startPos.y;
#endif
//计算uv到中间点的向量(向外扩,反过来就是向里缩)
float2 dv = _startPos.xy - i.uv;
//按照屏幕长宽比进行缩放
dv = dv * float2(_ScreenParams.x / _ScreenParams.y, 1);
//计算像素点距中点的距离
float dis = sqrt(dv.x * dv.x + dv.y * dv.y);
//用sin函数计算出波形的偏移值factor
//dis在这里都是小于1的,所以我们需要乘以一个比较大的数,比如60,这样就有多个波峰波谷
//sin函数是(-1,1)的值域,我们希望偏移值很小,所以这里我们缩小100倍,据说乘法比较快,so...
float sinFactor = sin(dis * _distanceFactor + _Time.y * _timeFactor) * _totalFactor * 0.01;
//距离当前波纹运动点的距离,如果小于waveWidth才予以保留,否则已经出了波纹范围,factor通过clamp设置为0
float discardFactor = clamp(_waveWidth - abs(_curWaveDis - dis), 0, 1) / _waveWidth;
//归一化
float2 dv1 = normalize(dv);
//计算每个像素uv的偏移值
float2 offset = dv1  * sinFactor * discardFactor;
//像素采样时偏移offset
float2 uv = offset + i.uv;
return tex2D(_MainTex, uv);
}

ENDCG

SubShader
{
Pass
{
ZTest Always
Cull Off
ZWrite Off
Fog{ Mode off }

CGPROGRAM
#pragma vertex vert_img
#pragma fragment f
4000
rag
#pragma fragmentoption ARB_precision_hint_fastest
ENDCG
}
}
Fallback off
}


PS:关于点击屏幕会出现水波位置Y轴反向的问题已经修正。在DX下由于uv纹理坐标与GL下Y轴反向,Unity会为我们处理大部分情况,但是当开启后处理,并且使用了抗锯齿的时候,直接从外界传来的屏幕空间坐标转化到输入的RT中时,Unity自然不会为我们处理得这么详尽,就可能出现想要的效果Y方向上反向的问题。所以Unity提供了一个#if UNITY_UV_STARTS_AT_TOP宏,我们在shader中增加这个编译宏,就可以了。关于#if UNITY_UV_STARTS_AT_TOP宏,上篇文章已经给了完美的解释了。。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: