博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[MetalKit]33-Ambient-Occlusion-in-Metal环境光遮蔽
阅读量:7090 次
发布时间:2019-06-28

本文共 3977 字,大约阅读时间需要 13 分钟。

本系列文章是对 上面MetalKit内容的全面翻译和学习.


今天我们将学习ambient occlusion环境光遮蔽.我们将使用Shadows in Metal part 2的playground代码.首先,让我们添加一个新的对象类型-矩形盒子:

struct Box {
float3 center; float size; Box(float3 c, float s) { center = c; size = s; }};复制代码

下一步,让我为新的结构体再添加一个新的距离函数:

float distToBox(Ray r, Box b) {    float3 d = abs(r.origin - b.center) - float3(b.size);    return min(max(d.x, max(d.y, d.z)), 0.0) + length(max(d, 0.0));}复制代码

然后,更新我们的场景:

float distToScene(Ray r) {    Plane p = Plane(0.0);    float d2p = distToPlane(r, p);    Sphere s1 = Sphere(float3(0.0, 0.5, 0.0), 8.0);    Sphere s2 = Sphere(float3(0.0, 0.5, 0.0), 6.0);    Sphere s3 = Sphere(float3(10., -5., -10.), 15.0);    Box b = Box(float3(1., 1., -4.), 1.);    float dtb = distToBox(r, b);    float d2s1 = distToSphere(r, s1);    float d2s2 = distToSphere(r, s2);    float d2s3 = distToSphere(r, s3);    float dist = differenceOp(d2s1, d2s2);    dist = differenceOp(dist, d2s3);    dist = unionOp(dist, dtb);    dist = unionOp(d2p, dist);    return dist;}复制代码

我们刚才做的是首先绘制一个半径为8的球体,一个半径为6的球体,并求出它们的差集.因为它们中心相同,所以小的那个看不到,除非我们做个横截面.这就是为什么我们用到了第三个球体,大很多而且中心也不同.我们再取一次差集,就能看到第一个差集的结果.最后,我们添加一个盒子,来让它更好看更多样.如果你现在运行playground你将看到类似的图像:

下一步,让我们删除lighting()shadow() 函数,因为我们不再需要他们了.还有,删除Light结构体和内核中的两个实例.现在让我们创建一个ambient occlusion环境光遮蔽的替代函数:

float ao(float3 pos, float3 n) {    return n.y * 0.5 + 0.5;}复制代码

我们在灯光中只用到了法线的y分量,就像有一个正上方的灯光一样.在内核中,创建法线之后(在else括号中),调用ao()函数:

float o = ao(ray.origin, n);col = col * o;复制代码

只有一个基本(正上方)灯光时,没有阴影了.如果你现在运行playground你将看到类似的图像:

是时候来点真正的ambient occlusion环境光遮蔽了. Ambient环境光意味着灯光不是来自一个定义好的光源,而是意味着一般的背景光照. * Occlusion遮蔽*意思是多少环境光被阻挡了.我们在曲面上取一个射线碰撞的点,观察它的周围.如果周围有一个物体,那颜色值阻挡场景中的大部分光源,所以这是一个暗区.如果周围没有东西,那就是亮区.对于处于中间状态的情况,我们需要精确计算出多少光被阻塞了.介绍一下cone tracing圆锥追踪概念.

cone tracing圆锥追踪的想法就是在场景中使用一个圆锥体代替射线.如果圆锥与物体相交,我们不仅仅能得到一个简单的true/false的结果.我们可以得到物体在该点处覆盖了多少圆锥体.但是我们如何追踪一个圆锥呢?我们可以使用许多球体来做一个圆锥.试着想一下许多球体排成一行,一头小一头大.这就是我们目前能近似得到的圆锥体.下面是我们需要步骤:

  • 从曲面上的一个点开始
  • 沿法线方向走出曲面
  • 每次迭代,用距离函数确定球体的多少被场景填充了
  • 每次迭代,离曲面的距离翻倍,同时球体尺寸翻倍

因为我们每步都把球体尺寸翻倍,这就意味着我们只需要几步迭代就可以很快从曲面表面出来.这也给了我们一个很棒的宽的圆锥.下面是完整的ao()函数:

