Unity Shader - 类似七龙珠的人物气焰效果

文章目录

  • 环境
  • 效果
  • 思路
    • 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,那么绘制状态又变化,基本上就不能合批了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/46854.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

虚幻引擎中GPU Lightmass全局光照的使用步骤

GPU Lightmass (GPULM) 是一种光烘焙方法,它预先计算来自具有 Stationary 或 Static 移动性的灯光的复杂光交互,并将该数据存储在创建的应用于场景几何体的光照贴图纹理中。GPU Lightmass 显着减少了为复杂场景计算、构建和生成光照数据所需的时间&#…

UE4 Shader 常用函数 学习笔记

Add:快捷键a 将两数相加,也可以将两纹理相加,如下图: append(追加): Subtract(减): 和加相反,特例,如下: Abs&#xff0…

Ubuntu搭建原神3.7版本服务器

本文涉及知识点 linux基础命令|mongoDB服务端搭建及指令|Java环境搭建 原神私服可以用来抽卡满足自己用,他的许多任务都得登录控制台自己发,不建议拿私服玩,想要体验完美原神请到官服,本文只做LINUX MongoDB java教学例子 写在前…

原神角色渲染详解

整体效果展示:主要方案是对下面几张图做不同的处理 身体 基础颜色光照:主要贴图卡通贴图ramp图法线图光照图 金属度与高光,头发部分高光:光照图,头发部分用高光black图 深度边缘光:用额外pass DepthNor…

打脸了兄弟们,Go1.20 arena 来了!

大家好,我是煎鱼。 大概半年前,我写过一篇文章《Go 要违背初心吗?新提案:手动管理内存》。有兴趣了深入解的同学,可以再回顾一下。 当时我们还想着 Go 团队应该不会接纳,至少不会那么快: 没想到…

对正在打野发育的红队同学的一次反制

文章目录 故事开始其他反制思路隐蔽C2CS重定向器实验 故事开始 真的是对同学的反制哈,我们最近都在学习内网&钓鱼就互相”攻击“,就有那么一天我就在想我偷懒把CS登录密码设置的很简单,会不会其它人也偷懒,于是就抱着尝试的心…

zsteg安装及CTF打野wp

下载文件解压后拖进kali里,移到root目录下 使用命令 zsteg 文件名 执行成功后即可获取qwxf{you_say_chick_beautiful?} 下面介绍zsteg的安装 使用命令 git clone http://www.github.com/zed-0xff/zsteg apt-get install gem gem install zsteg 若报错,…

AI版女网红“半藏森林”上线,服务项目让人意想不到

目前首批网红明星“AI克隆人”已提前上线,主营业务就是打造各种名人版AI聊天机器人,用户付费便可与之聊天。其后台报名参加AI克隆人的网红明星“全网粉丝总数已超过5亿”。该公司这波上线的网红明星AI克隆人,包括此前因“疑似插足他人恋情”&…

炉石传说 爬取全部卡牌

之前我30行爬了英雄联盟全部皮肤 这次爬炉石稍稍麻烦点,50行 网页分析 首先,我们分析炉石官方网站卡牌工具https://hs.blizzard.cn/cards/ 通过源代码和Network分析,发现返回的卡牌是用post请求的json文件 请求数据为: cardCla…

我用python玩炉石传说(3)-----炉石卡牌套牌自动构建算法

本文共三个部分: 我用python玩炉石传说(1)-----炉石卡牌相关度分析的手动打分器我用python玩炉石传说(2)-----炉石卡牌套牌爬取器及自动分析卡牌相关度我用python玩炉石传说(3)-----炉石卡牌套…

(写着玩)Python仿网络游戏《炉石传说》,继上次部分代码展示

上一期: (写着玩)Python仿网络游戏《炉石传说》,使用LOL的卡牌进行模拟对局(版本1.0)_AMarvelZ的博客-CSDN博客

手动爬取炉石传说所有卡牌

笔者还记得是从大学开始的时候玩的炉石传说,还记得当时的版本只有黑石山,纳克萨玛斯,地精大战侏儒这些卡包,转眼间到了现在,炉石传说早已和之前的那个炉石传说不再一样了,还记得以前的卡牌套路冰法&#xf…

革命炉石传说,Gods Unchained

欢迎各位新老朋友,我们细说P2E(Play to Earn),为广大用户提供一个深入了解链游的平台。不定期的跟大家分享一些精品项目和最新链游方向,也欢迎大家关注我们。 我们第七期分享的是Gods Unchained,Gods Uncha…

卷没用的,我要被 AI 大模型抢走饭碗了

SOTA AI Devs Park 是专注于生成式 AI 领域的公益开发者社区,由生成式 AI 产业加速营 SOTA AI 支持建设,正在策划举办系列 Meetup 与闭门会,聚焦于同频交流生成式 AI 的前沿趋势与一线技术实践。 前不久我们出了一篇文章《AGI 变革&#xff1…

关于《更新win11之后我的edge浏览器打不开》这件事

win11更新后打不开edge浏览器的解决方法 1.打开此电脑,打开路径C:\Program Files (x86)\Microsoft\Edge\Application下的106.0.1370.42文件夹(版本可能不同,总之找到全是数字的文件夹) 2.找到msedgewebview2.exe文件,…

Edge地址栏搜索引擎换成Bing

‘设置’找到‘隐私,搜索和服务’ 地址栏和搜索 管理搜索引擎 添加保存设为默认值即可 以%s代替查询的URL: {bing:baseURL}search?q%s&{bing:cvid}

【PTA】 统计成绩2 (10分)

给定N个学生的基本信息,包括学号(由12个数字组成的字符串)、姓名(长度小于20的不包含空白字符的非空字符串)、高等数学成绩、C语言程序设计成绩,成绩均为[0,100]区间内的整数,要求统计高等数学成…

好分数网查成绩分数查排名服务平台_好分数免费查年级排名

好分数在线查年段排名,好分数在线查班级排名,好分数免费查询同学成绩 分数API是一个公益性的在线免费查排名软件 由作者一木独立开发 如果您方便的话可以下载软件后投币来支持我们 以维持服务器的正常支出,感谢各位 所需软件下载地址☞点我下载 教程开始 1.打开…

好分数学生家长查排名成绩-在线好分数查成绩入口

好分数在线查年段排名,好分数在线查班级排名,好分数免费查询同学成绩 分数API是一个公益性的在线免费查排名软件 由作者一木独立开发 如果您方便的话可以投币来支持我们 以维持服务器的正常支出,感谢各位 所需软件链接在文末 教程开始咯 1.打开 分数API APP&…