开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

蛮牛译馆

关注:578

当前位置:游戏蛮牛 技术专区 蛮牛译馆

查看: 1277|回复: 6

[Unity教程] Unity改进彩虹【2】——程序化彩虹的几种方法

[复制链接]  [移动端链接]
排名
7912
昨日变化
9

260

主题

298

帖子

1509

积分

Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15

UID
159865
好友
9
蛮牛币
3908
威望
0
注册时间
2016-8-1
在线时间
623 小时
最后登录
2017-10-15

蛮牛译员

发表于 2017-8-11 11:16:13 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
本帖最后由 manew_JR 于 2017-8-11 11:21 编辑

Unity改进彩虹——程序化彩虹的几种方法
我们的光之旅的旅程要求我们不仅要了解光的工作原理,还要了解我们如何感知颜色。彩虹有多少种颜色?为什么粉色不是其中之一?这是这篇文章将要讨论的一些问题


整个文章的目录如下:

Part 1. The Nature of Light
Part 2. Improving the Rainbow (Part 1)
Part 3. Improving the Rainbow (Part 2)
Part 4. Understanding Diffraction Grating
Part 5. The Mathematics of Diffraction Grating
Part 6. CD-ROM Shader: Diffraction Grating (Part 1)
Part 7. CD-ROM Shader: Diffraction Grating (Part 2)
Part 8. Iridescence on Mobile
Part 9. The Mathematics of Thin-Film Interference
Part 10. Car Paint Shader: Thin-Film Interference
文章最后将会给出大家一个链接来下载程序包


介绍

这篇文章将介绍在计算机图形学中最常用的技术来重现彩虹中出现的颜色。虽然这看起来是一个无用的练习,但实际上它有非常实际的应用。彩虹的每一种颜色都对应于特定波长的光。这种对应将允许我们模拟物理上的反射。

这篇文章的第二部分,改进了彩虹-第2部分,将引入一种新颖的方法,对着色器进行高度优化,是迄今为止取得过的最好的效果(见下文)。

本教程中讨论的所有技术的比较WebGL版本可以在Shadertoy上找到。

色彩感知

视网膜是眼睛的一部分,专门用于探测光线。在那里,锥体细胞能够在探测到特定波长的光时向大脑发出信号。由于光在电磁场中是一种波,所以锥细胞在同样的原理下工作,使我们能够探测无线电波。锥细胞实际上是微小的天线。如果你学过电子学,你应该知道天线的长度与它捕获的波长有关。这就是为什么人类的眼睛有三种不同类型的锥体细胞:短,中,长。每一个都是专门用来探测特定波长范围的。

001.png

上图显示了每个锥体细胞对不同波长的反应有多强烈。当其中一个锥体激活时,大脑将它们的信号解释为颜色。尽管常说,短、中、长锥体细胞并不代表特定的颜色。更正确的是,每种类型对不同颜色的反应都不同。

假设短、中、长锥体细胞检测出蓝色、绿色和红光,这是不正确的。尽管如此,许多教科书(甚至着色器!)都依赖于这个假设,以获得一个相对可接受的近似,这是一个非常复杂的现象。


光谱颜色

如果我们想要再现使彩虹成为可能的物理现象,我们需要重新思考我们在电脑中储存和操纵颜色的方法。当你在Unity(或任何其他游戏引擎)中创建一个光源时,你可以指定它的颜色,作为三个主要成分的混合:红色,绿色和蓝色。虽然红色、绿色和蓝色的光可以混合在一起创造出所有可见的颜色,但这并不是光在最基本的层面上的作用。

.
光源可以被模拟成连续的光子流。携带不同能量的光子被我们的眼睛视为不同的颜色。然而,没有“白色光子”。它是许多光子的总和,每个光子有不同的波长,这使它的白色外观变得明亮。

我们在这个系列的未来文章中所需要的是能够谈论关于光的构建块。当我们谈论“波长”时,你应该考虑彩虹的特定颜色。这篇文章展示了使这一联系成为可能的不同方法。我们想要达到的是,最终,一个给定光波波长的函数返回它所感知的颜色:

[C] 纯文本查看 复制代码
fixed3 spectralColor (float wavelength);

