specular图使用方法_CSharpGL(54)用基于图像的光照(IBL)来计算PBR的Specular部分

CSharpGL(54)用基于图像的光照(IBL)来计算PBR的Specular部分

接下来本系列将通过翻译(https://learnopengl.com)这个网站上关于PBR的内容来学习PBR(Physically Based Rendering)。

+BIT祝威+悄悄在此留下版了个权的信息说:

原文虽然写得挺好,但是仍旧不够人性化。过一阵我自己总结总结PBR,写一篇更容易理解的。

正文

In the previous tutorial we've set up PBR in combination with image based lighting by pre-computing an irradiance map as the lighting's indirect diffuse portion. In this tutorial we'll focus on the specular part of the reflectance equation:

在上一篇教程中我们已经用IBL(基于图像的光照)来解决了PBR的一个问题:用预计算的辐照度贴图作为非直接光照的diffuse部分。在本教程中我们将关注反射率方程中的speclar部分。

+BIT祝威+悄悄在此留下版了个权的信息说:

You'll notice that the Cook-Torrance specular portion (multiplied by kS) isn't constant over the integral and is dependent on the incoming light direction, but also the incoming view direction. Trying to solve the integral for all incoming light directions including all possible view directions is a combinatorial overload and way too expensive to calculate on a real-time basis. Epic Games proposed a solution where they were able to pre-convolute the specular part for real time purposes, given a few compromises, known as the split sum approximation.

你会注意到Cook-Torrance的specular部分(乘以kS的那个)在积分上不是常量,它既依赖入射光方向,又依赖观察者方向。对所有入射光反向和所有观察者方向的乘积的积分进行求解,是过载又过载,对于实时计算是太过昂贵了。Epic游戏公司提出了一个解决方案(被称为拆分求和近似),用一点妥协,通过预计算卷积实现了specular部分的实时计算。

The split sum approximation splits the specular part of the reflectance equation into two separate parts that we can individually convolute and later combine in the PBR shader for specular indirect image based lighting. Similar to how we pre-convoluted the irradiance map, the split sum approximation requires an HDR environment map as its convolution input. To understand the split sum approximation we'll again look at the reflectance equation, but this time only focus on the specular part (we've extracted the diffuse part in the previous tutorial):

拆分求和近似方案,将反射率方程的specular部分拆分为两个单独的部分,我们可以分别对齐进行卷积,之后再结合起来。在shader中可以实现这样的specular的IBL。与我们预计算辐照度贴图相似,拆分求和近似方案需要一个HDR环境贴图作为输入。为了理解这个方案,我们再看一下反射率方程,但这次只关注specular部分(我们已经在上一篇教程中分离出了diffuse部分):

For the same (performance) reasons as the irradiance convolution, we can't solve the specular part of the integral in real time and expect a reasonable performance. So preferably we'd pre-compute this integral to get something like a specular IBL map, sample this map with the fragment's normal and be done with it. However, this is where it gets a bit tricky. We were able to pre-compute the irradiance map as the integral only depended on ωi and we could move the constant diffuse albedo terms out of the integral. This time, the integral depends on more than just ωi as evident from the BRDF:

由于和辐照度卷积相同的原因(性能),我们不能实时求解specular部分的积分,还期待一个可接受的性能。所以我们倾向于预计算这个积分,得到某种specular的IBL贴图,用片段的法线在贴图上采样,得到所需结果。但是,这里比较困难。我们能预计算辐照度贴图,是因为它的积分只依赖ωi,而且我们还能吧diffuse颜色项移到积分外面。这次,从BRDF公式上可见,积分不止依赖一个ωi。

This time the integral also depends on wo and we can't really sample a pre-computed cubemap with two direction vectors. The position p is irrelevant here as described in the previous tutorial. Pre-computing this integral for every possible combination of ωi and ωo isn't practical in a real-time setting.

这次,积分还依赖于wo,我们无法对有2个方向向量的预计算cubemap贴图进行采样。这里的位置p是无关的,我们在上一篇教程中讲过。在实时系统中预计算这个积分的ωi和ωo的每种组合,是不实际的。

Epic Games' split sum approximation solves the issue by splitting the pre-computation into 2 individual parts that we can later combine to get the resulting pre-computed result we're after. The split sum approximation splits the specular integral into two separate integrals:

Epic游戏公司的拆分求和近似方案解决了这个问题:把预计算拆分为2个互相独立的部分,且之后可以联合起来得到我们需要的预计算结果。拆分求和近似方案将specular积分拆分为2个独立的积分:

The first part (when convoluted) is known as the pre-filtered environment map which is (similar to the irradiance map) a pre-computed environment convolution map, but this time taking roughness into account. For increasing roughness levels, the environment map is convoluted with more scattered sample vectors, creating more blurry reflections. For each roughness level we convolute, we store the sequentially blurrier results in the pre-filtered map's mipmap levels. For instance, a pre-filtered environment map storing the pre-convoluted result of 5 different roughness values in its 5 mipmap levels looks as follows:

卷积时的第一部分被称为pre-filter环境贴图(类似辐照度贴图),是个预计算的环境卷积贴图,但这次它考虑了粗糙度。对于增长的粗糙度level,环境贴图用更散射的采样向量进行卷积,这造成了更模糊的反射。对我们卷积的每个粗糙度level,我们依次在pre-filter贴图的mipmap层上保存它。例如,一个保存着预计算结果的5个不同粗糙度(用6个mipmap层)的pre-fitler环境贴图如下图所示:

We generate the sample vectors and their scattering strength using the normal distribution function (NDF) of the Cook-Torrance BRDF that takes as input both a normal and view direction. As we don't know beforehand the view direction when convoluting the environment map, Epic Games makes a further approximation by assuming the view direction (and thus the specular reflection direction) is always equal to the output sample direction ωo. This translates itself to the following code:

