实现简易的水墨风效果大致分为三部分:
1.将原始的rgb贴图转化为灰度图
float4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);
float texGrey = (baseMap.r + baseMap.g + baseMap.b) * 0.33;
texGrey = pow(texGrey, 0.3);
texGrey *= 1 - cos(texGrey * 3.14);
2.使用一张水墨笔触的贴图,同样转化为灰度图,并和原始贴图的灰度进行混合。
用枚举的方式设置不同的混合方式:
[Enum(Opacity, 1, Darken, 2, Lighten, 3, Multiply, 4, Screen, 5, Overlay, 6, SoftLight, 7)]
_BlendType ("Blend Type", Int) = 1
基于PS的图层叠加算法:
float blend;
if (_BlendType == 1)
blend = texGrey * 0.5 + brushGrey * 0.5;
else if (_BlendType == 2)
blend = texGrey < brushGrey ? texGrey : brushGrey;
else if (_BlendType == 3)
blend = texGrey > brushGrey ? texGrey : brushGrey;
else if (_BlendType == 4)
blend = texGrey * brushGrey;
else if (_BlendType == 5)
blend = 1 - (1 - texGrey) * (1 - brushGrey);
else if (_BlendType == 6)
blend = brushGrey > 0.5 ? 1 - 2 * (1 - texGrey) * (1 - brushGrey) : 2 * texGrey * brushGrey;
else if (_BlendType == 7)
blend = brushGrey > 0.5 ? (2 * texGrey - 1) * (brushGrey - brushGrey * brushGrey) + brushGrey : (2 * texGrey - 1) * (sqrt(brushGrey) - brushGrey) + brushGrey;
float4 col = float4(blend, blend, blend, 1.0);
3.根据菲涅尔效果来进行描边处理
float vdotn = dot(IN.viewDirWS, IN.normalWS);
float edge = pow(vdotn, 1) / _Range;
edge = edge > _Thred ? 1 : edge;
edge = pow(edge, _Pow);
float4 edgeCol = float4(edge, edge, edge, edge);
整体效果实现主要参考自这篇文章:【Unity Shader】 水墨风格渲染:如何优雅的画一只猴子 - 知乎x
下面附上全部代码:
扫描二维码关注公众号,回复:
17282614 查看本文章
![](/qrcode.jpg)
Shader "Unlit/ShuiMo"
{
Properties
{
_BaseMap ("Base Texture", 2D) = "white" {}
_BaseColor ("Base Color", Color) = (1,1,1,1)
_BrushMap ("Brush Texture", 2D) = "White" {}
[Enum(Opacity, 1, Darken, 2, Lighten, 3, Multiply, 4, Screen, 5, Overlay, 6, SoftLight, 7)]
_BlendType ("Blend Type", Int) = 1
_Thred ("Edge Threshold", Range(0.01, 1)) = 0.25
_Range ("Edge Range", Range(1, 10)) = 1
_Pow ("Edge Intensity", Range(0, 10)) = 1
}
SubShader
{
Tags
{
"RenderPipeline"="UniversalPipeline"
"Queue"="Geometry"
"RenderType"="Opaque"
}
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
half4 _BaseColor;
float4 _BrushMap_ST;
int _BlendType;
float _Thred;
float _Range;
float _Pow;
CBUFFER_END
ENDHLSL
Pass
{
Tags { "LightMode"="UniversalForward" }
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
struct Attributes
{
float4 positionOS : POSITION;
float4 normalOS : NORMAL;
float2 uv : TEXCOORD;
};
struct Varings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
float3 positionWS : TEXCOORD1;
float3 viewDirWS : TEXCOORD2;
float3 normalWS : TEXCOORD3;
};
TEXTURE2D(_BaseMap);
TEXTURE2D(_BrushMap);
SAMPLER(sampler_BaseMap);
Varings vert(Attributes IN)
{
Varings OUT = (Varings)0;
OUT.positionCS = TransformObjectToHClip(IN.positionOS.xyz);
OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);
OUT.positionWS = TransformObjectToWorld(IN.positionOS.xyz);
OUT.viewDirWS = GetCameraPositionWS() - OUT.positionWS;
OUT.normalWS = TransformObjectToWorldNormal(IN.normalOS.xyz);
return OUT;
}
float4 frag(Varings IN) : SV_Target
{
float4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);
float texGrey = (baseMap.r + baseMap.g + baseMap.b) * 0.33;
texGrey = pow(texGrey, 0.3);
texGrey *= 1 - cos(texGrey * 3.14);
float4 brushMap = SAMPLE_TEXTURE2D(_BrushMap, sampler_BaseMap, IN.uv);
float brushGrey = (brushMap.r + brushMap.g + brushMap.b) * 0.33;
float blend;
if (_BlendType == 1)
blend = texGrey * 0.5 + brushGrey * 0.5;
else if (_BlendType == 2)
blend = texGrey < brushGrey ? texGrey : brushGrey;
else if (_BlendType == 3)
blend = texGrey > brushGrey ? texGrey : brushGrey;
else if (_BlendType == 4)
blend = texGrey * brushGrey;
else if (_BlendType == 5)
blend = 1 - (1 - texGrey) * (1 - brushGrey);
else if (_BlendType == 6)
blend = brushGrey > 0.5 ? 1 - 2 * (1 - texGrey) * (1 - brushGrey) : 2 * texGrey * brushGrey;
else if (_BlendType == 7)
blend = brushGrey > 0.5 ? (2 * texGrey - 1) * (brushGrey - brushGrey * brushGrey) + brushGrey: (2 * texGrey - 1) * (sqrt(brushGrey) - brushGrey) + brushGrey;
float4 col = float4(blend, blend, blend, 1.0);
float vdotn = dot(IN.viewDirWS, IN.normalWS);
float edge = pow(vdotn, 1) / _Range;
edge = edge > _Thred ? 1 : edge;
edge = pow(edge, _Pow);
float4 edgeCol = float4(edge, edge, edge, edge);
col = (edgeCol * (1 - edgeCol.a) + col * edgeCol.a) * _BaseColor;
return col;
}
ENDHLSL
}
}
}