在本系列的其余部分中,我们将用纳米来表达波长(十亿分之一米)。人类的眼睛可以感知到从400纳米到700纳米的光。范围以外的波长确实存在,但不被认为是颜色。

为什么没有一个最优解?

光谱图

下图显示了人眼如何感知波长在400纳米(蓝色)到700纳米(红色)的波长。
002.png
可见光谱中颜色的分布是高度非线性的。如果我们对每个波长,分别对应的R,G和B的颜色,我们最终会得到这样的结果:
11.png
没有一个简单的函数可以完全复制那条曲线。我们所能实现的最简单、最便宜的方法就是简单地使用我们的着色器中的纹理来将波长映射到颜色。

第一步是在着色器中创建一个新的纹理。我们可以通过在属性块中添加一个纹理属性来实现这一点。

[C] 纯文本查看 复制代码
// Properties
Properties
{
    ...
    _SpectralTex("Spectral Map (RGB)",2D) = "white" {}
    ...
}
// Shader code
SubShader
{
    ...
    CGPROGRAM
    ...
    sampler2D _SpectralTex;
    ...
    ENDCG
    ...
}



我们的光谱色度函数只能将范围[400,700]中的波长重新映射到范围[0,1]中的UV坐标:

[C] 纯文本查看 复制代码
fixed3 spectral_tex (float wavelength)
{
    // wavelength: [400, 700]
    // u:          [0,   1]
    fixed u = (wavelength -400.0) / 300.0;
    return tex2D(_SpectralTex, fixed2(u, 0.5));
}


在这个具体的例子中,我们不需要在范围内强制波长[400,700]。如果用Repeat来导入光谱纹理,那么在该范围之外的任何值将自动显示为黑色。

在一个循环中采样纹理…

JET 颜色方案


采样纹理看起来是个不错的主意。然而,它可能会极大地慢下来。我们将看到我们的关键在于cd - roms彩虹色,每个像素都需要几个纹理样本。

有几个函数近似于光谱中颜色的分布。其中一个最简单的可能就是JET色彩方案。这是MATLAB中默认的颜色方案,它最初是为了更好地想象来自国家超级计算机应用中心的天体物理流体喷射模拟而设计的
666.png


JET色彩方案是三种不同的曲线组合:蓝色、绿色和红色。颜色分解很明显地突出了这一点:
555.png


我们可以很容易地重新实现JET色彩方案,通过编写上面的图的线的方程。
[C] 纯文本查看 复制代码
// MATLAB Jet Colour Scheme
fixed3 spectral_jet(float w)
{
        // w: [400, 700]
        // x: [0,   1]
        fixed x = saturate((w - 400.0)/300.0);
        fixed3 c;

        if (x < 0.25)
                c = fixed3(0.0, 4.0 * x, 1.0);
        else if (x < 0.5)
                c = fixed3(0.0, 1.0, 1.0 + 4.0 * (0.25 - x));
        else if (x < 0.75)
                c = fixed3(4.0 * (x - 0.5), 1.0, 0.0);
        else
                c = fixed3(1.0, 1.0 + 4.0 * (0.75 - x), 0.0);

        // Clamp colour components in [0,1]
        return saturate(c);
}


所产生的颜色的R、G和B值均以[0,1]为上限,使用Cg函数饱和。如果你的相机设置为HDR(高动态范围渲染),那么就有必要避免使用超过1的组件的颜色。

请注意,如果你想严格遵守JET色彩计划,在可见范围之外的值不会是黑色的。

布鲁顿颜色方案

另一种将波长转换成可见光颜色的方法是由丹·布鲁顿在“可见光波长的近似RGB值”中提供的。同样地,他开始从一个近似分布的颜色被感知到。
888.png
然而,他的方法更接近于长锥体细胞的活动,在可见光谱的低端显示出更多的紫色:
000.png

