Unity物体外表面渲染「PART-1」-基本方法介绍

Unity物体外表面渲染「PART-1」-基本方法介绍

基本方法

体绘制

**
**

这是一个的教程关于体绘制,射线运用和标记距离区域的第一部分。这些技术使我们能够克服现代3D引擎的最大的限制,它只让我们渲染一个对象的外表。立体绘制使创建逼真的材料,与光以一种复杂的方式相互作用,如雾,烟雾,水和玻璃。制作精美的效果如NMZ的等离子球(下边),没有立体渲染是不可能的。

这些技术并不复杂,但需要大量的步骤,以复制上述效果。这个教程会包括上边这些。

  • 1部分:体绘制|介绍体绘制意味着什么,以及它如何可以做到在Unity中;

  • 2部分:射线测量|实施远程辅助渲染,达到事实标准技术来渲染物体;

  • 3部分:表面着色|全面指导如何实现真实的渲染;

  • 4部分:符号距离函数|在数学上的工具,使我们能够进行任意深度的探讨;

  • 5部分:硬和软阴影|如何添加真实的阴影;

  • 6部分:立体光线投射| raymarching的一种变形,可以渲染半透明表面如烟雾一样。

这第一部分将提供一个通用的介绍,以一个简单的着色,将是我们未来的不断学习的基础,做一个总体介绍:

简介

1部分。体积渲染

2部分。立体光线投射

3部分。射线检测

结论

介绍

球体,立方体和其他复杂的几何形状,用三角化在三维引擎渲染,根据定义来说是平滑的。Unity所采用的实时照明系统,只能够绘制平坦的表面。例如,当你在渲染一个球体时,只有一个三角形来绘制它的表面。即使是半透明的材料,只有外壳实际上是画的,它的颜色与它背后的一个对象混合效果。在照明系统中没有试图探测到一个材料的立体信息。对于GPU,只是制作了一个空壳。

有广泛的技术存在,为了克服这个强大的限制。即使是真的,一个传统的着色最终停止在一个对象的表面,它并不意味着我们不能去更深的位置。体绘制技术模拟光线的传播到一个材料的体积内部,体现出惊人的和复杂的视觉效果。

体绘制

一个无光纹理对象的片段着色器看起来像这样:

fixed4 frag (v2f i) : SV_Target

{

fixed4 col = tex2D(_MainTex, i.texcoord);

return col;

}

粗略地说,在最终的渲染图像中,每一个潜在的像素(片段)都调用了这段代码。当GPU调用片段着色器,是因为有一个三角形和摄像机。换句话说,相机看到的对象。统一需要知道该对象的确切颜色,以便它可以将它分配给所呈现的图像中的相应的像素。

的颜色,如从一个特定的角度看。这种颜色的计算方法是完全任意的。没有什么不允许我们“欺骗”,并返回一些不一定与我们真实渲染的几何体相匹配的东西。下面的图显示了在渲染三维立方体时的一个例子。当片段渲染器被查询得到立方体的脸的颜色,我们返回相同的颜色,我们可以看到在一个球体。几何是一个立方体,但从相机的角度来看,它看起来和感觉完全像一个球体。

这是立体绘制基本概念:模拟光如何在一个对象的体内传播。

如果我们要模拟上一张图所显示的效果,我们需要更精确地描述它。我们说,我们的主要结构是一个立方体,我们想渲染一个球体的体积内。实际上没有与球体相关的几何,因为我们将通过着色代码完全的渲染它。我们的球的中心在_centreand的半径_radius,表示世界坐标。移动立方体不会影响球体的位置,因为它被表示成绝对的世界坐标。它也值得注意的是,外部几何没有任何用途,不用改变其余的教程。立方体的外壳的三角形成为允许在几何体内部看到的入口。我们可以通过使用一个方块,但一个立方体可以从每一个角度看到体积球。

光线投射

