You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
196 lines
7.0 KiB
196 lines
7.0 KiB
3 years ago
|
# 卡通水体的制作
|
||
|
|
||
|
话不多说,先上效果图
|
||
|
|
||
|
![avatar](./img/ToonWaterShader2.gif)
|
||
|
|
||
|
该水体编写时,用到了GrabPass抓取屏幕,深度纹理,法线变换,菲涅尔反射,噪声纹理,以及顶点动画相关的知识
|
||
|
|
||
|
本篇中,仅解释编写原理,源码会在最后方放出,后续会不定期更新文档。
|
||
|
|
||
|
## 水体部分
|
||
|
|
||
|
##### 1.透明物体的制作
|
||
|
|
||
|
众所周知,水是大自然中透明的物体之一,首先我们需要做一个透明的物体。
|
||
|
|
||
|
这时,我们可以用到GrabPass抓取屏幕与透明度混合两种方式编写。
|
||
|
|
||
|
但由于透明度混合无法对后面的物体产生更改,故无法很好的模拟折射效果。
|
||
|
|
||
|
GrabPass抓取到图像后,经过变换获取到的视口空间中的坐标,从而模拟透明图像,这种方式做出来的透明图像,可以进行更多的操作,比如uv变换等等,可以很方便的模拟折射效果。
|
||
|
|
||
|
##### 2.使用法线贴图
|
||
|
|
||
|
##### 3.模拟菲涅尔反射
|
||
|
|
||
|
##### 4.模拟折射
|
||
|
|
||
|
## 浪花部分
|
||
|
|
||
|
##### 1.深度纹理描边
|
||
|
|
||
|
##### 2.泛光特效
|
||
|
|
||
|
##### 3.使用噪声纹理模拟浪花
|
||
|
|
||
|
##
|
||
|
|
||
|
## 通过顶点动画让水体动起来
|
||
|
|
||
|
##### 1.sin函数顶点变换
|
||
|
|
||
|
##### 2.时间参数获取
|
||
|
|
||
|
##
|
||
|
|
||
|
## 全部代码
|
||
|
|
||
|
```c
|
||
|
//卡通水体
|
||
|
//深度纹理与菲涅尔反射的综合应用
|
||
|
Shader "Unlit/ToonWaterShader"
|
||
|
{
|
||
|
Properties
|
||
|
{
|
||
|
// 水体颜色
|
||
|
_WaterColor ("WaterColor", COLOR) = (1,1,1,1)
|
||
|
// 浪花深度
|
||
|
_DepthScale("Depth Scale",Float) = 0.1
|
||
|
// 波浪噪声纹理
|
||
|
_WaveNoise("WaveTexture",2D) = "white" {}
|
||
|
// 菲涅尔反射颜色
|
||
|
_ReflectColor ("Reflect Color",Color) = (0, 0, 1, 1)
|
||
|
// 浅水区颜色
|
||
|
_DepthColor ("Depth Color",Color) = (0, 0, 1, 1)
|
||
|
// 菲涅尔反射幅度
|
||
|
_FresnelScale ("Fresnel Scale", Range(0, 1)) = 1
|
||
|
// 水体法线
|
||
|
_BumpMap ("Normal Map", 2D) = "bump" {}
|
||
|
// 法线影响度
|
||
|
_BumpScale("Bump Scale" ,Range(0,1)) = 1
|
||
|
}
|
||
|
SubShader
|
||
|
{
|
||
|
Tags { "RenderType"="Opaque" "Queue"="Transparent" }
|
||
|
LOD 100
|
||
|
//抓取屏幕图像,存在名为_RefractionTex的变量中
|
||
|
GrabPass { "_RefractionTex" }
|
||
|
Pass
|
||
|
{
|
||
|
CGPROGRAM
|
||
|
#pragma vertex vert
|
||
|
#pragma fragment frag
|
||
|
|
||
|
#include "UnityCG.cginc"
|
||
|
// 声明抓取的变量
|
||
|
sampler2D _RefractionTex;
|
||
|
float4 _RefractionTex_TexelSize;
|
||
|
// 声明深度图
|
||
|
sampler2D _CameraDepthTexture;
|
||
|
//声明Properties各项变量
|
||
|
sampler2D _WaveNoise;
|
||
|
float4 _WaveNoise_ST;
|
||
|
float _DepthScale;
|
||
|
fixed4 _WaterColor;
|
||
|
fixed4 _ReflectColor;
|
||
|
fixed _FresnelScale;
|
||
|
fixed4 _DepthColor;
|
||
|
|
||
|
sampler2D _BumpMap;
|
||
|
float4 _BumpMap_ST;
|
||
|
float _BumpScale;
|
||
|
|
||
|
//定义顶点着色器结构体
|
||
|
struct appdata
|
||
|
{
|
||
|
float4 vertex : POSITION;
|
||
|
float2 uv : TEXCOORD0;
|
||
|
float4 tangent : TANGENT;
|
||
|
float3 normal : NORMAL;
|
||
|
};
|
||
|
//定义片元着色器结构体
|
||
|
struct v2f
|
||
|
{
|
||
|
float2 uv : TEXCOORD0;
|
||
|
float4 vertex : SV_POSITION;
|
||
|
float4 scrPos : TEXCOORD1;
|
||
|
float3 worldPos : TEXCOORD2;
|
||
|
fixed3 worldNormal : TEXCOORD3;
|
||
|
fixed3 worldViewDir : TEXCOORD4;
|
||
|
//TBN矩阵
|
||
|
half3 wNormal : TEXCOORD5;
|
||
|
half3 wTangent : TEXCOORD6;
|
||
|
half3 wBitangent : TEXCOORD7;
|
||
|
};
|
||
|
|
||
|
v2f vert (appdata v)
|
||
|
{
|
||
|
v2f o;
|
||
|
//定义偏移参数,根据时间变换顶点,使x轴顶点按照sin曲线运动
|
||
|
float4 offset;
|
||
|
offset.xyzw = float4(0.0,0.0, 0.0, 0.0);
|
||
|
//offset.x = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength) * _Magnitude;
|
||
|
offset.x = cos(_Time.y + v.vertex.z)*0.3;
|
||
|
o.vertex = UnityObjectToClipPos(v.vertex+offset);
|
||
|
//
|
||
|
o.uv = v.uv;
|
||
|
o.scrPos = ComputeGrabScreenPos(o.vertex);
|
||
|
o.worldNormal = UnityObjectToWorldNormal(v.normal);
|
||
|
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
|
||
|
o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);
|
||
|
|
||
|
//计算TBN矩阵所用的三组变量。
|
||
|
o.wTangent = UnityObjectToWorldDir(v.tangent.xyz);
|
||
|
o.wNormal = UnityObjectToWorldNormal(v.normal);
|
||
|
// compute bitangent from cross product of normal and tangent
|
||
|
// 通过计算法线与切线的叉积,得到二次切线bitangent,叉积*切线方向
|
||
|
// half tangentSign = v.tangent.w * unity_WorldTransformParams.w;
|
||
|
// output the tangent space matrix
|
||
|
half tangentSign = v.tangent.w;
|
||
|
o.wBitangent = cross(o.wNormal, o.wTangent) * tangentSign;
|
||
|
return o;
|
||
|
}
|
||
|
|
||
|
fixed4 frag (v2f i) : SV_Target
|
||
|
{
|
||
|
fixed3 worldNormal = normalize(i.worldNormal);
|
||
|
fixed3 worldViewDir = normalize(i.worldViewDir);
|
||
|
|
||
|
half3 tnormal = UnpackNormal(tex2D(_BumpMap, i.uv));
|
||
|
float3x3 TBNMatrix = float3x3(i.wTangent,i.wBitangent,i.wNormal);
|
||
|
worldNormal = mul(tnormal,TBNMatrix);
|
||
|
//half3 worldViewDir = UnityWorldSpaceViewDir(i.worldPos);
|
||
|
half3 worldRefra = refract(worldViewDir,worldNormal,_BumpScale);
|
||
|
float2 offset = worldRefra.xy;
|
||
|
//i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;
|
||
|
|
||
|
// sample the texture
|
||
|
//fixed4 refrCol = tex2D(_RefractionTex,i.uv);
|
||
|
fixed4 refrCol = tex2D(_CameraDepthTexture, i.scrPos.xy/i.scrPos.w);
|
||
|
i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;
|
||
|
fixed4 refrColScreen = tex2D(_RefractionTex,i.scrPos.xy/i.scrPos.w);
|
||
|
|
||
|
fixed4 WaveNoise = tex2D(_WaveNoise,i.uv);
|
||
|
|
||
|
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);
|
||
|
float linearDepth = LinearEyeDepth(refrCol.r);
|
||
|
float diff = linearDepth - i.scrPos.w;
|
||
|
fixed4 intersect = fixed4(1,1,1,1)-fixed4(diff*_DepthScale,diff*_DepthScale,diff*_DepthScale,1);
|
||
|
fixed4 border = floor(saturate(intersect+WaveNoise)*2);
|
||
|
|
||
|
fixed4 depthColor = (1-saturate(diff)) * _DepthColor;
|
||
|
|
||
|
fixed4 reflection = _ReflectColor;
|
||
|
fixed fresnel = _FresnelScale + (1 - _FresnelScale) * pow(1 - dot(worldViewDir, worldNormal), 5)*_BumpScale;
|
||
|
fixed4 diffuse = _WaterColor*refrColScreen+depthColor+border;
|
||
|
|
||
|
fixed4 colorfinal = lerp(diffuse, reflection, saturate(fresnel));
|
||
|
return colorfinal;
|
||
|
}
|
||
|
ENDCG
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|