Cook-Torrance函数以法线和观察者方向为输入,我们用这个函数生成采样的方向向量和散射强度。卷积环境贴图时,我们无法提前预知观察者方向,Epic游戏公司又做了个近似,假设观察者方向(即specular反射方向)总是等于输出采样方向ωo。相应的代码如下:

1 vec3 N =normalize(w_o);2 vec3 R =N;3 vec3 V = R;

This way the pre-filtered environment convolution doesn't need to be aware of the view direction. This does mean we don't get nice grazing specular reflections when looking at specular surface reflections from an angle as seen in the image below (courtesy of the Moving Frostbite to PBR article); this is however generally considered a decent compromise:

这样,pre-filter环境贴图就不需要知道观察者方向。这意味着,在从下图(感谢Moving Frostbite to PBR 文章)所示的角度观察光滑表面反射时,我们得不到比较好的掠角反射结果。但是这一般被认为是相当好的折衷方案了。

The second part of the equation equals the BRDF part of the specular integral. If we pretend the incoming radiance is completely white for every direction (thus L(p,x)=1.0) we can pre-calculate the BRDF's response given an input roughness and an input angle between the normal n and light direction ωi, or n⋅ωi. Epic Games stores the pre-computed BRDF's response to each normal and light direction combination on varying roughness values in a 2D lookup texture (LUT) known as the BRDF integration map. The 2D lookup texture outputs a scale (red) and a bias value (green) to the surface's Fresnel response giving us the second part of the split specular integral:

方程的第二部分是specular积分的BRDF部分。如果我们假设入射光在所有方向上都是白色(即L(p,x)=1.0),我们就能预计算BRDF对给定粗糙度和入射角(法线n和入射方向ωi的夹角,或n⋅ωi)的返回值。Epic游戏公司保存了预计算的BRDF对每个法线+入射方向组合的返回值,保存到一个二维查询纹理(LUT)上,即BRDF积分贴图。这个二维查询纹理输出的是表面的菲涅耳效应的一个缩放和偏移值,也就是拆分开的specular积分的第二部分。

+BIT祝威+悄悄在此留下版了个权的信息说:

(我不懂这是啥)

We generate the lookup texture by treating the horizontal texture coordinate (ranged between 0.0 and 1.0) of a plane as the BRDF's input n⋅ωi and its vertical texture coordinate as the input roughness value. With this BRDF integration map and the pre-filtered environment map we can combine both to get the result of the specular integral:

我们将纹理坐标的U(从0.0到1.0)作为BRDF的n⋅ωi参数,将V坐标视为粗糙度参数,以此生成查询纹理。现在我们就可以联合BRDF积分贴图和pre-filter环境贴图来得到specular的积分结果:

1 float lod =getMipLevelFromRoughness(roughness);2 vec3 prefilteredColor =textureCubeLod(PrefilteredEnvMap, refVec, lod);3 vec2 envBRDF =texture2D(BRDFIntegrationMap, vec2(NdotV, roughness)).xy;4 vec3 indirectSpecular = prefilteredColor * (F * envBRDF.x + envBRDF.y)

This should give you a bit of an overview on how Epic Games' split sum approximation roughly approaches the indirect specular part of the reflectance equation. Let's now try and build the pre-convoluted parts ourselves.

这些应该让你大体上理解,Epic游戏公司的拆分求和近似方案是如何计算反射率方程的非直接specular部分的。现在我们试试自己构建预卷积部分。

预卷积HDR环境贴图

Pre-filtering an environment map is quite similar to how we convoluted an irradiance map. The difference being that we now account for roughness and store sequentially rougher reflections in the pre-filtered map's mip levels.

预卷积一个环境贴图,与我们卷积辐照度贴图类似。不同点在于,我们现在要考虑粗糙度,并且将越来越粗糙的反射情况依次保存到贴图的各个mipmap层里。

First, we need to generate a new cubemap to hold the pre-filtered environment map data. To make sure we allocate enough memory for its mip levels we call glGenerateMipmap as an easy way to allocate the required amount of memory.

首先,我们需要生成一个cubemap对象,用于保存pre-filter的环境贴图数据。为保证我们为它的mipmap层分配了足够的内存,我们使用glGenerateMipmap这一简便方式来分配需要的内存。

1 unsigned intprefilterMap;2 glGenTextures(1, &prefilterMap);3 glBindTexture(GL_TEXTURE_CUBE_MAP, prefilterMap);4 for (unsigned int i = 0; i < 6; ++i)5 {6 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, 128, 128, 0, GL_RGB, GL_FLOAT, nullptr);7 }8 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);9 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);10 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);11 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);12 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);13

14 glGenerateMipmap(GL_TEXTURE_CUBE_MAP);

Note that because we plan to sample the prefilterMap its mipmaps you'll need to make sure its minification filter is set to GL_LINEAR_MIPMAP_LINEAR to enable trilinear filtering. We store the pre-filtered specular reflections in a per-face resolution of 128 by 128 at its base mip level. This is likely to be enough for most reflections, but if you have a large number of smooth materials (think of car reflections) you may want to increase the resolution.

注意,因为我们计划对prefilterMap 的各层mipmap采样,你需要确保它的最小过滤参数设置为GL_LINEAR_MIPMAP_LINEAR ,这样才能启用三线过滤。以128x128像素为mipmap基层(第一层),我们将pre-filter的反射情况保存到cubemap的各个面上。这对大多数反射情况都足够用,但是如果你有大量光滑材质(例如汽车的反射),你可能需要增加分辨率。

In the previous tutorial we convoluted the environment map by generating sample vectors uniformly spread over the hemisphere Ω using spherical coordinates. While this works just fine for irradiance, for specular re

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值