我们提出了一个使用多边形模型的实时毛发渲染算法,并且将其应用于今年 SIGGRAPH 动画节上的一个实时动画《 ruby The Double Cross 》上面。该毛发渲染算法是基于 Kajiya-Kay 毛发渲染模型的算法,但是在其之上添加了一个实时的接近现实高光的镜面反射效果( pancy: 原始算法可能没有考虑到头发的高光只计算了漫反射或者只是简单的模拟了镜面反射 )。这个部分的工作主要由由 Marschner 等人来实现。此外,我们还定义了一个简明的渲染过程 ( pancy: 图形引擎里面一般将一个渲染效果称作一个 tecnique ,暂且翻译为渲染过程 ) ,该过程将告诉大家如何近似达到 back-to-front 排序的效果来实现多层半透明毛发的渲染。( pancy: 模拟 back-to-front 排序是 ATI 解决多层半透明渲染的重要 GPU 算法 , 在这篇论文下面会详细介绍 ),在以往的算法中,多层半透明材质的排序需要在每一帧里面依靠 cpu 来做,在我们的算法中这个方法将被摒弃,我们将依靠在 gpu 上进行多次渲染来做到对多层半透明毛发的排序。 ( pancy: 这里解释一下为什么之前要对头发进行排序,半透明效果是通过 alpha 混合来产生的,但是 alpha 混合的缺点就是需要进行 back-to-front 排序,也就是说半透明是相对的,相对于在其之前绘制的物体才是半透明,在他之后绘制的东西则会完全盖住它。而头发模型只有一个,除非建模的时候分开否则就要由 cpu 来分层决定谁先绘制谁后绘制达到完美的半透明效果 )

毛发模型:

我们算法所需要输入的模型是一种由很多层的 2 维多边形所模拟的毛发,这种多层模拟既很容易模拟头发细节,又可以凑成一套几何体。这种多边形模型非常容易进行渲染,因为他的顶点信息较少而能够降低 vertex shader 的载荷,并且其简化了多边形毛发的几何形状,使其易于进行 back-to-front 渲染。

该模型有两种最主要的纹理,其一是用于映射精细结构的映射,另一个是一组用于修补多层毛发之间不协调的不透明纹理。 ( pancy: 毛发之所以需要半透明效果主要是由于毛发的发梢部分是逐渐渐透明的,这个效果是 alpha-test 不能代替的,但是多层毛发之间的不协调修补则不涉及发梢,可以作为完全不透明的材质 )

毛发渲染:

1 :漫反射部分:

我们使用了 N-L 偏移以及一个缩放来模拟漫反射光既: diffuse = max(0,0.75N*L + 0.25)( pancy: 我们通常在计算漫反射光的时候是使用 N*L 作为漫反射角度的,而这篇论文通过上述公式,将头发的漫反射系数压缩到了一个比较小的区域,所计算的漫反射会比通常的计算效果色差更小,亮度更亮,不过大家可以根据自己模型的发质情况选择性的使用这个公式 ) 这种计算漫反射的方法让头发的对光部分更加明亮,并且为整个头发创建了一个柔和的外观。

2 :镜面反射高光:

我们用于模拟镜面反射光的基本方法是沿用了 Kajiya-Kay 的着色模型,但是我们有更为新颖的做法。 Marschner 等人指出头发应该有两种可以被肉眼辨认的镜面反射高光,其一便是头发表面直接所反射的光线,并且这部分光线会集中于头发的顶端。其二便是光线进入到很多头发组成的发束中,经过多次反射进入到观察者眼中 ( pancy: 这个过程类似于环境光模拟要解决的问题,只不过是严格的高光经过了多次反射,如果要严格模拟必然会涉及到 ray-tracing ,不过这里既没有严格的头发模型,也不可能在实时渲染算法中出现 ray-tracing ,所以应该会根据这种光线的特点弄一些近似的算法 ) 这种突出的高光颜色取决于头发的本身发色,并且它总是朝着头发的根部,并且外观也很杂乱。

