python 深度 视差 计算_2,Learn about Parallax(视差贴图)

接上文 : https://zhuanlan.zhihu.com/p/128682162

浮雕视差贴图

浮雕视差贴图是陡峭视差贴图的进阶版.并允许GLSLshader更加精确地找到偏移的UV坐标.首先您要使用陡峭视差贴图.然后shader中会有两层的深度(最后采样的两层),而V和表面的交点坐落于这两层之间.如下图这两个图层在纹理坐标T3和T2的位置.现在您能使用二叉搜索来提高精度.二叉搜索的每一次迭代都能使得精度调高2.

下图为浮雕视差的原理:

  • 在经过陡峭视差算法之后,我们知道了纹理坐标T2和T3,V和曲面的实际交点在绿点位置.
  • 让当下每层的UV坐标偏移量和高度除以2.
  • 将T3以计算后的UV坐标偏移量沿着V相反的方向移动(向着T2).以当前层高度减少层深度.
  • (*)采样高度图.将当前每层的UV偏移量和高度除以2.
  • 如果采样高度大于层深度.那么以当前每层高度来增加层深度.并以当前的UV偏移量让当前UV坐标沿着V偏移.
  • 如果从纹理中采样的高度小于层深度,那么按照当下的层高减少层深度,并且以当下的偏移量来沿着V反向移动UV坐标.
  • 按照设定的次数重复步骤(*)处的二叉搜索.
  • 最后一次搜索的纹理坐标就是浮雕视差的结果

f8b5aa069db3b59f6bcf39ccc9daa4f9.png

以下是浮雕视差的实现:

vec2 parallaxMapping(in vec3 V, in vec2 T, out float parallaxHeight)
{
   // determine required number of layers
   const float minLayers = 10;
   const float maxLayers = 15;
   float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0, 0, 1), V)));

   // height of each layer
   float layerHeight = 1.0 / numLayers;
   // depth of current layer
   float currentLayerHeight = 0;
   // shift of texture coordinates for each iteration
   vec2 dtex = parallaxScale * V.xy / V.z / numLayers;

   // current texture coordinates
   vec2 currentTextureCoords = T;

   // depth from heightmap
   float heightFromTexture = texture(u_heightTexture, currentTextureCoords).r;

   // while point is above surface
   while(heightFromTexture > currentLayerHeight) 
   {
      // go to the next layer
      currentLayerHeight += layerHeight; 
      // shift texture coordinates along V
      currentTextureCoords -= dtex;
      // new depth from heightmap
      heightFromTexture = texture(u_heightTexture, currentTextureCoords).r;
   }

   ///
   // Start of Relief Parallax Mapping

   // decrease shift and height of layer by half
   vec2 deltaTexCoord = dtex / 2;
   float deltaHeight = layerHeight / 2;

   // return to the mid point of previous layer
   currentTextureCoords += deltaTexCoord;
   currentLayerHeight -= deltaHeight;

   // binary search to increase precision of Steep Paralax Mapping
   const int numSearches = 5;
   for(int i=0; i<numSearches; i++)
   {
      // decrease shift and height of layer by half
      deltaTexCoord /= 2;
      deltaHeight /= 2;

      // new depth from heightmap
      heightFromTexture = texture(u_heightTexture, currentTextureCoords).r;

      // shift along or agains vector V
      if(heightFromTexture > currentLayerHeight) // below the surface
      {
         currentTextureCoords -= deltaTexCoord;
         currentLayerHeight += deltaHeight;
      }
      else // above the surface
      {
         currentTextureCoords += deltaTexCoord;
         currentLayerHeight -= deltaHeight;
      }
   }

   // return results
   parallaxHeight = currentLayerHeight;    return currentTextureCoords;
}

视差闭塞贴图(POM)

视差闭塞贴图是陡峭视差贴图的另外一种进阶版本,浮雕视差应用二叉搜索来提升结果精度,但是过多的迭代次数会使性能下降.POM意在获得比浮雕视差更好的性能,并提供比陡峭视差更好的结果,但是POM的结果要比浮雕视差稍差一些.

