CSDN博客

img Nhsoft

Vertex Shader 支持的卡通描边渲染

发表于2004/6/23 1:33:00  5228人阅读

Vertex Shader 支持的卡通描边渲染

Cell Style Rendering use vertex shader

潘李亮 2003-10-4

Stanly Lee 2003-10-4

Email : xheartblue@etang.com

Homepage gamehunter.3322.net/xpertsoft/

通常情况下,我们见到3D场景都是按照真实的光照模型来照明渲染的,虽然目前实时的光照模型还只是对真实世界的一种近似。但是这种模型已经有了良好的可接受的真实感。

在人们想法提高场景渲染结果的同时,其他的非真实感渲染技术也是非常有用的,比较常见的就是卡通风格的渲染,这种渲染结果类似于我们常见的卡通画。本文将介绍使用Vertex shader来实现一种卡通风格的渲染。

1. 什么是卡通风格的渲染

首先我们要弄清楚什么是卡通风格的渲染结果,即我们需要得到什么样的效果。考察下图的茶壶渲染结果,我们得出以下的结论:

?ü?????????????????

图1,以卡通风格渲染的茶壶模型。

1、 要有比较明显边缘黑线,即明显的轮廓感觉。

2、 要有很急剧的明暗对比度,即颜色变化比较的快。

3、 考虑到视点变化的时候,我们也应该得到相同的效果。

2、解决方案。

对于上面提到的第3点需求,我们必须建立一个在视空间中的光照方向。这个光照的方向是不应该受到变换矩阵的影响的。同时这个光照方向应该和我们观察方向是一致的,只有这样才能得到如图一所示的明暗效果。

产生边缘轮廓线的方法有很多,最容易想到的就是绘制两遍场景,第一次按正常的光照模型来绘制,第二遍以线框的模式来绘制,同时将Z-Buffer的检测设置为相等的时候才通过。但是这样做的缺点是很明显的:首先你要绘制两遍场景,严重的影响了性能。其次是你在D3D里无法设置线的宽度,你只能得到比较单薄的轮廓。第二种方法是用观察方向(Camera的方向)去点乘转换后的三角形的法向量。点积的结果表示了一个三角形靠近边缘的程度,边缘必定是由一个正面朝向的三角形和一个背面朝向的三角形的公共边。如果一个三角形的法向量和Camera的方向的点积的绝对值越小则表示它离边缘越近,如果点积是负的话则表示它是背离了观察者。我们可以把靠近边缘的三角形渲染成黑色,这样就可以给物体制造一种很重的边缘效果。

?ü?????????????????

图2,光线与三角形法线夹角对光照强度的影响,纵坐标为光照强度,横坐标为夹角

最后是如何创建急剧的明暗变化问题,正常的光源方向和法线夹角对光照强度的影响如图2。正常的光照为蓝色的线条所示,通常它是条余弦曲线。为了达到我们需要的效果,我们希望的曲线是如上图的绿色所示的。角度小于一定数值时候,曲线较平坦,表示我们看到的大部分区域有较亮的着色效果,在经过一段急剧的变换以后,在接近90度的地方又变的很平坦(图2中的红色圆圈内的曲线),这是主要因为我们要制造一个比较浓重的边缘线条效果。

3,实现方法

在通常的做法中,要把视空间的法向量映射成亮度的话有两种方法。一种是事先做好一个一维纹理,纹理地址从0-256之间的值的变化是按照图2的亮度曲线定义的。一个顶点的纹理坐标是根据它的法向量来决定的,这需要我们给D3D/OpenGL来指定一定的纹理坐标自动生成算法。第二种方法是使用Cube Map。Cube Map是用一个向量(在这里是顶点的法向量)来索引一个纹理的像素。这种方法需要我们生成一个特殊的Cube纹理,而且也需要特定的自动纹理坐标生成方法。

以上两种方法都是用纹理来照明整个模型的,都需要占用一个纹理级别(Texture Stage)。而且也都不太直观(相对来说,第一种方法稍微直观一点)。

由于观察者的朝向(Camera的朝向)是一定的,因此我们只要能把法向量变换到视空间里,同时能在计算光照的时候,构造一条像图2中的曲线就可以实现需要的卡通渲染效果了,也就是说只要我们能控制T&L过程就可以了。这在现在的3D API和图形卡中都是很容易实现的。Vertex Shader提供了对这方面的全面支持。

4、 光照曲线的构造方法

由于Vertex Shader是Per-Vertex的形式处理的,我们不应该建立一个很复杂的计算代码。要达到图2所示的曲线最简单的方法就是给计算好的法线和Camera方向的点积乘上一个倍数,然后再减去一个数值。具体的曲线关系如图3,图中粉红色的为正常的光照模型曲线,我们先将其放大到一定的倍数到绿色曲线,然后再向下平移到蓝色曲线位置,由于3D API会将大于1的截断到1,小于0的归整到0。最终我们得到的光照曲线如红色的曲线所示。这种方法在D3D的VS2.0的版本中只要多增加一条指令(mad)就可以实现。但是由于D3D支持创建亮度大于1的光照,因此这种方法的渲染结果带有明显的金属亮泽。

VS2.0中有一条slt指令,这条指令可以根据两个操作数的大小的不同来选择是将目的操作寄存器设置为1还是0。我们可以用这条指令来做出如图4一样的曲线来。

?ü?????????????????

图3 图 4

5、 代码

本文不是一篇Vertex Shader的教程,所以我不会对如何使用以及什么是Vertex Shader花费过多的力气。本文的程序是用D3D实现的。我没有给顶点添加任何的数据。也就是D3D部分和普通的画Tearpot的程序没有任何的不同。因此我在这里只给出我的vertex shader的代码。

vs_2_0 // version instruction

dcl_position v0 // define position data in register v0

dcl_normal v4

def c15,1,1,1,0 //光的颜色

def c14,1.5,1.5,1.5,7 //一些在计算卡通效果时用到的常数

def c16,0,0,1,0 //定义眼睛的位置

def c17,0.5,2,0.3,0 //定义了图43条横线的位置

//大于c17.x的都放大到1c17.y则负责把c1.x放大到1,小于c17.z的都缩小为0

m4x4 oPos, v0, c0 //将点通过变换矩阵变换到投影空间

m3x4 r0,v4,c0 //将法向量变换到投影空间里来

nrm r1,r0 //单位化法向量

dp3 r1,r1,-c16 //计算正常的光照模型,将camera方向与法向量做点积

//=============以下的代码形成图4的曲线===========

//形成卡通渲染的关键步骤

slt r1.w,r1.x,c17.x

slt r1.z,-r1.x,-c17.x

slt r1.y,-r1.x,-c17.z

mul r1.x,r1.x,r1.w

mad r1.x,r1.x,c17.y,r1.z //将大于c17.x都放大到1,并将0-c17.x的放大到0-1的范围

mul r1.x,r1.x,r1.y //将小于c17.z的都变成0

//==============形成图3所示的卡通光照效果==================

//下面的代码也可以形成卡通效果,但是看上去更加的浓重Metal效果

//mad r1,r1,c14.w,-c14

//====施加卡通光照效果========

mul oD0 ,r1.x,c15

?ü?????????????????

使用 图4的曲线渲染结果

?ü?????????????????

使用图3曲线的渲染结果

阅读全文
0 0

相关文章推荐

img
取 消
img