为了模拟 Marschner 等人发现的这一视觉效果,我们在每次渲染的时候会输入两个不同的镜面发射光源,这两个镜面反射光的颜色不同,镜面反射指数不同,并且,我们会根据头发丝的方向将这两个镜面反射光转换到相反的两个方向 ( pancy: 大概就是一个顺着头发,一个逆着头发丝吧,这里就是为了模拟上一段提到的两种不同的镜面反射光 ) ,对于第二种镜面反射光,我们会通过一个噪声纹理来两次对其高光进行调整,这样就可以以极小的代价来实现类似于真实头发的闪烁外观 ( pancy:gpu shader 没有很多内置函数,例如产生随机数,因此涉及到随机算法一般会传给 shader 一张由 cpu 产生的纹理来实现这个效果 ) ,为了得到两个沿着头发方向相反的镜面反射光方向,我们可以重新构造头发的表面切向量,通过使用用于各个层头发片的切向量以及头发片的法向量: T ’ = normalize(T+s*N), 其中 s 代表修改参数,根据这个参数取正或取负的不同,我们可以分别得到沿着头发指向发根,以及沿着头发指向发梢这两个不同的方向。这里为了修缮细节我们不会对一个头发片决定 s ,而是从纹理上来获得,这种方法类似于我们常用的法线贴图,只不过这里 s 是寻找头发的切线细节,所以我们称之为“切线贴图”

( pancy: 正如我们之前所说,第二种镜面光想法不错但是实现难度很大,只能是近似模拟,这里他提到了用切线贴图,我解释一下这个名词,如果对于每一层的头发只使用一个切线,也就是那一层的多边形片所在平面切线,那么计算出来的高光就不是针对每个头发的,而是对那个多边形片的,因此他们想到了现在用于记录法线细节的法线贴图算法,也就是把头发的细节记录在一张图片上,当然只是切线细节而不是所有的细节。然后回头用的时候再根据 uv 坐标还原回去,这样就可以从视觉角度上还原一部分头发信息,当然这种方法是有缺陷的,比如不能平视等,顺便吐槽一下,如今曲面细分技术已经可以在大部分家用机器上实现了,所以这个算法应该可以再进行很大的改进吧例如切线位移贴图神马的云云 ..... 当然只是吐槽,人家 ATI 或者 NVIDA 肯定有更好的方法渲染毛发了,今非昔比 )

3 环境光遮蔽部分:

我们为了简要的表示头发的自阴影,在程序的预处理阶段我们为每个顶点计算了一次环境光遮蔽,在最终的像素处理中,我们将像素的漫反射 + 镜面发射 +ao 的颜色综合作为像素输出。 ( pancy: 这里基本上相当于啥都没说 ^O^, 环境光遮蔽算法我说一下,这里基本上可以认为他们用的是 SSAO 了,环境光遮蔽主要是为了模拟环境光,也就是间接反射光线,跟上面的第二种镜面反射光差不多。不过真要准确模拟代价很大,因此就提出了 计算遮蔽 这种近似的算法。根据一个顶点前面没有完全遮挡住他的点来计算出一个环境光遮蔽值,如论文所述,对于毛发渲染来说这个算法也就是主要模拟头发自己遮蔽自己所造成的阴影 )

近似深度排序渲染:

为了实现头发正确的半透明混合,我们必须还原头发模型之前的各个层面的深度顺序。我们在建模的时候就要先进行一遍处理,将个层头发根据它们离头部的距离进行排序,然后根据这个顺序记录将索引存放于静态的索引缓冲当中,然后再渲染的过程中,我们使用 4 pass 来描述我们的着色模型:

Pass1

1 ,激活 alpha-test ,只允许不透明像素通过测试。

2 ,关闭背面消隐

3 ,激活深度缓冲区,设置其为 less

4 ,关闭颜色缓冲区的写操作

这一步的 pixelshader 只返回 alpha 信息

Pass2

1, 关闭背面消隐

2, 激活深度缓冲区 , 设置为 equal

Pass3

1 ,开启背面消隐,消隐掉正面

2 ,关闭 z 缓冲区

Pass4

1 ,开启背面消隐,消隐掉背面

2 ,打开 z 缓冲区,设置其为 less