POM简单在陡峭视差的结果之间进行插值.请看下图.POM使用了位于交点之后的层深度(0.375,也就是陡峭视差停止的位置),上一个H(T2)和下一个H(T3)为采样深度.就如同您从图片中看到的.POM的结果点位于向量V与H(T3)H(T2)连线的交点.该点已经足够接近真是的交点(绿标记点).

计算流程如下:

  • nextHeight = HT3 - currentLayerHeight;
  • prevHeight = HT2 - (currentLayerHeight - layerHeight)
  • weight = nextHeight / (nextHeight - prevHeight)//这`里nextHeight为负数,分母分子都是负数,最后的weight是正的
  • TP = TT2 * weight + TT3 * (1.0 - weight)

POM以相对较少的高度图采样次数获得较好的结果.但是POM相对浮雕视差舍去了高度图中细节.对于高度图中的突然变化可能产生不正确的结果.

8640a4b5ec04c2e232d3524d4b39d700.png

下面是POM在shader中的实现函数:

vec2 parallaxMapping(in vec3 V, in vec2 T, out float parallaxHeight)
{
   // determine optimal number of layers
   const float minLayers = 10;
   const float maxLayers = 15;
   float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0, 0, 1), V)));

   // height of each layer
   float layerHeight = 1.0 / numLayers;
   // current depth of the layer
   float curLayerHeight = 0;
   // shift of texture coordinates for each layer
   vec2 dtex = parallaxScale * V.xy / V.z / numLayers;

   // current texture coordinates
   vec2 currentTextureCoords = T;

   // depth from heightmap
   float heightFromTexture = texture(u_heightTexture, currentTextureCoords).r;

   // while point is above the surface
   while(heightFromTexture > curLayerHeight) 
   {
      // to the next layer
      curLayerHeight += layerHeight; 
      // shift of texture coordinates
      currentTextureCoords -= dtex;
      // new depth from heightmap
      heightFromTexture = texture(u_heightTexture, currentTextureCoords).r;
   }

   ///

   // previous texture coordinates
   vec2 prevTCoords = currentTextureCoords + texStep;

   // heights for linear interpolation
   float nextH = heightFromTexture - curLayerHeight;
   float prevH = texture(u_heightTexture, prevTCoords).r
                           - curLayerHeight + layerHeight;

   // proportions for linear interpolation
   float weight = nextH / (nextH - prevH);

   // interpolation of texture coordinates
   vec2 finalTexCoords = prevTCoords * weight + currentTextureCoords * (1.0-weight);

   // interpolation of depth values
   parallaxHeight = curLayerHeight + prevH * weight + nextH * (1.0 - weight);

   // return result
   return finalTexCoords;
}

视差贴图的自阴影

您可以应用类似于陡峭视差的算法来确定像素是否在阴影之内.您需要搜索的不是在表面之下,而是之上的采样点.当然偏移UV坐标的方向也要沿着光的方向(L)而不是摄像机(V).灯光矢量L必须和V一样在切线空间.能够直接用来指定偏移UV的方向.自投影计算的结果阴影因子范围在[0,1],这个值后面用来调整漫反射和高光的强度 .

2bf3a08de028471e62d6a4826b9f6e3c.png

计算硬阴影您必须沿着灯光的方向检测值直到表面下的第一个点.如果点在 表面之下那么阴影因子为0,否则为1.举个栗子.在下一张图中.H(TL1)小于层深度Ha,那么这个点在表面之下.阴影因子为 0.如果L没有点在表面之下,那么该像素就在灯光下,阴影因子等于1.阴影的质量很大程度取决于层数.调整比例值(Scale控制视差程度的)和灯光与法线的角度.会使得阴影出现锯齿甚至更加糟糕.

