完整资源:

我的Github地址

前情提要:

从0开始编写minecraft光影包(0)GLSL,坐标系,光影包结构介绍

从零开始编写minecraft光影包(1)基础阴影绘制

从零开始编写minecraft光影包(2)阴影优化

从零开始编写minecraft光影包(3)基础泛光绘制

从零开始编写minecraft光影包(4)泛光性能与品质优化

从零开始编写minecraft光影包(5)简单光照系统,曝光调节,色调映射与饱和度

目录

  • 前言
  • 干掉原版天空
  • 天空基色绘制
  • 太阳与月亮绘制
  • 颜色混合
  • 昼夜交替平滑过渡
  • 小结

前言

在国庆又当懒狗了几天之后,终于决定又来更新博客了。。。。

今天更新天空的绘制,我们将绘制自定义的天空颜色以及太阳,月亮,雾,以及他们的昼夜交替效果。

注:本章节博客基于 上一篇博客 中完成的光影包代码

干掉原版天空

要绘制我们自定义的天空,首先我们需要干掉原版的天空,通过查阅资料可知,有两个着色器负责天空的绘制:

  1. gbuffers_skybasic :天空原色 + 星星
  2. 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 (点乘)操作,计算出他们之间的 “距离”

离太阳中心的距离小于一定范围,我们便认为这就是太阳。于是绘制太阳,值得注意的是,绘制的时候要考虑两个因素:

  1. 当前像素是否离太阳足够近
  2. 当前像素是否是天空(即离世界坐标系原点的距离是否足够远)

所以绘制太阳和月亮的两个 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)天空绘制相关推荐

  1. 从零开始编写minecraft光影包(8)中级水面绘制 水下阴影与焦散

    完整资源: 我的Github地址 前情提要: 从0开始编写minecraft光影包(0)GLSL,坐标系,光影包结构介绍 从零开始编写minecraft光影包(1)基础阴影绘制 从零开始编写minec ...

  2. 从零开始编写minecraft光影包(9)高级水面绘制 反射与屏幕空间反射

    完整资源: 我的Github地址 前情提要: 从0开始编写minecraft光影包(0)GLSL,坐标系,光影包结构介绍 从零开始编写minecraft光影包(1)基础阴影绘制 从零开始编写minec ...

  3. 从零开始编写minecraft光影包(5)简单光照系统,曝光调节,色调映射与饱和度

    完整资源: 我的Github地址 前情提要: 从0开始编写minecraft光影包(0)GLSL,坐标系,光影包结构介绍 从零开始编写minecraft光影包(1)基础阴影绘制 从零开始编写minec ...

  4. 从零开始编写minecraft光影包(4)泛光性能与品质优化

    完整资源: 我的Github地址 前情提要: 从0开始编写minecraft光影包(0)GLSL,坐标系,光影包结构介绍 从零开始编写minecraft光影包(1)基础阴影绘制 从零开始编写minec ...

  5. 我的世界光影mod怎么用_大片的正确打开方式-MineCraft光影材质包安装及使用教程...

    我从2014年开始玩MC到现在已经断断续续玩了1年了,这款像素方块游戏看起来不起眼,那是魅力深藏不露,一款能玩1年多的游戏,不但没有疲倦,而且周围跟着一起玩的人越来越多,可见这款游戏在某种程度已经激发 ...

  6. R语言使用ggplot2包geom_jitter()函数绘制分组(strip plot,一维散点图)带状图(编写自定义函数添加均值、标准偏差)实战

    R语言使用ggplot2包geom_jitter()函数绘制分组(strip plot,一维散点图)带状图(编写自定义函数添加均值.标准偏差)实战 目录 R语言使用ggplot2包geom_jitte ...

  7. 从零开始编写自己的C#框架(16)——Web层后端父类

    从零开始编写自己的C#框架(16)--Web层后端父类 原文:从零开始编写自己的C#框架(16)--Web层后端父类 本章节讲述的各个类是后端系统的核心之一,涉及到系统安全验证.操作日志记录.页面与按 ...

  8. 从零开始编写一个vue插件

    title: 从零开始编写一个vue插件 toc: true date: 2018-12-17 10:54:29 categories: Web tags: vue mathjax 写毕设的时候需要一 ...

  9. 从零开始制作 NuGet 源代码包(全面支持 .NET Core / .NET Framework / WPF 项目)

    默认情况下,我们打包 NuGet 包时,目标项目安装我们的 NuGet 包会引用我们生成的库文件(dll).除此之外,我们也可以专门做 NuGet 工具包,还可以做 NuGet 源代码包.然而做源代码 ...

最新文章

  1. iOS App与iTunes文件传输的方法和对iOS App文件结构的说明
  2. django ajax传参数
  3. angular监听图片加载完成_angular1.0 如何监听页面渲染完毕 (转)
  4. linux内核路由反向检查,反向路径过滤——reverse path filter
  5. bert中文预训练模型_HFL中文预训练系列模型已接入Transformers平台
  6. 思科路由和交换限制用户出外网的几种策略
  7. [原创]浅谈移动互联网App兼容性测试
  8. VS Code 中的文件添加图标的插件vscode-icons
  9. javascript、jquery获取网页的高度和宽度
  10. 五、spring-data-Jpa 数据库操作
  11. Visual Studio 2008创建项目(ATL)
  12. 手把手教你搭建pytorch深度学习网络
  13. Android SearchView 实现搜索框
  14. 25. Magento 创建新闻模块(5)
  15. SpringCloud学习(五)路由网关(zuul)(Finchley版本)
  16. DSP原理及图像处理应用
  17. Note :提取图像空间频率、色度、亮度、饱和度Python—Opencv
  18. 【企业网络】我在51cto技术门诊的提问以及专家的解答汇总
  19. win10安装CA证书服务器,分享一下win10系统安装数字证书的方法
  20. h3c交换机划分vlan

热门文章

  1. Symfony2浅析
  2. 计算机计算合格不合格的公式,excel给成绩等级划分_如何使用Excel计算优秀、良好、合格、不合格的比例?...
  3. bootload启动流程(四)--Eboot每个函数的详细说明
  4. java sns_SNS:美图秀秀的社交化变革
  5. 《Python编程:从入门到实践》课后习题-第5章
  6. pool win10提示bad_Win10系统电脑蓝屏提示bad pool header怎么办
  7. 父亲给女儿 九条人生忠告
  8. 如何选择一台高性能计算机,【小白必看】如何选购电脑?
  9. python基于django的商品比价平台
  10. python绘制时频图