翻译成以下代码:
[C] 纯文本查看 复制代码
// Dan Bruton
fixed3 spectral_bruton (float w)
{
        fixed3 c;

        if (w >= 380 && w < 440)
                c = fixed3
                (
                        -(w - 440.) / (440. - 380.),
                        0.0,
                        1.0
                );
        else if (w >= 440 && w < 490)
                c = fixed3
                (
                        0.0,
                        (w - 440.) / (490. - 440.),
                        1.0
                );
        else if (w >= 490 && w < 510)
                c = fixed3
                (        0.0,
                        1.0,
                        -(w - 510.) / (510. - 490.)
                );
        else if (w >= 510 && w < 580)
                c = fixed3
                (
                        (w - 510.) / (580. - 510.),
                        1.0,
                        0.0
                );
        else if (w >= 580 && w < 645)
                c = fixed3
                (
                        1.0,
                        -(w - 645.) / (645. - 580.),
                        0.0
                );
        else if (w >= 645 && w <= 780)
                c = fixed3
                (        1.0,
                        0.0,
                        0.0
                );
        else
                c = fixed3
                (        0.0,
                        0.0,
                        0.0
                );

        return saturate(c);
}


撞色方案
JET和Bruton颜色方案都使用不连续函数。因此,它们的颜色变化非常明显。而且,它们在可见范围之外不会消失。GPU Gems(GPU Gems)以更温和的颠簸取代了之前的色彩计划的尖锐线条来解决这些问题。每个撞只是一个抛物线的类型y = 1 - x ^ 2。更具体地说。

   
作者Randima Fernando为每一个颜色组件使用一个凹凸点,按如下方式排列:
45.png
46.png



我们可以写以下代码:
[C] 纯文本查看 复制代码
// GPU Gems
inline fixed3 bump3 (fixed3 x)
{
        float3 y = 1 - x * x;
        y = max(y, 0);
        return y;
}

fixed3 spectral_gems (float w)
{
           // w: [400, 700]
        // x: [0,   1]
        fixed x = saturate((w - 400.0)/300.0);
        
        return bump3
        (        fixed3
                (
                        4 * (x - 0.75),        // Red
                        4 * (x - 0.5),        // Green
                        4 * (x - 0.25)        // Blue
                )
        );
}


这种颜色方案的另一个优点是,它不使用纹理样本或分支,如果你喜欢性能而不是质量,这是最好的解决方案之一。在本教程的最后,您将看到这个颜色方案的修订版本,它提供了最好的性能,同时仍然能产生很高的颜色保真度。

Spektre颜色方案

一种最精确的配色方案是由Stack Overflow使用Spektre。他们用RGB的可见光谱来解释他们的方法,在那里他们从太阳光谱中取样蓝色、绿色和真实数据的组成部分。然后,它们以简单的函数来满足个体的间隔。其结果如下图所示:
75.png
产生出:
76.png

下面是代码:
[C] 纯文本查看 复制代码
// Spektre
fixed3 spectral_spektre (float l)
{
        float r=0.0,g=0.0,b=0.0;
                        if ((l>=400.0)&&(l<410.0)) { float t=(l-400.0)/(410.0-400.0); r=    +(0.33*t)-(0.20*t*t); }
        else if ((l>=410.0)&&(l<475.0)) { float t=(l-410.0)/(475.0-410.0); r=0.14         -(0.13*t*t); }
        else if ((l>=545.0)&&(l<595.0)) { float t=(l-545.0)/(595.0-545.0); r=    +(1.98*t)-(     t*t); }
        else if ((l>=595.0)&&(l<650.0)) { float t=(l-595.0)/(650.0-595.0); r=0.98+(0.06*t)-(0.40*t*t); }
        else if ((l>=650.0)&&(l<700.0)) { float t=(l-650.0)/(700.0-650.0); r=0.65-(0.84*t)+(0.20*t*t); }
                        if ((l>=415.0)&&(l<475.0)) { float t=(l-415.0)/(475.0-415.0); g=             +(0.80*t*t); }
        else if ((l>=475.0)&&(l<590.0)) { float t=(l-475.0)/(590.0-475.0); g=0.8 +(0.76*t)-(0.80*t*t); }
        else if ((l>=585.0)&&(l<639.0)) { float t=(l-585.0)/(639.0-585.0); g=0.82-(0.80*t)           ; }
                        if ((l>=400.0)&&(l<475.0)) { float t=(l-400.0)/(475.0-400.0); b=    +(2.20*t)-(1.50*t*t); }
        else if ((l>=475.0)&&(l<560.0)) { float t=(l-475.0)/(560.0-475.0); b=0.7 -(     t)+(0.30*t*t); }

        return fixed3(r,g,b);
}



