Unity立体渲染系列教程链接:

Unity教程|立体渲染(一)

Unity立体渲染(二)|Raymarching

Unity立体渲染(三)|表面着色


本教程为Unity立体渲染(Volumetric Rendering,为保持一致,后续译文均称立体渲染)系列最终篇,前面三篇分别介绍了立体渲染的基本概念、Raymarching技术以及表面着色。本篇将介绍如何在体积着色器中创建复杂的三维模型。

本系列教程全集:
第一篇:立体渲染。介绍立体渲染的概念以及在Unity中如何实现立体渲染。 第二篇:光线追踪。文章着重说明如何实现距离辅助的光线追踪,这是是渲染立体的事实性标准技术。 第三篇:表面着色。全面指引如何逼真地进行立体着色。 第四篇:有向距离函数。一篇对于数学工具更深入的讨论,让我们能制作和组合任意几何体。

本片教程介绍了如何在体积着色器中创建复杂的三维模型。有向距离函数(通常被称为场)是用来描述球形,盒子和环面的几何形状的数学工具。和传统的由三角形组成的3D模型相比,有向距离函数提供了几乎无限的分辨率,并且适合进行计划操作。下面的动画来自于一个动画教程:制作一个蜗牛,展示了如何使用更简单的形状去创建一个蜗牛。

150535o49bp4mwrnq4uwvy.png

因为同样的原因,显而易见的获取两个SDF的最大值则返回了他们的交集。

float map (float3 p) { 
  return max ( 
  sdf_sphere(p, - float3 (1.5, 0, 0), 2), 
  // Left sphere sdf_sphere(p, + float3 (1.5, 0, 0), 2) 
  // Right sphere 
  ); 
}


150536ybwmrmgq043bq55k.png
SDF 盒子

很多几何图形可以用我们已知的方式进行构建。如果我们想把知识更进一步,我们需要引入一个新的SDF原型:半空间。就像名字所指出的那样,它只是一个原始的占据了半个3D空间的东西。

// X Axis d = + p.x - c.x; 
// Left half-space full d = - p.x + c.x; 
// Right half-space full 
// Y Axis// X Axis d = + p.x - c.x; 
// Left half-space full d = - p.x + c.x; 
// Right half-space full 
// Y Axis d = + p.y - c.y; 
// Left half-space full d = - p.y + c.y; 
// Right half-space full 
// Z Axis d = + p.z - c.z; 
// Left half-space full d = - p.z + c.z; 
// Right half-space full d = + p.y - c.y; 
// Left half-space full d = - p.y + c.y; 
// Right half-space full 
// Z Axis d = + p.z - c.z; 
// Left half-space full d = - p.z + c.z; 
// Right half-space full


关键点是要用6个平面相交,以创建一个给定大小为s的盒子,就如下面动画所示:

float sdf_box (float3 p, float3 c, float3 s) 
{ 
  float x = max ( p.x - _Centre.x - float3(s.x / 2., 0, 0), _Centre.x - p.x - float3(s.x / 2., 0, 0) ); float y = max ( p.y - _Centre.y - float3(s.y / 2., 0, 0), _Centre.y - p.y - float3(s.y / 2., 0, 0) ); float z = max ( p.z - _Centre.z - float3(s.z / 2., 0, 0), _Centre.z - p.z - float3(s.z / 2., 0, 0) ); 
  float d = x; d = max(d,y); 
  d = max(d,z); return d; 
}


150536sc4ay4egvycacav0.gif

有更简洁(但不够精确)的方法来创建一个盒子,利用了中心周围的对称性。

float vmax(float3 v) 
{ 
  return max(max(v.x, v.y), v.z);
} 

float sdf_boxcheap(float3 p, float3 c, float3 s) 
{ 
  return vmax(abs(p-c) - s); 
}


形状混合
如果你熟悉alpha混合的概念,你可能会认得下面的代码:

float sdf_blend(float d1, float d2, float a) 
{ 
  return a * d1 + (1 - a) * d2; 
}


这么做的目的是创建d1和d2两个值之间的混合,通过a的值(从0到1)进行控制。用于混合颜色的代码也可以用于混合形状。例如,下面的代码将一个球体混合到一个立方体中:

d = sdf_blend ( sfd_sphere(p, 0, r), sfd_box(p, 0, r), (_SinTime[3] + 1.) / 2. );


150537cy6x6chx3xv1qcfl.gif

光滑合并
在上一章节中我们已经看到两个SDF可以通过取最小值的方式合并在一起。SDF的合集虽然确实是有效的,但它的结果会有一点不真实。SDF可以将原物体以多种方式混合在一起。其中一个技巧就是,指数平滑(链接:最小光滑)已经被广泛使用在本教程的原始动画中。

float sdf_smin(float a, float b, float k = 32) 
{ 
  float res = exp(-k*a) + exp(-k*b); 
  return -log(max(0.0001,res)) / k; 
}


当两个形状以这个新的操作进行结合时,他们会平滑地合并,创建一个温和的步骤去移除所有锋利的边缘。在下面的动画中,你可以看到球体们是如何合并在一起的:

150720hk19kirc1pprskc1.gif

SDF 代数
可以预见的是,那些SDF元物体以及操作是有向距离函数代数的一部分。旋转,缩放,混合,扭曲…所有这些操作都可以用有向距离函数来表示。
在他名为《使用有向距离函数建模》的文章中,Íñigo Quílez创造了很多SDF,可以用来构建更复杂的几何体。你们可以通过点击下面的可交互ShaderToy进行查看:点击链接进入ShaderToy
一个更大的元物件和操作的集合在MERCURY团队创建的hg_sdf(链接)库中。虽然是由GLSL写的,但是函数可以很方便的移植到Unity的Cg/HLSL中。

结论
可以通过SDF表现的几何体几乎是无限的。这篇文章只是提供一个该主题的简介。如果你真的想掌握体积渲染,增加你对SDF的了解是一个很好的起点.
Unity, 立体渲染, 着色, 系列教程锐亚教育

锐亚教育 锐亚科技 unity unity教程