( pancy:ATI 著名的 4-pass 半透明渲染过程,大致思想就是前两个 pass 找到不透明的部分,把他们先绘制了,避免了透明部分遮住不透明部分这种尴尬的现象。然后再去正反两面绘制半透明部分 , 这样就能完美的绘制出半透明发梢,并且巧妙的避开了之前多层头发之间的互相遮挡所需要的多层排序问题 )

提前的深度剔除 ( early-Z culling )

我们之所以在第一个 pass 开启 z 缓冲区,因为激活 alpha-test 会让我们无法开启提前的 z 消隐 (early-Z culling) ,我们提前将 z 缓冲区记录,然后使用通过 z 缓冲的像素来执行 pixel shader 是一种效率很高的做法。余下的三个 pass 都会由于这个提前的 z 缓冲区记录获得性能上的收益。 ( pancy:early-Z culling 是指要求深度测试早于 pixelshader ,通常来讲,我们的 z 测试,模板测试等测试都是在 pixelshader 之后的,而将其提前的话在某些算法里面可以消隐掉很多不该出现的面,节省很多的资源,比如说这里的多层半透明毛发渲染 )

我们的头发渲染方案最大的优点就是不需要在运行的每一帧都用 cpu 对头发进行空间上的排序,但是我们的算法只能对头发模型进行一些“温和”的动画,也就是说不能把头发片移动的过于相关联 ( pancy: 大概就是头发不能完全按照物理模型相关联的受力,比如这片撞到了那一片然后一起运动一类的 ) 。如果不符合这个假设的话。暂时还是只能依靠 cpu 基础的运行时逐帧毛发排序

一个实用的实时毛发渲染及着色方法Thorsten scheuermannATI Resarch,Inc.翻译:潘曦(译文里的(pancy:XXX)为译者注) 介绍:我们提出了一个使用多边形模型的实时毛发渲染算法,并且将其应用于今年SIGGRAPH动画节上的一个实时动画《ruby:The Double Cross》上面。该毛发渲染算法是基于Kajiya-Kay毛发
hdrpVersion 7.2.0 of the HDRP package is now out of Preview and verified for production. That means we guarantee stability, platform support and upgrade path. HDRP软件包的7.2.0版现已退出预览版,并已进行生产验证。 这意味着我们保证...
某天心血来潮,想搞搞词云这玩意,网上很多在线生成词云的网站,但是加载都比较慢,或者有水印,再或者是无水印却背景颜色不喜欢。心想,这玩意不难,应该有开源代码库可以使用的吧?网上一搜,虽然 方法 不多,但是也总有一些好使的,特意摘取huaairen一文,加以提取出使用Python、Java和JS生成词云的 方法 ,转载内容如下: 1. python生成词云图 简单的代码示例如下: # coding=utf-8 from word. 这两天在整理 小甜甜 的捏脸系统,顺带把前项目 半透明头发 渲染 异常的bug 修正了,效果如下: 其实,前项目的头发工作良好,只是有一点 半透明 渲染 顺序 的小瑕疵,但正如前文 一个 自阴影的Bug 所提到的:我在迁移shader的时候因为偷懒,把头发改成了 Surface Shader,从而带来了一些新的问题。 问题一:关于Surface Shader的alpha指令 // 图像预处理,例如灰度化、降噪 cvtColor(image, image, COLOR_BGR2GRAY); GaussianBlur(image, image, Size(5, 5), 0); // 对图像进行二值化处理 Mat binary; threshold(image, binary, 128, 255, THRESH_BINARY); // 对图像进行边缘检测 Mat edges; Canny(binary, edges, 100, 200); // 对检测到的边缘进行形态学处理,以消除噪声 Mat dilated; Mat element = getStructuringElement(MORPH_RECT, Size(3, 3)); dilate(edges, dilated, element); // 使用机器学习模型识别 毛发 缺陷 // ... // 绘制结果并显示 imshow("Result", dilated); waitKey(); return 0; 这份代码是对图像进行了一些预处理,然后对图像进行边缘检测,最后使用机器学习模型识别 毛发 缺陷。 这仅是一份代码框架,详细的代码实现可能需要根据具体情况进行调整。