文章目录

  • 环境
  • 效果
  • 思路
    • passes
    • pass - 气焰
    • 优化后的 shader
  • Project
  • 关于效果落地

环境

Unity : 2018.3.11f1
Pipeline : BRP


效果

覆盖身前

覆盖身前 + 叠加混合

风格化 版本,更适合 NPR:

再优化一版本


该效果是自己摸索的(也是自己再国外社区逛的少,等后续将 小说 看完了,再开始刷效果。。。)

制作思路不复杂,做过 shell 描边(法线方向外扩)的同学都已经知道怎么回事

麻烦的是调整参数,需要花些时间来调整到心意的效果


思路


passes

  • pass 0 - toon shading
  • pass 1 - toon outline
  • pass 2 - power up 气焰效果(英文暂时不知道叫啥,这里暂时叫:power up)


pass - 气焰

思路:

FORWARD pass 先写入模板值:90

然后气焰不绘制 stencil 90 的部分

气焰 shader
思路写在了 shader 代码的注释中

//=================== power up START ===============================
struct appdata_powerup {float4 vertex : POSITION;half3 normal : NORMAL;
};
struct v2f_powerup {float4 pos : SV_POSITION;float3 normalWS : TEXCOORD0;float4 newPosWS : TEXCOORD2;float4 srcXY_NewXY : TEXCOORD4;
};
uniform half _PowerUpOutline;
uniform half _PowerUpRimPower;
uniform half4 _PowerUpOutlineColor1;
uniform half4 _PowerUpOutlineColor2;
uniform half _PowerUpNoiseScale_Alpha;
uniform half _PowerUpNoiseScale;
uniform half3 _PowerUpNoiseOffset;
uniform half3 _PowerUpSpeed_Alpha;
uniform half3 _PowerUpSpeed;
uniform half _PowerUpFadeOutDistance;
// uniform float2 _PowerUpGradientOffsetVertexEdge;
uniform half _PowerUpPingpongEdgeSizeFrequence;
uniform half _PowerUpPingpongEdgeSizeStrength;
uniform half _PowerUpVertexNoiseFrequence;
uniform half _PowerUpVertexNoiseStrength;v2f_powerup vert_powerup(appdata_powerup v) {v2f_powerup o;// jave.lin : 计算 shell 扩展前的 世界坐标、裁剪坐标,为的是:计算原始的屏幕坐标// 原始世界坐标float4 srcPosWS = mul(unity_ObjectToWorld, v.vertex);// 原始裁剪坐标float4 srcPosCS = UnityWorldToClipPos(srcPosWS.xyz);// float edgeA = _PowerUpGradientOffsetVertexEdge.x;// float edgeB = _PowerUpGradientOffsetVertexEdge.y;// float outlineW = smoothstep(edgeA, edgeB, v.vertex.y);// jave.lin : pingpong 边缘大小【可选】,_PowerUpPingpongEdgeSizeFrequence 外头我暂时设置为0,也可以制作变体控制float pingpongOutlineW = sin(_Time.y * _PowerUpPingpongEdgeSizeFrequence);Unity_Remap_float4(pingpongOutlineW, half2(-1, 1), half2(0.5, 1), pingpongOutlineW);pingpongOutlineW *= _PowerUpPingpongEdgeSizeStrength;// jave.lin : shell 扩展v.vertex.xyz += v.normal.xyz * (pingpongOutlineW * _PowerUpOutline);// jave.lin : 顶点动画:这个是旧项目的模型,本来是作用在:v.vertex.y 的,但是模型制作不标准v.vertex.x += snoise((/*outlineW * */v.vertex.xyz + _Time.y) * _PowerUpVertexNoiseFrequence)* _PowerUpVertexNoiseStrength;// jave.lin : 计算新的 世界坐标、裁剪坐标,为的是:计算 shell 扩展后的屏幕坐标o.newPosWS = mul(unity_ObjectToWorld, v.vertex);o.pos = UnityWorldToClipPos(o.newPosWS.xyz);// jave.lin : 计算顶点法线 matrix_mITo.normalWS = UnityObjectToWorldNormal(v.normal);// jave.lin : 根据 裁剪坐标,计算 shell 扩展前的原始SP,和 计算 shell 扩展后的 SP,(SP:screen position)float4 srcSP = ComputeScreenPos(srcPosCS);o.srcXY_NewXY.xy = srcSP.xy / srcSP.w;float4 newSP = ComputeScreenPos(o.pos);o.srcXY_NewXY.zw = newSP.xy / newSP.w;return o;
}
half4 frag_powerup(v2f_powerup i) : SV_Target{// jave.lin : 法线,如果为了节省性能,可以不做: normalize 计算,该 API 计算有点费half3 N = normalize(i.normalWS);// return half4(N, 1);// jave.lin : 计算 ViewDirhalf3 V = normalize(_WorldSpaceCameraPos.xyz - i.newPosWS.xyz);// return half4(V, 1);// jave.lin : 计算 rim / fresnel 的反向值half rimInv = dot(N, V);rimInv = pow(rimInv, _PowerUpRimPower);// return half4(rimInv.xxx, 1);// return half4(i.srcXY_NewXY.xy, 0, 1);// return half4(i.srcXY_NewXY.zw, 0, 1);// jave.lin : 计算 shell 扩展前的 原始 SP 和 扩展后的 SP 之间的距离float dis = distance(i.srcXY_NewXY.xy, i.srcXY_NewXY.zw);// jave.lin : 根据距离值做 fade outfloat fade = smoothstep(_PowerUpFadeOutDistance, 0, dis);// return half4(fade.xxx, 1);// jave.lin : 计算 alpha 的噪点抖动 的 3d noise 采用坐标的偏移值float3 moveVec_alpha = _Time.y * _PowerUpSpeed_Alpha;// float3 moveVec = _Time.y * _PowerUpSpeed;// jave.lin : 使用 3d noise 来采样,有点费,可以改用 2d noise,但只能使用 世界坐标xz,// 或是 屏幕坐标 来作为采样坐标,会导致多个角色的气焰叠加一起时,抖动完全一致的问题,但可以使用 noise offset 来处理,这里偷懒就不这么挣了// jave.lin : snoise 参考:Unity Shader - 搬砖日志 - 3D Noise, Noise 3D 相关// https://blog.csdn.net/linjf520/article/details/122342408float noise_alpha = snoise((i.newPosWS + moveVec_alpha) * _PowerUpNoiseScale_Alpha);// float noise_alpha1 = snoise((i.newPosWS + moveVec_alpha * noise_alpha) * _PowerUpNoiseScale_Alpha);// noise_alpha = max(noise_alpha, noise_alpha1);// jave.lin : 将 距离的淡出值应用上noise_alpha *= fade;// jave.lin : noise alpha 再次和 rim 反向之去一个最大值,为的是:尽量让靠经原始坐标的像素的 alpha 值大一些noise_alpha = max(noise_alpha, rimInv * fade);// float noise = snoise((i.newPosWS + moveVec + _PowerUpNoiseOffset) * _PowerUpNoiseScale);// return noise_alpha;// Unity_Remap_float4(noise, half2(0, 1), half2(0.5, 1), noise);// jave.lin : 使用 rim 反向值,来 混合两个颜色,color1 是气焰内色, color2 是气焰外色half3 tintCol = lerp(_PowerUpOutlineColor1.rgb, _PowerUpOutlineColor2.rgb, rimInv);half4 finalCol = half4(/*noise * */tintCol, noise_alpha);// jave.lin : TODO - 使用 rampTex 来映射气焰内外的着色// 后续添加上    // jave.lin : 夹一下,避免溢出finalCol = saturate(finalCol);return finalCol;
}
//=================== power up END ===============================

