从零开始编写minecraft光影包(6)天空绘制
完整资源:
我的Github地址
前情提要:
从0开始编写minecraft光影包(0)GLSL,坐标系,光影包结构介绍
从零开始编写minecraft光影包(1)基础阴影绘制
从零开始编写minecraft光影包(2)阴影优化
从零开始编写minecraft光影包(3)基础泛光绘制
从零开始编写minecraft光影包(4)泛光性能与品质优化
从零开始编写minecraft光影包(5)简单光照系统,曝光调节,色调映射与饱和度
目录
- 前言
- 干掉原版天空
- 天空基色绘制
- 太阳与月亮绘制
- 颜色混合
- 昼夜交替平滑过渡
- 小结
前言
在国庆又当懒狗了几天之后,终于决定又来更新博客了。。。。
今天更新天空的绘制,我们将绘制自定义的天空颜色以及太阳,月亮,雾,以及他们的昼夜交替效果。
注:本章节博客基于 上一篇博客 中完成的光影包代码
干掉原版天空
要绘制我们自定义的天空,首先我们需要干掉原版的天空,通过查阅资料可知,有两个着色器负责天空的绘制:
- gbuffers_skybasic :天空原色 + 星星
- gbuffers_skytextured : 太阳与月亮
那么我们需要干掉原版的天空,让这两个着色器输出纯黑即可。我们编写四个着色器,分别是:
gbuffers_skybasic.fsh,gbuffers_skybasic.vsh,gbuffers_skytextured.fsh,gbuffers_skytextured.vsh
其中 .vsh 的内容都是:
#version 120void main() {gl_Position = ftransform();
}
与之对应,.fsh 的内容为:
#version 120void main() {gl_FragData[0] = vec4(vec3(0), 1.0);
}
再次加载光影包,我们发现原版的天空已经被我们干掉了,太阳,月亮以及其他天空颜色都消失了。。。现在可以开始着手绘制我们的天空了
天空基色绘制
因为没有一个很好的机制来判断何处是天空,我们采用曲线救国的方式,通过距离计算原色与天空颜色混合(即mix线性混合)。
即:越远的方块,颜色越靠近天空的颜色,这样绘制最终会形成雾的效果。这样会比直接使用距离作为阈值来绘制天空要平滑很多。
要绘制天空颜色,首先我们需要有昼夜变换的的天空颜色,我们在 composite.vsh 中加入:
uniform int worldTime;varying vec3 mySkyColor;vec3 skyColorArr[24] = {vec3(0.1, 0.6, 0.9), // 0-1000vec3(0.1, 0.6, 0.9), // 1000 - 2000vec3(0.1, 0.6, 0.9), // 2000 - 3000vec3(0.1, 0.6, 0.9), // 3000 - 4000vec3(0.1, 0.6, 0.9), // 4000 - 5000 vec3(0.1, 0.6, 0.9), // 5000 - 6000vec3(0.1, 0.6, 0.9), // 6000 - 7000vec3(0.1, 0.6, 0.9), // 7000 - 8000vec3(0.1, 0.6, 0.9), // 8000 - 9000vec3(0.1, 0.6, 0.9), // 9000 - 10000vec3(0.1, 0.6, 0.9), // 10000 - 11000vec3(0.1, 0.6, 0.9), // 11000 - 12000vec3(0.1, 0.6, 0.9), // 12000 - 13000vec3(0.02, 0.2, 0.27), // 13000 - 14000vec3(0.02, 0.2, 0.27), // 14000 - 15000vec3(0.02, 0.2, 0.27), // 15000 - 16000vec3(0.02, 0.2, 0.27), // 16000 - 17000vec3(0.02, 0.2, 0.27), // 17000 - 18000vec3(0.02, 0.2, 0.27), // 18000 - 19000vec3(0.02, 0.2, 0.27), // 19000 - 20000vec3(0.02, 0.2, 0.27), // 20000 - 21000vec3(0.02, 0.2, 0.27), // 21000 - 22000vec3(0.02, 0.2, 0.27), // 22000 - 23000vec3(0.02, 0.2, 0.27) // 23000 - 24000(0)
};
这个数组表示了一天24小时中,昼夜的天空颜色的变换,然后在main函数中加入如下的代码,根据世界时间 worldTime
对颜色进行实时地平滑过渡取值。
// 颜色过渡插值
int hour = worldTime/1000;
int next = (hour+1<24)?(hour+1):(0);
float delta = float(worldTime-hour*1000)/1000;// 天空颜色
mySkyColor = mix(skyColorArr[hour], skyColorArr[next], delta);
其中 mySkyColor
负责将天空颜色从顶点着色器传递到片段着色器。
然后我们在 composite.fsh 中编写drawSky
函数,该函数接收不包含天空的原始颜色,以及其他必要的数据,并且绘制天空:
/** @function drawSky : 天空绘制* @param color : 原始颜色* @param positionInViewCoord : 眼坐标* @param positionInWorldCoord : 我的世界坐标* @return : 绘制天空后的颜色*/
vec3 drawSky(vec3 color, vec4 positionInViewCoord, vec4 positionInWorldCoord) {// 距离float dis = length(positionInWorldCoord.xyz) / far;return mix(color, mySkyColor, clamp(pow(dis, 3), 0, 1));
}
这一步仅仅是根据像素在世界坐标系中的距离,进行简单的颜色混合。越远的像素,颜色越接近天空,越近的像素则不受影响:
虽然很简陋,但是我们完成了一个简单的天空基色的绘制。以线性混合的形式,通过距离将很远的像素绘制上颜色,以表示天空。
太阳与月亮绘制
天空绘制非常简单,但是现在有个带问题:没有太阳和月亮,因为我们在 gbuffer 着色器中将他们干掉了。。。
所以我们迫切需要绘制太阳和月亮,我们同样需要先在 composite.vsh 中,和计算天空颜色类似,我们计算太阳(或者月亮)的颜色。
在 composite.vsh 中加入:
varying vec3 mySunColor;vec3 skyColorArr[24] = {vec3(0.1, 0.6, 0.9), // 0-1000vec3(0.1, 0.6, 0.9), // 1000 - 2000vec3(0.1, 0.6, 0.9), // 2000 - 3000vec3(0.1, 0.6, 0.9), // 3000 - 4000vec3(0.1, 0.6, 0.9), // 4000 - 5000 vec3(0.1, 0.6, 0.9), // 5000 - 6000vec3(0.1, 0.6, 0.9), // 6000 - 7000vec3(0.1, 0.6, 0.9), // 7000 - 8000vec3(0.1, 0.6, 0.9), // 8000 - 9000vec3(0.1, 0.6, 0.9), // 9000 - 10000vec3(0.1, 0.6, 0.9), // 10000 - 11000vec3(0.1, 0.6, 0.9), // 11000 - 12000vec3(0.1, 0.6, 0.9), // 12000 - 13000vec3(0.02, 0.2, 0.27), // 13000 - 14000vec3(0.02, 0.2, 0.27), // 14000 - 15000vec3(0.02, 0.2, 0.27), // 15000 - 16000vec3(0.02, 0.2, 0.27), // 16000 - 17000vec3(0.02, 0.2, 0.27), // 17000 - 18000vec3(0.02, 0.2, 0.27), // 18000 - 19000vec3(0.02, 0.2, 0.27), // 19000 - 20000vec3(0.02, 0.2, 0.27), // 20000 - 21000vec3(0.02, 0.2, 0.27), // 21000 - 22000vec3(0.02, 0.2, 0.27), // 22000 - 23000vec3(0.02, 0.2, 0.27) // 23000 - 24000(0)
};
这个数组同样代表平滑过渡的太阳颜色取值。然后在 composite.vsh 的 main 函数中,计算天空颜色的部分之后,加入:
// 太阳颜色
mySunColor = mix(sunColorArr[hour], sunColorArr[next], delta);
以完成对太阳颜色的平滑过渡取值。
随后进入到 composite.fsh 中,添加如下两个uniform变量:
uniform vec3 sunPosition;
uniform vec3 moonPosition;
他们代表太阳或者月亮在眼坐标系中的坐标。然后,我们修改一下 drawSky 函数的内容:
vec3 drawSky(vec3 color, vec4 positionInViewCoord, vec4 positionInWorldCoord) {// 距离float dis = length(positionInWorldCoord.xyz) / far;// 眼坐标系中的点到太阳的距离float disToSun = 1.0 - dot(normalize(positionInViewCoord.xyz), normalize(sunPosition)); // 太阳float disToMoon = 1.0 - dot(normalize(positionInViewCoord.xyz), normalize(moonPosition)); // 月亮// 绘制圆形太阳vec3 drawSun = vec3(0);if(disToSun<0.005 && dis>0.99999) {drawSun = mySunColor * 2;}// 绘制圆形月亮vec3 drawMoon = vec3(0);if(disToMoon<0.005 && dis>0.99999) {drawMoon = mySunColor * 2;}// 根据距离进行最终颜色的混合return mix(color, mySkyColor, clamp(pow(dis, 3), 0, 1)) + drawSun + drawMoon;
}
首先我们通过 positionInViewCoord 变量, 即当前像素在眼坐标系中的坐标,和太阳(或者月亮)在眼坐标系中的坐标进行 dot (点乘)操作,计算出他们之间的 “距离”
离太阳中心的距离小于一定范围,我们便认为这就是太阳。于是绘制太阳,值得注意的是,绘制的时候要考虑两个因素:
- 当前像素是否离太阳足够近
- 当前像素是否是天空(即离世界坐标系原点的距离是否足够远)
所以绘制太阳和月亮的两个 if 需要这么判断。重新加载光影包,我们可以看到,太阳和月亮已经绘制成功了:
颜色混合
在现实世界中,天空往往会被太阳(或者月亮)的颜色染色,我们希望实现这一特效。
和绘制太阳一样,我们仍然通过眼坐标系中的距离,判断当前像素是否足够靠近太阳。如果是,则将其颜色和太阳的颜色 mySunColor 进行线性混合。
我们修改 drawSky 函数:
vec3 drawSky(vec3 color, vec4 positionInViewCoord, vec4 positionInWorldCoord) {// 距离float dis = length(positionInWorldCoord.xyz) / far;// 眼坐标系中的点到太阳的距离float disToSun = 1.0 - dot(normalize(positionInViewCoord.xyz), normalize(sunPosition)); // 太阳float disToMoon = 1.0 - dot(normalize(positionInViewCoord.xyz), normalize(moonPosition)); // 月亮// 绘制圆形太阳vec3 drawSun = vec3(0);if(disToSun<0.005 && dis>0.99999) {drawSun = mySunColor * 2;}// 绘制圆形月亮vec3 drawMoon = vec3(0);if(disToMoon<0.005 && dis>0.99999) {drawMoon = mySunColor * 2;}// 雾和太阳颜色混合float sunMixFactor = clamp(1.0 - disToSun, 0, 1);vec3 finalColor = mix(mySkyColor, mySunColor, pow(sunMixFactor, 4));// 雾和月亮颜色混合float moonMixFactor = clamp(1.0 - disToMoon, 0, 1);finalColor = mix(finalColor, mySunColor, pow(moonMixFactor, 4));// 根据距离进行最终颜色的混合return mix(color, finalColor, clamp(pow(dis, 3), 0, 1)) + drawSun + drawMoon;
}
其中在绘制太阳和月亮之后,加入了混合颜色的步骤。我们对那些靠近太阳(或者月亮)的像素,将其基色和太阳颜色进行线性混合(mix函数)。
再次重新加载光影包,我们可以看到,天空逐渐被太阳的颜色染色:
昼夜交替平滑过渡
事到如今,我们的天空已经绘制的较为完善了,接下来我们需要对其瑕疵进行修复。
因为太阳和月亮使用的是同一套颜色,那么昼夜交替的时候,就会出现两个太阳(或者两个月亮),这显然是不够平滑的。
还记得我们怎么解决阴影的昼夜交替问题
从零开始编写minecraft光影包(6)天空绘制相关推荐
- 从零开始编写minecraft光影包(8)中级水面绘制 水下阴影与焦散
完整资源: 我的Github地址 前情提要: 从0开始编写minecraft光影包(0)GLSL,坐标系,光影包结构介绍 从零开始编写minecraft光影包(1)基础阴影绘制 从零开始编写minec ...
- 从零开始编写minecraft光影包(9)高级水面绘制 反射与屏幕空间反射
完整资源: 我的Github地址 前情提要: 从0开始编写minecraft光影包(0)GLSL,坐标系,光影包结构介绍 从零开始编写minecraft光影包(1)基础阴影绘制 从零开始编写minec ...
- 从零开始编写minecraft光影包(5)简单光照系统,曝光调节,色调映射与饱和度
完整资源: 我的Github地址 前情提要: 从0开始编写minecraft光影包(0)GLSL,坐标系,光影包结构介绍 从零开始编写minecraft光影包(1)基础阴影绘制 从零开始编写minec ...
- 从零开始编写minecraft光影包(4)泛光性能与品质优化
完整资源: 我的Github地址 前情提要: 从0开始编写minecraft光影包(0)GLSL,坐标系,光影包结构介绍 从零开始编写minecraft光影包(1)基础阴影绘制 从零开始编写minec ...
- 我的世界光影mod怎么用_大片的正确打开方式-MineCraft光影材质包安装及使用教程...
我从2014年开始玩MC到现在已经断断续续玩了1年了,这款像素方块游戏看起来不起眼,那是魅力深藏不露,一款能玩1年多的游戏,不但没有疲倦,而且周围跟着一起玩的人越来越多,可见这款游戏在某种程度已经激发 ...
- R语言使用ggplot2包geom_jitter()函数绘制分组(strip plot,一维散点图)带状图(编写自定义函数添加均值、标准偏差)实战
R语言使用ggplot2包geom_jitter()函数绘制分组(strip plot,一维散点图)带状图(编写自定义函数添加均值.标准偏差)实战 目录 R语言使用ggplot2包geom_jitte ...
- 从零开始编写自己的C#框架(16)——Web层后端父类
从零开始编写自己的C#框架(16)--Web层后端父类 原文:从零开始编写自己的C#框架(16)--Web层后端父类 本章节讲述的各个类是后端系统的核心之一,涉及到系统安全验证.操作日志记录.页面与按 ...
- 从零开始编写一个vue插件
title: 从零开始编写一个vue插件 toc: true date: 2018-12-17 10:54:29 categories: Web tags: vue mathjax 写毕设的时候需要一 ...
- 从零开始制作 NuGet 源代码包(全面支持 .NET Core / .NET Framework / WPF 项目)
默认情况下,我们打包 NuGet 包时,目标项目安装我们的 NuGet 包会引用我们生成的库文件(dll).除此之外,我们也可以专门做 NuGet 工具包,还可以做 NuGet 源代码包.然而做源代码 ...
最新文章
- iOS App与iTunes文件传输的方法和对iOS App文件结构的说明
- django ajax传参数
- angular监听图片加载完成_angular1.0 如何监听页面渲染完毕 (转)
- linux内核路由反向检查,反向路径过滤——reverse path filter
- bert中文预训练模型_HFL中文预训练系列模型已接入Transformers平台
- 思科路由和交换限制用户出外网的几种策略
- [原创]浅谈移动互联网App兼容性测试
- VS Code 中的文件添加图标的插件vscode-icons
- javascript、jquery获取网页的高度和宽度
- 五、spring-data-Jpa 数据库操作
- Visual Studio 2008创建项目(ATL)
- 手把手教你搭建pytorch深度学习网络
- Android SearchView 实现搜索框
- 25. Magento 创建新闻模块(5)
- SpringCloud学习(五)路由网关(zuul)(Finchley版本)
- DSP原理及图像处理应用
- Note :提取图像空间频率、色度、亮度、饱和度Python—Opencv
- 【企业网络】我在51cto技术门诊的提问以及专家的解答汇总
- win10安装CA证书服务器,分享一下win10系统安装数字证书的方法
- h3c交换机划分vlan
热门文章
- Symfony2浅析
- 计算机计算合格不合格的公式,excel给成绩等级划分_如何使用Excel计算优秀、良好、合格、不合格的比例?...
- bootload启动流程(四)--Eboot每个函数的详细说明
- java sns_SNS:美图秀秀的社交化变革
- 《Python编程:从入门到实践》课后习题-第5章
- pool win10提示bad_Win10系统电脑蓝屏提示bad pool header怎么办
- 父亲给女儿 九条人生忠告
- 如何选择一台高性能计算机,【小白必看】如何选购电脑?
- python基于django的商品比价平台
- python绘制时频图