体绘制的第一种方法完全像以前的图表一样。片段着色器接收我们绘制的点(worldposition)我们朝着方向(viewdirection);然后使用一个函数调用raycasthit表明我们是否击中红色球。这项技术被称为立体光线投射,因为它延伸的射线投射从相机到几何物体。

我们现在可以为我们的片段着色写一个代码:

float3 _Centre;

float _Radius;

fixed4 frag (v2f i) : SV_Target

{

float3 worldPosition = ...

float3 viewDirection = ...

if ( raycastHit(worldPosition, viewDirection) )

return fixed4(1,0,0,1); // Red if hit the ball

else

return fixed4(1,1,1,1); // White otherwise

}

让我们解决每一个没有的组件。

世界坐标

首先,片段程序的世界的位置是由相机产生的射线击中的几何点。我们已经看到在顶点和片段着色器如何在一个片段着色中检索世界的位置:

struct v2f {

float4 pos : SV_POSITION; // Clip space

float3 wPos : TEXCOORD1; // World position

};

v2f vert (appdata_full v)

{

v2f o;

o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

o.wPos = mul(_Object2World, v.vertex).xyz;

return o;

}

视线方向

其次,视图方向是来自相机的光线的方向,并在我们正在渲染的几何点上。这就要求我们知道相机的位置;其中统一包括内置变量_worldspacecamerapos。通过这两个点的方向可以计算如下:

float3 viewDirection = normalize(i.wPos - _WorldSpaceCameraPos);

光线投射函数

我们现在需要的是一个功能raycasthit函数,给我们渲染点和方向,决定如果我们几种虚拟的红色球体。这是一个与一个线段相交的问题。这个问题的公式存在,但他们通常是非常低效的。如果你想去一个分析的方法,你将需要推导公式与自定义几何相交段。此解决方案强烈约束你可以创建的模型,因此它很少被采用。

固定参数的体射线检测

正如在上一节中,纯分析体积光线投射并不是我们的问题的一个可行的方法。如果我们要模拟任意体量,我们需要找到一个更灵活的技术,不依赖于相交的方程。一个常见的解决方案是为体积射线检测,它是基于一个迭代的方法。

体积射线检测慢慢延伸入立方体的体积内。在每一步中,它询问是否射线是目前击中球体。

每一射线从片段的位置worldposition,并反复延长STEP_SIZE单位在定义的viewdirection方向。这可以通过代数增加STEP_SIZE * viewDirection在worldPosition上,每次迭代后执行。

现在我们可以用下面的raymarchhit函数代替raycasthit

#define STEPS 64

#define STEP_SIZE 0.01

bool raymarchHit (float3 position, float3 direction)

{

for (``int i = 0; i < STEPS; i++)

{

if ( sphereHit(position) )

return true``;

position += direction * STEP_SIZE;

}

return false``;

}

这项技术所需的剩余的一部分,它来测试是否一个点P是在一个球体内:

bool sphereHit (float3 p)

{

return distance(p,_Centre) < _Radius;

}

与球面相交的射线是很难的,但反复检查,如果一个点是在一个球体是容易的。

结果是相当漂亮的红色球体。尽管看起来像一个普通的圆,这实际上是一个亮的球。

结论

本文介绍了体绘制的概念。即使传统的着色站在一个材料的外壳上,它是可能的,保持投射这些射线在材料的体积,以创造深度的错觉。raymarching是一种最常用的技术。我们使用的是画一个亮的红色球体。我们将在下面的教程中看到如何真实地绘制它(部分3。表面阴影),如何使有趣的形状(3部分。标记距离区域),甚至如何添加阴影(6部分。硬和软的阴影)。在这个系列的结束,你将能够创建这样的对象,只需三行代码和一个体积着色:

本教程的下一部分将包括距离辅助的raymaching方法,这是用于体绘制达到真是效果的标准技术。

原文地址:http://www.alanzucconi.com/2016/07/01/volumetric-rendering/

标签: unity, 渲染

?>