优化后的 shader

减少了比较多的计算量

//=================== power up START ===============================
struct appdata_powerup {float4 vertex : POSITION;half3 normal : NORMAL;
};
struct v2f_powerup {float4 pos : SV_POSITION;float3 normalWS : TEXCOORD0;float4 newPosWS : TEXCOORD2;float4 srcXY_NewXY : TEXCOORD4;float fadeNoise : TEXCOORD5; // jave.lin : 调试用
};
// jave.lin : 下面为了快速效果,暂时没有 pack 一下各个 uniform 的分量,后续可以优化
uniform half _PowerUpOutline;
uniform half _PowerUpRimPower;
sampler2D _PowerUpOutlineColorRampTex;
// uniform half _PowerUpNoiseScale_Alpha;
uniform half _PowerUpNoiseScale;
uniform half3 _PowerUpNoiseOffset;
uniform half3 _PowerUpSpeed_Alpha;
uniform half3 _PowerUpSpeed;
uniform half _PowerUpFadeOutDistance;
uniform float2 _PowerUpGradientOffsetVertexEdge;
uniform half _PowerUpPingpongEdgeSizeFrequence;
uniform half _PowerUpPingpongEdgeSizeStrength;
uniform half _PowerUpVertexNoiseFrequence;
uniform half _PowerUpVertexNoiseStrength;
uniform half2 _PowerUpVertexNoiseVerticalStrengthThreshold;v2f_powerup vert_powerup(appdata_powerup v) {v2f_powerup o;// jave.lin : 计算 shell 扩展前的 世界坐标、裁剪坐标,为的是:计算原始的屏幕坐标// 原始世界坐标float4 srcPosWS = mul(unity_ObjectToWorld, v.vertex);// 原始裁剪坐标float4 srcPosCS = UnityWorldToClipPos(srcPosWS.xyz);// jave.lin : 纵向的 气焰强度控制half outlineW_EdgeA = _PowerUpVertexNoiseVerticalStrengthThreshold.x;half outlineW_EdgeB = _PowerUpVertexNoiseVerticalStrengthThreshold.y;half outlineW;Unity_Remap_float4(v.vertex.x, half2(outlineW_EdgeA, outlineW_EdgeB), half2(0, 1), outlineW);o.fadeNoise = outlineW;// jave.lin : pingpong 边缘大小【可选】,_PowerUpPingpongEdgeSizeFrequence 外头我暂时设置为0,也可以制作变体控制// float pingpongOutlineW = sin(_Time.y * _PowerUpPingpongEdgeSizeFrequence);// Unity_Remap_float4(pingpongOutlineW, half2(-1, 1), half2(0.5, 1), pingpongOutlineW);// pingpongOutlineW *= _PowerUpPingpongEdgeSizeStrength;// jave.lin : shell 扩展(挤出)强度half shellExtrudeIntensity =// pingpongOutlineW *_PowerUpOutline;// jave.lin : shell 扩展v.vertex.xyz += v.normal.xyz * shellExtrudeIntensity;// jave.lin : 顶点动画:这个是旧项目的模型,本来是作用在:v.vertex.y 的,但是模型制作不标准// jave.lin : snoise 参考:Unity Shader - 搬砖日志 - 3D Noise, Noise 3D 相关// https://blog.csdn.net/linjf520/article/details/122342408v.vertex.x += snoise((v.vertex.xyz + _Time.y) * _PowerUpVertexNoiseFrequence)* _PowerUpVertexNoiseStrength * outlineW;// jave.lin : 计算新的 世界坐标、裁剪坐标,为的是:计算 shell 扩展后的屏幕坐标o.newPosWS = mul(unity_ObjectToWorld, v.vertex);o.pos = UnityWorldToClipPos(o.newPosWS.xyz);// jave.lin : 计算顶点法线 matrix_mITo.normalWS = UnityObjectToWorldNormal(v.normal);// jave.lin : 根据 裁剪坐标,计算 shell 扩展前的原始SP,和 计算 shell 扩展后的 SP,(SP:screen position)float4 srcSP = ComputeScreenPos(srcPosCS);o.srcXY_NewXY.xy = srcSP.xy / srcSP.w;float4 newSP = ComputeScreenPos(o.pos);o.srcXY_NewXY.zw = newSP.xy / newSP.w;return o;
}
half4 frag_powerup(v2f_powerup i) : SV_Target{// return half4(i.fadeNoise.xxx, 1);// jave.lin : 法线,如果为了节省性能,可以不做: normalize 计算,该 API 计算有点费// half3 N = normalize(i.normalWS);// half3 N = i.normalWS;// return half4(N, 1);// jave.lin : 计算 ViewDirhalf3 V = normalize(_WorldSpaceCameraPos.xyz - i.newPosWS.xyz);// return half4(V, 1);// jave.lin : 计算 rim / fresnel 的反向值// half rimInv = dot(N, V);half rimInv = dot(i.normalWS, V);rimInv = pow(rimInv, _PowerUpRimPower);// return half4(rimInv.xxx, 1);// return half4(i.srcXY_NewXY.xy, 0, 1);// return half4(i.srcXY_NewXY.zw, 0, 1);// jave.lin : 计算 shell 扩展前的 原始 SP 和 扩展后的 SP 之间的距离// 可以优化,不适用 distance,改用 dot,_PowerUpFadeOutDistance 阈值重新调整一下即可// float dis = distance(i.srcXY_NewXY.xy, i.srcXY_NewXY.zw);float dis = dot(i.srcXY_NewXY.xy, i.srcXY_NewXY.zw);// jave.lin : 根据距离值做 fade outfloat fade = smoothstep(_PowerUpFadeOutDistance, 0, dis);// return half4(fade.xxx, 1);// jave.lin : 计算 alpha 的噪点抖动 的 3d noise 采用坐标的偏移值// float3 moveVec_alpha = _Time.y * _PowerUpSpeed_Alpha;// float3 moveVec = _Time.y * _PowerUpSpeed;// // jave.lin : 使用 3d noise 来采样,有点费,可以改用 2d noise,但只能使用 世界坐标xz,// // 或是 屏幕坐标 来作为采样坐标,会导致多个角色的气焰叠加一起时,抖动完全一致的问题,但可以使用 noise offset 来处理,这里偷懒就不这么挣了// float noise_alpha = snoise((i.newPosWS + moveVec_alpha) * _PowerUpNoiseScale_Alpha);// // float noise_alpha1 = snoise((i.newPosWS + moveVec_alpha * noise_alpha) * _PowerUpNoiseScale_Alpha);// // noise_alpha = max(noise_alpha, noise_alpha1);// // jave.lin : 将 距离的淡出值应用上// noise_alpha *= fade;// // jave.lin : noise alpha 再次和 rim 反向之去一个最大值,为的是:尽量让靠经原始坐标的像素的 alpha 值大一些// noise_alpha = max(noise_alpha, rimInv * fade);float noise_alpha = rimInv * fade;// float noise = snoise((i.newPosWS + moveVec + _PowerUpNoiseOffset) * _PowerUpNoiseScale);// return noise_alpha;// Unity_Remap_float4(noise, half2(0, 1), half2(0.5, 1), noise);// jave.lin : 使用 rim 反向值,来 混合两个颜色,color1 是气焰内色, color2 是气焰外色// half3 tintCol = lerp(_PowerUpOutlineColor1.rgb, _PowerUpOutlineColor2.rgb, rimInv);// jave.lin : 使用 rampTex 来映射气焰内外的着色half4 tintCol = tex2D(_PowerUpOutlineColorRampTex, half2(rimInv, 0.5));half4 finalCol = half4(/*noise * */tintCol.rgb, noise_alpha * tintCol.a);// jave.lin : 夹一下,避免溢出finalCol = saturate(finalCol);return finalCol;
}
//=================== power up END ===============================