float ao(float3 pos, float3 n) {    float eps = 0.01;    pos += n * eps * 2.0;    float occlusion = 0.0;    for (float i=1.0; i<10.0; i++) {        float d = distToScene(Ray(pos, float3(0)));        float coneWidth = 2.0 * eps;        float occlusionAmount = max(coneWidth - d, 0.);        float occlusionFactor = occlusionAmount / coneWidth;        occlusionFactor *= 1.0 - (i / 10.0);        occlusion = max(occlusion, occlusionFactor);        eps *= 2.0;        pos += n * eps;    }    return max(0.0, 1.0 - occlusion);}复制代码

让我们一行一行看看这些代码.首先,我们定义了eps变量,它包含了圆锥半径和距离曲面的距离.然后,我们移出去一点来避免我们碰撞到我们离开的表面.下一步,我们定义occlusion遮蔽变量,初始化为nil(场景是完全被照亮的).然后,我们进入循环,每次迭代我们拿到场景距离,将半径加倍以便知道圆锥的多少被遮蔽了,确保排队了灯光的负值,拿到遮蔽数量(比率)乘以圆锥宽度,给远处的遮蔽(可以从迭代次数获取远近)设置一个低的影响因子,保存当前最高的遮蔽值,将eps加倍并沿法线移动同样距离.然后返回一个值,它代表有多少光线到达了这个点.

现在让我们创建个camera结构体.它需要一个位置.我们只需储存一个射线来代替摄像机方向.最后rayDivergence给我们一个因子,代表射线扩散了多少.

struct Camera {
float3 position; Ray ray = Ray(float3(0), float3(0)); float rayDivergence; Camera(float3 pos, Ray r, float div) { position = pos; ray = r; rayDivergence = div; }};复制代码

下一步,设置摄像机.需要一个摄像机位置,观察目标/朝向,视场和视图坐标:

Camera setupCam(float3 pos, float3 target, float fov, float2 uv, int x) {    uv *= fov;    float3 cw = normalize(target - pos );    float3 cp = float3(0.0, 1.0, 0.0);    float3 cu = normalize(cross(cw, cp));    float3 cv = normalize(cross(cu, cw));    Ray ray = Ray(pos, normalize(uv.x * cu + uv.y * cv + 0.5 * cw));    Camera cam = Camera(pos, ray, fov / float(x));    return cam;}复制代码

现在我们只需要初始化摄像机.我们让它环绕场景,朝向中心**(0,0,0)**.添加到内核,放在uv变量创建后:

float3 camPos = float3(sin(time) * 10., 3., cos(time) * 10.);Camera cam = setupCam(camPos, float3(0), 1.25, uv, width);复制代码

然后删除ray变量,用cam.ray替换内核中用到它的地方.如果你现在运行playground你将看到类似的图像:

要看这份代码的动画效果,我在下面使用一个Shadertoy嵌入式播放器.只要把鼠标悬浮在上面,并单击播放按钮就能看到动画:<译者注:这里不支持嵌入播放器,我用gif代替https://www.shadertoy.com/embed/4ltSWf>

源代码已发布在Github上.

下次见!

转载地址:http://wpiql.baihongyu.com/

你可能感兴趣的文章
从1到6,机智云打造传统企业的物联网转身攻略
查看>>
通用电气进军国际工业物联网 “数码芯片厂”在巴黎开张
查看>>
RSA 2015:关于物联网,我们该做些什么?
查看>>
国际刑警组织:打击网络数字犯罪需全球协作
查看>>
办公娱乐一站式服务 威联通TS-251 NAS评测
查看>>
行业企业“攻克”大数据需从三方面入手
查看>>
程序员要有持续产出
查看>>
解读全球最严重的5起勒索软件攻击
查看>>
亚信安全4A管理平台中标国家信息中心 自主可控提升我国电子政务安全
查看>>
Salesforce签署多年协议 在其数据中心使用戴尔基础架构
查看>>
绿盟科技互联网安全威胁周报2016.34 本周关注ntp拒绝服务漏洞
查看>>
云计算与大数据下的革新 行业应用广泛
查看>>
云上贵州•大数据国际年会将于7月11日在贵州召开
查看>>
TMT监管的未来:从电信运营商网络到OTT网络效应
查看>>
Linux内核里的DebugFS
查看>>
7个影响数据分析的数据建模错误
查看>>
专家困惑了:勒索软件TeslaCrypt解密主密钥公开
查看>>
开发者必知的8款App快速开发工具
查看>>
又一轮高“烤”来袭 ,千万别忘数据备份
查看>>
深度探讨网络攻击给企业带来的商业影响
查看>>