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.
7.0 KiB
7.0 KiB
卡通水体的制作
话不多说,先上效果图
该水体编写时,用到了GrabPass抓取屏幕,深度纹理,法线变换,菲涅尔反射,噪声纹理,以及顶点动画相关的知识
本篇中,仅解释编写原理,源码会在最后方放出,后续会不定期更新文档。
水体部分
1.透明物体的制作
众所周知,水是大自然中透明的物体之一,首先我们需要做一个透明的物体。
这时,我们可以用到GrabPass抓取屏幕与透明度混合两种方式编写。
但由于透明度混合无法对后面的物体产生更改,故无法很好的模拟折射效果。
GrabPass抓取到图像后,经过变换获取到的视口空间中的坐标,从而模拟透明图像,这种方式做出来的透明图像,可以进行更多的操作,比如uv变换等等,可以很方便的模拟折射效果。
2.使用法线贴图
3.模拟菲涅尔反射
4.模拟折射
浪花部分
1.深度纹理描边
2.泛光特效
3.使用噪声纹理模拟浪花
通过顶点动画让水体动起来
1.sin函数顶点变换
2.时间参数获取
全部代码
//卡通水体
//深度纹理与菲涅尔反射的综合应用
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
}
}
}