0x00 序言
项目需要模型在展示时,可以拥有类似全息投影的效果,类似一些科幻电影或者是高科技的场景。本文分析了如何利用凹凸纹理和边缘光来近似模拟这种效果。
0x01 效果图
0x02 基本概念
本次的shader部分主要修改了surface shader,其中的一些基本概念:
- SurfaceOutput是预定义的输出结构:
struct SurfaceOutput {
half3 Albedo; //纹理颜色(r,g,b)
half3 Normal; //法向量(x,y,z)
half3 Emission; //自发光颜色(r,g,b)
half Specular; //镜面反射度
half Gloss; //光泽度
half Alpha; //透明度
};
- UnpackNormal
UnpackNormal接受一个fixed4的输入,并将其转换为所对应的法线值(fixed3),并将其赋给输出的Normal。
o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
- saturate( )函数
作用其实也就是将取值转化为[0,1]之内的一个值。如果 x 小于 0,返回 0;如果 x 大于 1,返回1;否则,返回 x
0x03 Shader代码
下面的代码算是强撸出来的,参考了网上一些资料。有些细节还不是很明了,希望能倒逼自己花时间把所有原理吃透。
Shader "CoffeecatGame/Hologram" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
_BumpMap ("Bumpmap", 2D) = "bump" {}
// 边缘颜色
_RimColor("Rim Color", Color) = (0.26,0.7,1.0,0.0)
// 边缘颜色强度
_RimPower("Rim Power", Range(0.1,8.0)) = 3.0
// 像素 颜色强度系数
_RimIntensity("Rim Intensity", Range(0.1,8.0)) = 3.0
// 条纹粗细
_ClipPower("Clip Power", Range(0.0,301.0)) = 200.0
_Brightness("Brightness",Range(0.0,30)) = 1.5
_Alpha("Alpha", Range(0.0,1.0)) = 0.0
// 条纹颜色插值系数
_Crackle("Crackle", Range(0.0, 1.0)) = 0.0
}
SubShader {
//"RenderType" = "Transparent" 半透明着色器
//"Queue"="Transparent" 半透明物体渲染队列
//"IgnoreProjector"="True" 忽略投影
Tags { "RenderType" = "Transparent" "Queue"="Transparent" "IgnoreProjector"="True"}
Pass {
//ZWrite On 深度写入 开启
//ColorMask 0 设置颜色写遮罩 关闭所有颜色通道渲染
ZWrite On
ColorMask 0
}
CGPROGRAM
//Lambert,声明了表面着色器,并指定了光照模型。
#pragma surface surf Lambert alpha
struct Input {
float2 uv_MainTex;
float2 uv_BumpMap;
float3 viewDir;
float3 worldPos;
float4 screenPos;
};
sampler2D _MainTex;
sampler2D _BumpMap;
float4 _RimColor;
float _RimPower;
float _ClipPower;
float _Brightness;
float _Alpha;
float _RimIntensity;
float _Crackle;
void surf (Input IN, inout SurfaceOutput o)
{
float2 screenUV = IN.screenPos.xy / IN.screenPos.w;
// 扩散系数
float _DiffuseAmount = 1;
// 在贴图颜色与RimColor之间做插值
half4 basecol = lerp(tex2D(_MainTex, IN.uv_MainTex), _RimColor, _Crackle);
// 获取贴图颜色对应的灰度图
half3 graycol = dot(basecol.rgb, float3(0.3, 0.59, 0.11));
// frac 返回小数部分
if (frac(screenUV.y * _ClipPower) - 0.5 > 0)
_DiffuseAmount = 1;
else
_DiffuseAmount = 0;
if (_ClipPower == 0)
{
_DiffuseAmount = 1;
}
// 像素的颜色
o.Albedo = lerp(graycol, basecol, _DiffuseAmount);
// 从凹凸纹理提取法线信息
o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
// 从_RimColor参数获取自发光颜色(_Brightness是增强因子)
half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
o.Emission = lerp(_RimColor.rgb * pow (rim, _RimPower) * _Brightness, basecol, _DiffuseAmount) * _Brightness;
o.Alpha = _Alpha;
}
ENDCG
}
Fallback "Diffuse"
}
0x04 参考资料
【浅墨Unity3D Shader编程】之六 暗黑城堡篇: 表面着色器(Surface Shader)的写法(一)