Project

backup : 备份用,个人学习,不公开

  • TestCharacterPowerUpEffectScene.unitypackage
  • TestSG_CharacterUpPowerEffectV1_2021_3_1F1 - BRP
  • TestCharacterPowerUpEffectSceneV2.unitypackage
  • TestCharacterPowerUpEffectSceneV3.unitypackage
  • TestCharacterPowerUpEffectSceneV3_BRP_2021_3_1f1.unitypackage
  • CharacterPowerUpEffect_CommandBuffer_hook_to_render_BRP_Unity_2018.3.11f1.unitypackage

关于效果落地

另外,这上面的代码都是 shader 层的实现
真正要落地,如果直接使用 3 个 pass 的来实现的化,会涉及到 半透明渲染顺序问题
这个在 BRP 项目,我们可以使用 CommandBuffer 来 hook 到对应的 after opaque rendering 之后渲染即可
URP 项目就更方便一些,直接扩展一个 RenderFeature 来专门渲染气焰效果即可,而且还可以 SetPassCall 合批,性能比 BRP 的高多

下面是在 BRP 中,落地方案实现(CommandBuffer)

比如,下面的绘制顺序的问题:

写个排序即可解决

但此种方法有个缺点,每个 character 模型 和 power up 气焰 绘制都需要独立的 stencil,那么绘制状态又变化,基本上就不能合批了