结语

本文提供了一些最常用的技术,以在着色器中生成类似彩虹的模式。本文的第二部分,改进《彩虹》第二部分,将介绍一种新的方法来解决这个问题。
Name
Gradient
JET
Bruton
GPU Gems
Spektre
Zucconi
Zucconi6
Visible
以下是文章目录:

Part 1. The Nature of Light
Part 2. Improving the Rainbow (Part 1)
Part 3. Improving the Rainbow (Part 2)
Part 4. Understanding Diffraction Grating
Part 5. The Mathematics of Diffraction Grating
Part 6. CD-ROM Shader: Diffraction Grating (Part 1)
Part 7. CD-ROM Shader: Diffraction Grating (Part 2)
Part 8. Iridescence on Mobile
Part 9. The Mathematics of Thin-Film Interference
Part 10. Car Paint Shader: Thin-Film Interference


原文标题:Improving the Rainbow – Part 1







[发帖际遇]: 一个袋子砸在了 manew_JR 头上,manew_JR 赚了 1 蛮牛币. 幸运榜 / 衰神榜

回复

使用道具 举报

7日久生情
1870/5000
排名
6543
昨日变化
68

4

主题

1530

帖子

1870

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
209186
好友
0
蛮牛币
2458
威望
0
注册时间
2017-3-1
在线时间
184 小时
最后登录
2017-10-18
发表于 2017-8-14 09:19:51 | 显示全部楼层
学习了,part2
[发帖际遇]: luastudy 发帖时在路边捡到 1 蛮牛币,偷偷放进了口袋. 幸运榜 / 衰神榜

回复

使用道具 举报

排名
28106
昨日变化
8

0

主题

23

帖子

61

积分

Rank: 2Rank: 2

UID
119006
好友
0
蛮牛币
1
威望
0
注册时间
2015-8-20
在线时间
30 小时
最后登录
2017-8-14
发表于 2017-8-14 10:52:30 | 显示全部楼层
请问恶趣味去切切

回复 支持 反对

使用道具 举报

2初来乍到
127/150
排名
10304
昨日变化
20

0

主题

26

帖子

127

积分

Rank: 2Rank: 2

UID
221395
好友
0
蛮牛币
154
威望
0
注册时间
2017-5-9
在线时间
31 小时
最后登录
2017-9-25
发表于 2017-8-14 13:59:11 | 显示全部楼层

学习了,part2

回复

使用道具 举报

4四处流浪
321/500
排名
9332
昨日变化
109

0

主题

178

帖子

321

积分

Rank: 4

UID
235323
好友
1
蛮牛币
486
威望
0
注册时间
2017-8-1
在线时间
57 小时
最后登录
2017-10-17
发表于 2017-8-22 21:18:25 | 显示全部楼层
66666666,谢谢分享
[发帖际遇]: lixun123456 发帖时在路边捡到 1 蛮牛币,偷偷放进了口袋. 幸运榜 / 衰神榜

回复 支持 反对

使用道具 举报

4四处流浪
319/500
排名
8008
昨日变化
187

3

主题

152

帖子

319

积分

Rank: 4

UID
234982
好友
0
蛮牛币
591
威望
0
注册时间
2017-7-30
在线时间
60 小时
最后登录
2017-10-18
发表于 2017-9-2 11:10:24 | 显示全部楼层
撸代码撸一撸,大撸多撸强撸,撸撸撸撸撸撸..............................

回复 支持 反对

使用道具 举报

4四处流浪
349/500
排名
10961
昨日变化
13

0

主题

239

帖子

349

积分

Rank: 4

UID
235457
好友
0
蛮牛币
340
威望
0
注册时间
2017-8-2
在线时间
46 小时
最后登录
2017-10-6
发表于 2017-9-4 13:25:32 | 显示全部楼层
6666666666666666666

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

快速回复 返回顶部 返回列表