【UnityShader】常用效果内外发光、 描边
这篇博客记录一下常用的几种效果:内发光、外发光以及描边的几种实现方法
内发光
实现方法
一般实现方法就是使用Empricial菲涅尔近似公式来实现:
F (v,n)=saturate(base + pow(scale * (1 - v·n), power))
- base为基础反射率,scale为菲涅尔效应强弱,power为菲涅尔效应作用角度范围大小(即power越大,反射率会在越小的范围内迅速变为1)
- 在计算菲涅尔效应时,使用F对反射和基础颜色值进行插值混合,在这里计算内发光,就对发光颜色和基础颜色进行插值混合。
half shine = max(pow(1-dot(normal, viewDir), _ShinePower * 10), 0.001) * _ShineScale;
col.rgb = lerp(col.rgb, _ShineColor.rgb, shine);
实现代码
Shader "Custom/Effect/Shine_Out"
{Properties{_Color ("Main Color", Color) = (1,1,1,1)_ShineColor ("Shine Color", Color) = (1,1,1,1)_ShineScale ("Shine Scale", Range(0, 1)) = 0.5_ShinePower ("Shine Power", Range(0, 1)) = 0.5}SubShader{Tags { "Queue"="Transparent" }LOD 200Pass{Blend SrcAlpha OneMinusSrcAlphaCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#include "Lighting.cginc"half4 _Color;half4 _ShineColor;float _ShineScale;float _ShinePower;struct a2v{float4 vertex : POSITION;float3 normal : NORMAL;};struct v2f{float4 pos : SV_POSITION;float3 normal : TEXCOORD0;float3 worldPos : TEXCOORD1;};v2f vert (a2v v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.normal = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(unity_ObjectToWorld, v.vertex);return o;}fixed4 frag (v2f i) : SV_Target{half3 normal = normalize(i.normal);half3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));half3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));fixed4 col = fixed4(0,0,0,1);col.rgb = _LightColor0.rgb * _Color.rgb * (dot(lightDir, normal)*0.5+0.5);half shine = max(pow(1-dot(normal, viewDir), _ShinePower * 10), 0.001) * _ShineScale;col.rgb = lerp(col.rgb, _ShineColor.rgb, shine);return col;}ENDCG}}
}
描边
内发光方法实现
- 沿用上文写的内发光实现方法,仅在内发光的值上做mask或者数值限制。这里仅使用step做限制,需要更风格化的效果需要制作mask做描边的限制。
half shine = max(pow(1-dot(normal, viewDir), _ShinePower * 10), 0.001) * _ShineScale;
shine = step(_Threshold, shine);
col.rgb = lerp(col.rgb, _ShineColor.rgb, shine);
- 阈值为新增变量,当内发光的渐变范围值大于阈值时,显示为描边颜色,小于阈值则显示为原颜色
- 为了显示清楚描边,图中去掉了原本的光照计算
- 优点
- 实现简单,可以在一个Pass里实现,性能消耗小
- 缺点
- 只能向内描边,且描边宽度受视角与法线方向影响,不易控制
沿法线外扩背面方法实现
这种方法比较常用,用两个Pass实现,一个Pass只渲染背面,且把物体背面沿法线方向向外扩张,第二个Pass正常渲染物体。
v2f vert (appdata v) {v2f o;float4 viewPos = float4(UnityObjectToViewPos(v.vertex), 1.0);float3 viewNormal = mul(UNITY_MATRIX_IT_MV, v.normal);viewNormal.z = -0.5;//防止内凹模型背面扩张后遮挡正面float3 normal = normalize(viewNormal);viewPos += float4(normal, 1.0) * _OutlineWidth;o.pos = mul(UNITY_MATRIX_P, viewPos);o.worldPos = mul(unity_ObjectToWorld, v.vertex);o.normal = UnityObjectToWorldNormal(v.normal);return o; }
效果图如下
优点
- 实现简单,大部分模型的描边效果都很不错
缺点
- 对于例如正方体这样平整且转折锐利的物体无法使用
- 多使用了一个Pass,渲染性能消耗提高了不少
全部代码如下
Shader "Custom/Effect/Outline" {Properties{_Color ("Main Color", Color) = (1,1,1,1)[HDR]_OutlineColor ("Outline Color", Color) = (1,1,1,1)_OutlineWidth ("Outline Width", Range(0, 2)) = 1}SubShader{Tags { "Queue"="Transparent" }LOD 200Pass{Cull FrontBlend SrcAlpha OneMinusSrcAlphaCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float3 normal : NORMAL;};struct v2f{float4 pos : SV_POSITION;};half4 _OutlineColor;float _OutlineWidth;v2f vert (appdata v){v2f o;float4 viewPos = float4(UnityObjectToViewPos(v.vertex), 1.0);float3 viewNormal = mul(UNITY_MATRIX_IT_MV, v.normal);viewNormal.z = -0.5;float3 normal = normalize(viewNormal);viewPos += float4(normal, 1.0) * _OutlineWidth;o.pos = mul(UNITY_MATRIX_P, viewPos);return o;}fixed4 frag (v2f i) : SV_Target{return _OutlineColor;}ENDCG}Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;};struct v2f{float4 pos : SV_POSITION;};half4 _Color;v2f vert (appdata v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);return o;}fixed4 frag (v2f i) : SV_Target{fixed4 col = _Color;return col;}ENDCG}} }
图像处理方法实现
基于后处理图像处理的边缘检测方法:常用的一般是Sobel算子和Roberts算子,边缘处的梯度绝对值会较大,也就是计算每个像素的梯度,并用梯度对初始颜色和描边颜色进行插值,也就完成了描边的操作。
half Sobel(v2f i) {const half Gx[9] = { -1,-2,-1,0, 0, 0,1, 2, 1 };const half Gy[9] = { -1, 0, 1,-2, 0, 2,-1, 0, 1 };half texColor;half edgeX = 0;half edgeY = 0;for (int it = 0; it < 9; it++){texColor = luminance(tex2D(_MainTex, i.uv[it]));edgeX += texColor * Gx[it];edgeY += texColor * Gy[it];}half edge = lerp(0, abs(edgeX) + abs(edgeY), _EdgeWidth);return edge; }
效果如下
优点
- 适用于任何模型
- 不用增加一个Pass的渲染性能消耗
缺点
- 增加了后处理的性能消耗
- 无法单独对一个物体进行描边
- 一些变化很小的变换难以检测
全部代码如下
Shader "Custom/Postprocess/EdgeDetection" {Properties{_MainTex ("Texture", 2D) = "white" {}_EdgeColor ("EdgeColor", Color) = (1,0,0,1)_BackgroundColor ("BackgroundColor", Color) = (1,1,1,1)_EdgeOnly ("Edge Only", Range(0,1)) = 0.5_EdgeWidth ("Edge Width", Range(0,1)) = 1}SubShader{Pass{ZTest Always Cull Off ZWrite OffCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct v2f{ float4 vertex : SV_POSITION;half2 uv[9] : TEXCOORD0;};sampler2D _MainTex;float2 _MainTex_TexelSize;fixed4 _EdgeColor;fixed4 _BackgroundColor;fixed _EdgeOnly;fixed _EdgeWidth;v2f vert (appdata_img v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);half2 uv = v.texcoord;o.uv[0] = uv + half2(_MainTex_TexelSize.x *-1, _MainTex_TexelSize.y *-1);o.uv[1] = uv + half2(_MainTex_TexelSize.x * 0, _MainTex_TexelSize.y *-1);o.uv[2] = uv + half2(_MainTex_TexelSize.x * 1, _MainTex_TexelSize.y *-1);o.uv[3] = uv + half2(_MainTex_TexelSize.x *-1, _MainTex_TexelSize.y * 0);o.uv[4] = uv + half2(_MainTex_TexelSize.x * 0, _MainTex_TexelSize.y * 0);o.uv[5] = uv + half2(_MainTex_TexelSize.x * 1, _MainTex_TexelSize.y * 0);o.uv[6] = uv + half2(_MainTex_TexelSize.x *-1, _MainTex_TexelSize.y * 1);o.uv[7] = uv + half2(_MainTex_TexelSize.x * 0, _MainTex_TexelSize.y * 1);o.uv[8] = uv + half2(_MainTex_TexelSize.x * 1, _MainTex_TexelSize.y * 1);return o;}fixed luminance(fixed4 color){return 0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b;}half Sobel(v2f i){const half Gx[9] = { -1,-2,-1,0, 0, 0,1, 2, 1 };const half Gy[9] = { -1, 0, 1,-2, 0, 2,-1, 0, 1 };half texColor;half edgeX = 0;half edgeY = 0;for (int it = 0; it < 9; it++){texColor = luminance(tex2D(_MainTex, i.uv[it]));edgeX += texColor * Gx[it];edgeY += texColor * Gy[it];}half edge = lerp(0, abs(edgeX) + abs(edgeY), _EdgeWidth);return edge;}fixed4 frag (v2f i) : SV_Target{half edge = Sobel(i);fixed4 withEdgeColor = lerp(tex2D(_MainTex, i.uv[4]), _EdgeColor, edge);fixed4 onlyEdgeColor = lerp(_BackgroundColor, _EdgeColor, edge);half4 col = lerp(withEdgeColor, onlyEdgeColor, _EdgeOnly);return col;}ENDCG}} }
外发光
Bloom
- 后处理非常常用的Bloom效果,也是最为常用的外发光渲染方案。
- 基本思路为,提取图像中的亮部,渲染为RT,再对这张RT进行模糊,最后再把模糊后的亮部RT叠加回原图,就得到了发光物体外一层光晕的效果。
- HDR可以让颜色亮度高于1,与Bloom配合可以让暗部不会也随着Bloom效果一起发光,仅仅让亮部有Bloom效果
- 实现代码有很多,之前的一篇写后处理的博客也有写过,就不贴代码了
- 效果图
沿法线外扩背面方法实现
在上文的沿法线外扩背面实现的描边方法基础上,给背面的alpha像内发光一样,乘以视角与法线点积。不过要稍作改动,让背面从中心向外逐渐透明,模拟光晕的效果。
float ndotv = max(pow(-dot(normal, viewDir),_OutlineFeather), 0.001); fixed4 col = _OutlineColor; col.a *= ndotv;
注意:
- 因为是背面的法线,所以点积的范围在[-1,0],要乘以-1让它在0到1之间。
- 内发光是从中心到边缘逐渐变大,而此处是需要让alpha从中心到边缘逐渐变小,所以之前是1-ndotv,现在直接使用ndotv计算
全部代码如下
Shader "Custom/Effect/Shine_In"
{Properties{_Color ("Main Color", Color) = (1,1,1,1)[HDR]_ShineColor ("Shine Color", Color) = (1,1,1,1)_ShineWidth ("Shine Width", Range(0, 2)) = 1_ShineFeather ("Shine Feather", Range(0, 10)) = 0}SubShader{Tags { "Queue"="Transparent" }LOD 200Pass{Cull FrontBlend SrcAlpha OneMinusSrcAlphaCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float3 normal : NORMAL;};struct v2f{float4 pos : SV_POSITION;float3 worldPos : TEXCOORD0;float3 normal : TEXCOORD1;};half4 _ShineColor;float _ShineWidth;float _ShineFeather;v2f vert (appdata v){v2f o;float4 viewPos = float4(UnityObjectToViewPos(v.vertex), 1.0);float3 viewNormal = mul(UNITY_MATRIX_IT_MV, v.normal);viewNormal.z = -0.5;float3 normal = normalize(viewNormal);viewPos += float4(normal, 1.0) * _ShineWidth;o.pos = mul(UNITY_MATRIX_P, viewPos);o.worldPos = mul(unity_ObjectToWorld, v.vertex);o.normal = UnityObjectToWorldNormal(v.normal);return o;}fixed4 frag (v2f i) : SV_Target{float3 normal = normalize(i.normal);float3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));float ndotv = max(pow(-dot(normal, viewDir),_ShineFeather), 0.001);fixed4 col = _ShineColor;col.a *= ndotv;return col;}ENDCG}Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;};struct v2f{float4 pos : SV_POSITION;};half4 _Color;v2f vert (appdata v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);return o;}fixed4 frag (v2f i) : SV_Target{fixed4 col = _Color;return col;}ENDCG}}
}
【UnityShader】常用效果内外发光、 描边相关推荐
- PS将PNG图片内外发光和变色
调整图片的图片内外发光 调整图片的色相和饱和度
- HTML5系列代码:为文字设置描边效果文字发光
padding属性bai:设置du所有内边距属性. 行内非替换元素上设置的内边距不会影响行高计算:因zhi此,如果一dao个元素既有内边距又有背景,从视觉上看可能会延伸到其他行,有可能还会与其他内容重 ...
- css设置边框内外发光效果
box-shadow: 0 0 10px rgb(0,153,184) inset,0 0 30px rgb(0,153,184);
- js常用效果代码封装
我们在工作中经常需要写很多效果或方法,但是有些效果和方法我们都已经写过无数次了,因此我们会把这些代码给封闭起来,以便以后使用,以下就是我个人在工作中整理出的一些常用代码,以后会不定期更新. 1.时间格 ...
- css3-文字外发光效果2-文字描边
使用文字阴影效果除了可以制作外发光之外,在某些时候还可以替代图片的文字描边效果. 上过程: 1.文字阴影 text-shadow: #000 1px 0 0, #000 0 1px 0, #000 - ...
- 阿诺德给物体加描边_视频人物描边效果 怎么给视频人物进行描边?视频内物体描边效果制作|视频描边特效...
今日霜降,霜降是秋季的最后一个节气,是反映气温变化的节气,是秋季到冬季的过渡,意味着即将进入冬天.虽然说可能在广东,这意味着即将进入秋天,哈哈,这天气还不能够称之为冬天呢~毕竟走在大家大街上短袖短裤还 ...
- UnityShader——流光效果
声明:本文为个人笔记,用于学习研究使用非商用,内容为个人研究及综合整理所得,若有违规,请联系,违规必改. UnityShader学习目录 文章目录 UnityShader学习目录 前言 一.实现原理 ...
- android canvas光晕绘制_Unity实现UI光晕效果(发光效果)
unity中,我们怎么制作ui物体发光的渐隐渐现的效果呢? 比如说我们有一张月亮光晕的精灵图片 我们可以给它添加一个canvasgroup组件 我们可以发现,组件上的alpha值可以控制图片的透明度, ...
- Unity中制作UI光晕效果(发光效果)
Unity中,我们怎么制作UI物体发光的渐隐渐现的效果呢? 比如说我们有一张月亮光晕的精灵图片 我们可以给它添加一个CanvasGroup组件 我们可以发现,组件上的Alpha值可以控制图片的透明度, ...
最新文章
- Spring Cache抽象-使用Java类注解的方式整合EhCache
- 阿里云E-MapReduce 创建执行计划
- 2015-03-18 header note creation in my Appointment
- Docker03 Docker基础知识、Docker实战
- CCNA-第十二篇-STP+ACL(下)
- 云原生引领全云开发时代
- Linux基本管理七大流程
- 理解vue中$watch使用
- MIT机器学习免费课程,13周从理论到实践,大牛教授Python授课
- 苹果画画软件_数位板可以连手机画画?有哪些好用的绘画APP?
- Ali-tomcat之HSF框架Demo启动报错HSFServiceAddressNotFoundException
- java商品管理系统黑马_JavaEE基础班 水果超市管理系统
- 抖音育儿类账号的创作灵感分享, 想进圈的不妨了解一下
- 测试大会能给我们带来什么?
- 用户情感可视化分析——天池竞赛
- Citavi导入中国知网caj文件
- 2022-2028全球及中国电动汽车充电站和充电桩行业研究及十四五规划分析报告
- 建议收藏!让造价员疯传的100条知识,没有师傅也入门
- 第三方分享QQ QQZONE
- [go学习笔记.第二章] 2.go语言的开发工具以及安装和配置SDK