Unity Shader - 类似七龙珠的人物气焰效果相关推荐

  1. 着色器编程_unity中的基础纹理,使用Unity Shader实现基础纹理的渲染效果

    学习通过使用Unity Shader实现基础纹理的渲染效果 目录 学习通过使用Unity Shader实现基础纹理的渲染效果 问1:详细描述一下漫反射纹理.高度纹理.法线纹理.渐变纹理和遮罩纹理? 问 ...

  2. Unity Shader 实现简单的压扁效果

    有点累啊,一个CoverMap搞了一周多,还是太嫩了,还有好多东西等着我去学呢,今天就写个简单的东西吧--一个把模型压扁的效果,参考博客Unity Shader - 一些玩具Shader.话不多说,先 ...

  3. Unity Shader学习案例一: 流光效果

    Unity Shader Lab新手宝典简单Shader案例一:流光效果 + 相关基础知识说明 Shader "Samples/Light Flow"//shader名称 {Pro ...

  4. Unity Shader学习笔记/Urp/水墨风效果

    实现简易的水墨风效果大致分为三部分: 1.将原始的rgb贴图转化为灰度图 float4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN ...

  5. Unity Shader实现PPT 30多种切换效果

    目录 原文 效果 Unity代码 00基础工具 Tools.cginc 01Fade 淡入淡出 02Push 推入 03Fade 擦除 04Split 分割 Unity工程 原文 原文 是用 GLSL ...

  6. Unity Shader深度相关知识总结与效果实现

    鸣谢:puppet_master (VIA CSDN)贡献此文 前言 前言废话依旧比较多,感觉我是个写游戏体验评测的,233.最近想起了<恶灵附身>这款游戏的几个效果: <恶灵附身& ...

  7. unity shader 入门 全透明与半透明效果实现

    片元函数的fixed4类型的返回值的第4位即为阿尔法值,0代表完全不显示(透明),1代表完全显示.中间的数值代表半透明.但只修改这个值是不能直接修改透明度的,因为还要对队列等进行修改. 本文介绍透明度 ...

  8. 【Unity Shader】渲染纹理实现镜子效果

    1 基本概念 1.1 什么是渲染到纹理? 全称是Render To Texture,<入门精要>好像又把渲染目标纹理,即Render Target Texture也叫做RTT,但我认为&l ...

  9. [Unity]Shader利用Geometry处理实现描边效果

    1.原理 利用几何着色器,生成新的顶点,用新生成的顶点,构建描边,在用模板测试剔除原模型,如下图 灰色三角形是模型原来的一个三角面,用几何着色器,延顶点法线向外,创建出新的顶点A,B,C.已知一个面的 ...