软阴影考虑沿灯光L的多个值.仅考虑曲面下的点.局部阴影因子为当前深度和采样深度的差,您还必须考虑到点和像素之间的距离.所以局部阴影因子要乘以(1 - stepIndex/numberOfSteps).为了计算最后的阴影因子您必须在局部阴影中求最大值.因此这就是计算软阴影的阴影因子的公式:

fe88cdfd1b5cb154c94c150ddd217849.png

以下是软阴影的阴影因子计算流程:

  • 设置阴影影子值为0.步进次数为4.
  • 沿着L步进到Ha,Ha比H(TL1)小.因此点在表面之下.计算局部阴影因子 Ha- H(TL1).这是第一次检查.而检查总次数为4.考虑到像素到点距离的影响.局部阴影因子要乘以(1 - 1/4).储存局部阴影因子.
  • 沿着L步进到Hb,Hb比H(TL2)小.因此点在表面之下.计算局部阴影因子 Hb- H(TL2).这是第二次检查.而检查总次数为4.考虑到像素到点距离的影响.局部阴影因子要乘以(1 - 2/4).储存局部阴影因子.
  • 沿着L继续步进.点在表面之上.
  • 沿着L继续步进.点也在表面之上.
  • 顶点在0.0层之上.停止沿着L的步进采样.
  • 选择局部阴影因子中的最大值作为最后的阴影因子.

(非翻译部分)局部阴影的乘法系数(1 - stepIndex/numberOfSteps)个人感觉描述它成采样点附近表面突起的距离系数来更合适.而不是像素到该点的距离.越远的突点对该点的阴影影响越小.如下图靠近采样点步进的ABC位置刚好有三个小峰挡着光线,那么这三个点都对采样点的阴影有贡献.但是最近的点贡献肯定最大.较远的点贡献最小.它们相对L的高度一样(layerHeight - heightFromTexTure),而A点最近,那么局部阴影因子最大值就在A点,但如果C点这个位置的峰非常高那么遮挡光纤效果好那么最大值就是它.

20881acd9182a3937ec85921960f4c92.png

4c5453f2323bd9f29aa101dfb2601aca.png

下面的代码片段包含了自阴影实现的shader函数:

float parallaxSoftShadowMultiplier(in vec3 L, in vec2 initialTexCoord,
                                       in float initialHeight)
{
   float shadowMultiplier = 1;

   const float minLayers = 15;
   const float maxLayers = 30;

   // calculate lighting only for surface oriented to the light source
   if(dot(vec3(0, 0, 1), L) > 0)
   {
      // calculate initial parameters
      float numSamplesUnderSurface = 0;
      shadowMultiplier = 0;
      float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0, 0, 1), L)));
      float layerHeight = initialHeight / numLayers;
      vec2 texStep = parallaxScale * L.xy / L.z / numLayers;

      // current parameters
      float currentLayerHeight = initialHeight - layerHeight;
      vec2 currentTextureCoords = initialTexCoord + texStep;
      float heightFromTexture = texture(u_heightTexture, currentTextureCoords).r;
      int stepIndex = 1;

      // while point is below depth 0.0 )
      while(currentLayerHeight > 0)
      {
         // if point is under the surface
         if(heightFromTexture < currentLayerHeight)
         {
            // calculate partial shadowing factor
            numSamplesUnderSurface += 1;
            float newShadowMultiplier = (currentLayerHeight - heightFromTexture) *
                                             (1.0 - stepIndex / numLayers);
            shadowMultiplier = max(shadowMultiplier, newShadowMultiplier);
         }

         // offset to the next layer
         stepIndex += 1;
         currentLayerHeight -= layerHeight;
         currentTextureCoords += texStep;
         heightFromTexture = texture(u_heightTexture, currentTextureCoords).r;
      }

      // Shadowing factor should be 1 if there were no points under the surface
      if(numSamplesUnderSurface < 1)
      {
         shadowMultiplier = 1;
      }
      else
      {
         shadowMultiplier = 1.0 - shadowMultiplier;
      }
   }
   return shadowMultiplier;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值