初探雾效果!shader 源码分析与讲解! Cocos Creator 3D Shader Fog !
随便讲讲雾的原理以及旧版本的使用雾的方法。
效果
原理
雾效(fog
)是游戏中常用的一种效果,根据远近产生不同的深度的雾效果。
这个效果涉及两个关键字。
- 距离
- 颜色
在着色器中,雾效的距离
,一般转换成计算雾效因素(factor_fog
),这个数字范围是0-1
。
-
1
表示完全不受雾影响 -
0
表示完全被雾笼罩
接着再根据这个雾效因素去计算颜色
,混合当前颜色color
和雾factor_fog
的颜色即可。
mix(cc_fogColor.rgb, color.rgb, factor_fog)
接下来就是如何去计算这个雾效因素了。
雾效因素一般是通过由摄像机位置(cc_cameraPos
)和当前点位置(世界坐标,由世界空间矩阵和模型坐标计算的wPos
)的距离去计算的。
float cam_dis = distance(cc_cameraPos, wPos);
不同的雾效果,采用不同的方式去计算雾效因素。
线性雾
线性一般会有两个参数去计算。雾开始的地方fogStart
和雾最远的地方fogEnd
。
- 当距离小于雾开始的地方,表示没有雾,即雾效因素为
1
- 当距离大于雾最远的地方,完全被雾给覆盖了,即雾效因素为
0
factor_fog = clamp((fogEnd - cam_dis) / (fogEnd - fogStart), 0., 1.);
当然也可以改造一下,在0-1
之间平滑一些。
factor_fog = 1.0 - smoothstep(fogStart, fogEnd, fogDistance);
指数雾
指数雾就是用指数函数根据距离计算雾效因素。
一般会有一个雾浓度fogDensity
去控制这个指数函数。
一般指数雾:
factor_fog = exp(-cam_dis * fogDensity);
平方指数雾:
factor_fog = exp(-cam_dis * cam_dis * fogDensity * fogDensity);
层级雾
层级雾计算过程相对复杂,大致过程就是计算水平面的距离和高度距离以及浓度参数,具体实现过程可以参考源代码 cc-fog.chunk
。
fDensity = (sqrt(1.0 + ((fDeltaD / fDeltaY) * (fDeltaD / fDeltaY)))) * fDensityIntegral;
factor_fog = exp(-fDensity);
移植
在 Cocos Creator 3D v1.2
版本之前还未提供雾效果,那么该如何移植或定制呢?
把控一个重点,雾效果的关键就是计算雾效因素。
因为材质默认使用的是 builtin-standard.effect
,可以拷贝一份重新写个build-in-fog.effect
,然后再将需要雾化的材质选择这个effect
。
先在顶点着色器中加一个雾效因素的计算方法。
out float v_fogFactor;
float computeFogFactor(float fogDistance){
const float start = 1000.0;
const float end = 3000.0;
const float density = 0.0002;
// return clamp((end-fogDistance)/(end-start), 0.0, 1.0); //雾化因子线性变化
return 1.0 - smoothstep(start,end,fogDistance); //雾化因子非线性变化
// return exp(-(density*fogDistance)); // 指数雾
// return exp(-density*density*fogDistance*fogDistance); // 指数雾2
}
把雾效因素传给片元着色器。
v_fogFactor = computeFogFactor(length(cc_cameraPos.xyz - pos.xyz));
在片元着色器中接收雾效因素。
in float v_fogFactor;
计算混合颜色。
color.rgb = mix(fogColor, color.rgb, v_fogFactor);
效果预览(示例项目可以加Q群 859642112
获取):
小结
雾
fog
!计算雾效系数 !混合颜色 !shader
!
以上为白玉无冰讲解 Cocos Creator 3D v1.2
中的 "雾效果(fog)原理解析"
的技术分享。欢迎分享给身边的朋友!
天下事有难易乎?为之,则难者亦易矣;不为,则易者亦难矣。人之为学有难易乎?学之,则难者亦易矣;不学,则易者亦难矣。
- Python解析psiBlast输出的JSON文件结果
- 经验分享:社会工程学数据库搭建TIPS
- 过时但仍值得学习的选项卡TabHost
- 你所不知道的渗透测试:应用虚拟化的攻防
- C++中const小结
- 很多人不知道还有这个——搜索框组件SearchView
- 免费主题暗藏后门,波及WordPress等知名CMS系统
- 揭秘:针对PoS机的恶意软件工具箱
- 屏幕宽高不够,滚动视图ScrollView来凑
- 结合中间人攻击,Pidgin鸡肋漏洞变废为宝
- 日历视图CalendarView和定时器Chronometer
- 不用Linux也可以的强大文本处理方法
- 虚函数与虚继承寻踪
- AnalogClock、DigitalClock和TextClock时钟组件
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 5分钟入门GANS:原理解释和keras代码实现
- 使用ML 和 DNN 建模的技巧总结
- 医学图像分割模型U-Net介绍和Kaggle的Top1解决方案源码解析
- 机器学习中的音频特征:理解Mel频谱图
- 兄弟,如何淡定地渡过七夕?
- Spring 源码第 9 篇,深入分析 FactoryBean
- PowerBI 动态数据格式 高级版 以及重要通知
- 气哭老板的顶级密钥存放方案,又做了一件蠢事
- 构建没有数据集的辣辣椒分类器,准确性达到96%
- 由 Redis 分布式锁造成的重大事故
- 10分钟搞定 Java 并发队列好吗?好的
- MySQL 案例:关于程序端的连接池与数据库的连接数
- spark和kafka jar包冲突NoSuchMethodError: net.jpountz.lz4.LZ4BlockInputStream
- 聊聊claudb的scripting command
- PHP怎么获取视频总时长的函数方法