最新文章

  1. Centos7系统下httpd各种方式实现与配置
  2. https安全传输揭秘
  3. 13.2.8 用户认证
  4. 计算机第二道启动密码怎么设置,电脑一道密码怎么设置
  5. scala面试题简要总结
  6. setuna截图怎么放大缩小_实用的高清截图系列小窍门详解,一章带你“真正学会”截图!...
  7. 玩转 SSH(六):SpringMVC + MyBatis 架构搭建(注解版)
  8. java登录验证码_java实现登录验证码
  9. 在VT上搜索恶意软件
  10. flyaway mysql_keycloak搭配mysql
  11. 关于react、vue的相关问题
  12. 书籍推荐:国内第一本ASP.NET 3.5 MVC技术专著
  13. 字符26进制 与 10进制【可以这样来理解】
  14. Android 输入法表情上传服务器
  15. Hadoop,master和slave简单的分布式搭建
  16. KubeSphere又开始对接公有云了,这一次是阿里云 SLB
  17. PIL Image创建空白图片的bug
  18. 【杂篇 · 技巧】WebStorm页面窗口与显示bug
  19. 传统企业如何建设B2C平台做网络营销?
  20. 使用scrapy再次爬取猫眼前100榜单电影!

热门文章

  1. 读书1--《白鹿原》
  2. 软件开发人员如何与测试人员相处
  3. 程序员求职面试心经40条—谨记原则
  4. python---文件的操作
  5. sas univariate 结果解释_【SAS NOTES】proc univariate检查单变量分布
  6. 联想笔记本win10突然没有显示网络?找不到Wlan,适配器驱动程序的问题
  7. 月活4亿市值仅次于腾讯的美图到底是怎么回事?
  8. 大学三年狂拿国内外十几个3D挑战赛大奖?!国内CG新星崛起
  9. 千寻和省cors精度对比_测量员实操攻略:解析省CORS和千寻CORS账号区别及其如何选择运用...
  10. 时间序列预测(四)—— LSTM模型