请选择 进入手机版 | 继续访问电脑版

查看: 10269|回复: 2

[原创] Unity教程|立体渲染(一)

[复制链接]

1214

主题

1918

帖子

2万

贡献

管理员

Rank: 9Rank: 9Rank: 9

积分
25200
QQ
发表于 2016-9-19 06:01:30 | 显示全部楼层 |阅读模式
近期我们将陆续推出专注于立体渲染、光线追踪、表面着色及有向距离场的Unity系列教程。本文是第一篇:立体渲染(Volumetric Rendering)。这些技术能突破现代3D引擎只能渲染物体外壳的最大限制。立体渲染可以实现逼真的材质与灯光进行复杂的交互,例如雾、烟、水和玻璃。查看NMZ的Plasma Globe效果了解立体渲染的基本概念,如下图:



诚然,这些技术本身并不复杂,但实现上图效果需要非常多的步骤。本教程将为大家全面介绍以下概念:

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


本文将介绍立体渲染的基本概念,并以一个简单的着色器收尾,后续的文章将以此为基础进行迭代:

  • 介绍
  • 第一部分 立体渲染
  • 第二部分 立体射线投射
  • 第三部分 固定步长的立体光线追踪
  • 结论


介绍


在3D游戏引擎中,球体、立方体以及所有其它复杂的集合体都是由三角面片组成的。Unity采用的实时光照系统只能渲染平面。例如渲染球体,Unity仅绘制球体表面的三角形。尽管一些材质是半透明的,也只绘制表面并将其颜色与后面的物体进行混合。光照系统无法探测到材质的几何体的内部。对于GPU来说,整个世界就是由各种空壳构成的。

为了突破这种强大的限制,大量技术顺势而出。尽管传统着色器最终都会止于物体表面,但这并不意味着无法更深入。立体渲染技术可以在材质内部模拟光线的传送,从而实现更震撼也更真实的视觉效果。

立体渲染

无光照纹理的片段着色器如下:
[C#] 纯文本查看 复制代码
fixed4 frag (v2f i) : SV_Target
{
        fixed4 col = tex2D(_MainTex, i.texcoord);
        return col;
}

不严格地说,上述代码会为最终渲染图像每一个可能的像素(片段)调用。GPU会在有三角形与相机视锥体相交时,调用该片段着色器。换句话说,就是相机“看见了”该对象。Unity需要知道该对象的实际颜色,以便将其分配到渲染图像的各个像素。



片段着色器最后返回的对象,是从特定角度看过去特定位置的颜色。这种方式计算的颜色是完全随意的,因此返回的内容可以不必匹配几何体的真实渲染情况。下图展示了一个3D立方体的例子。当片段着色器检测到立方体表面的颜色时,我们获得的颜色如同我们在一个球体上所看到的。这个几何体是个立方体,但是从相机的角度来看,它的外观和感觉其实“酷似”一个球体。



这就是立体渲染(Volumetric Rendering)的基本概念:模拟光线在物体内部的传送。

如果想模拟前面的效果,就要更精确地进行描述。假设主物体是一个立方体,要在其内部立体渲染一个球体,实际上并不存在这个球体,因为我们将完全通过着色器代码来渲染。球体中心点位于_Centre,半径是_Radius,均为世界坐标。移动立方体不会影响球体位置,因为它是完全以世界坐标系来表述的。外部的几何体也不会对该球体造成任何影响。立方体表面的三角形就是通向几何体内部的窗口。虽然可以使用四边形(Quad)减少三角形数量,但要能从立方体的任意角度观看该球体。

立体射线投射

第一种立体渲染的方式完全适用于实现前文所述的效果。片段着色器接收要渲染的点(wolrdPosition)以及视线方向(viewDirection),然后使用raycastHit函数检测是否投射到红色球体。这种技术叫做立体射线投射(Volumetric Raycasting),它将射线从相机投射到几何体内部。

在片段着色器函数中添加剩下的代码:

[C#] 纯文本查看 复制代码
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
}


下面来解释代码中的其它变量。

世界坐标

首先,片段的世界坐标就是从相机生成的射线投射到几何体上的点。在片段着色器中获取世界坐标的代码如下:

[C#] 纯文本查看 复制代码
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;
}

视线方向

其次,视线方向就是射线从相机投射到几何体上被渲染的点的方向。这里需要知道相机坐标,Unity已内置了该变量 _WorldSpaceCameraPos。计算通过两点的射线方向可使用如下代码:

[C#] 纯文本查看 复制代码
float3 viewDirection = normalize(i.wPos - _WorldSpaceCameraPos);

Raycast Hit函数

当我们知道了渲染点的坐标和方向后,现在需要使用raycastHit函数来决定射线是否投射到了虚拟的红色球体上。这就是球体与线段相交的问题,这种问题已有惯用解决方案,但通常效率不高。如果需要更具分析性的方法,就需要自行解决线段与自定义几何体相交的问题。这种方案极大限制了可以创建的模型,所以很少被应用。

固定步长的立体光线追踪

上面提到纯分析式的立体射线投射,其实不适合解决这里的问题。如果希望模拟任意几何体,就要找到不依赖于相交方程的更为灵活的技术。常见的解决方案叫做立体光线追踪(Volumetric Raymarching),是基于迭代的解决方案。

立体光线追踪会缓慢地将射线投射到立方体内,每一步都会检测当前是否已投射到红色球体。



每条射线均从片段坐标worldPosition开始,然后迭代沿着viewDirection的方向投射STEP_SIZE单位长度。这可以通过每次迭代为worldPosition加上STEP_SIZE * viewDirection 来实现。

用下面的raymarchHit函数代替之前的raycastHit:

[C#] 纯文本查看 复制代码
#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是否位于球体内:
[C#] 纯文本查看 复制代码
bool sphereHit (float3 p)
{
    return distance(p,_Centre) < _Radius;
}

线段与球体相交很难,但迭代检测点是否位于球体内就很简单了。结果见下图,别看它看起来就是个圆形,实际上这就是个无光照的球体:



结论

本文介绍了基本的立体渲染概念。尽管传统着色器只能渲染材质的外壳,但还是有办法让光线穿透到材质内部的几何体,创造画面的深度。光线追踪就是最常用的技术,本文用该技术在立方体内绘制了一个红色球体。后面的教程将分享如何逼真地着色(第三篇表面着色)以及如何做出一些有趣的形状(第四篇 有向距离场)。最终将能够使用简单几行代码和一个立体渲染着色器实现如下效果:



本系列共有四篇教程,立体渲染是第一篇,后面将继续更新,请保持关注。

原文链接:http://www.alanzucconi.com/2016/07/01/volumetric-rendering/
原文作者:Alan Zucconi
感谢Unity官方翻译组成员“小七不乖”对本文翻译所做的贡献。
转载请注明来源:Unity官方中文社区 (forum.china.unity3d.com)。请勿私自更改任何版权说明信息。





本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

0

主题

4

帖子

230

贡献

初级UU族—3级

Rank: 3Rank: 3

积分
230
发表于 2016-9-20 02:58:36 | 显示全部楼层
虽然看的不是很懂,但是任然感觉很厉害!

0

主题

3

帖子

85

贡献

初级UU族—2级

Rank: 2

积分
85
发表于 2016-9-21 06:08:48 | 显示全部楼层
本帖最后由 Matrixfu 于 2016-9-21 06:11 编辑

同步测试已成功,期待续集!管理员加油啊
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表