卡通渲染有很多样式,这里仅介绍轮廓中最常用的线框渲染。
我们先判断网格中那些边是轮廓边那些不是,将是轮廓边的渲染出来就得到效果了。究竟那些边才是轮廓边那?——如果与边相邻的两个面相对于视线的方向有不同的方向,也就说是如果一个面向观察者前面而另一个面是面向后面,那么它就是轮廓边。下面的图表示了轮廓边和非轮廓边:

一条边是由两个顶点组成的,为了计算边是否是轮廓边,我们必须为其两个顶点的定义中加入两个法线向量,再送到顶点渲染中处理,(对于输入的数据VS并不知道那些是顶点那些是法线信息,只有输出的数据才是有意义的。这里我们选用VS1.0,显卡会进行高速计算,即使显卡不支持CPU也能快速模拟)
自定义顶点类型如下: Type CUSTOMVERTEX postion As D3DVECTOR normal1 As D3DVECTOR normal2 As D3DVECTOR End Type
我们定义两个额外的法线向量,Normal1和Normal2。这两个向量表示共用这个边的两个面(face0和face1)的法线。
下面是验证的数学方程。假设我们在view空间里,令v为从原点到我们要测试的顶点的向量--如上图,n0为面face0的法线,n1为面face1的法线。那么满足下面的不等式的顶点就是轮廓:
(v*n1)(v*n2)<0
现在还有两个问题,如果线段在边界或在棱角上,绘图上通常也是将它画出的,如下图:

我们总是让这两种边为可见轮廓边。
边界的判断方法:只有一个三角形与其相邻
棱边的判断方法:如果它的2个法线点积小于一阀值(-1到1之间),我们就可认为它算是一条棱边。
为了让VS处理,令Normal2=-Normal1,这样,不等式总是为真
寻找轮廓边可以这样:
for I=0 to 网格面数
计算当前面的法线
normal1=当前面的法线
Normal2=-Normal1
for J=0 to 网格三角面数
if 第1条边有与其相邻的三角面(除本身) then
if 与其相邻面的夹角<阀值 then normal2=相邻面的法线
endif
next
处理第2条边......
处理第3条边......
next
Vertex Shader定义:
Dim shaderCode As D3DXBuffer decl(0) = D3DVSD_STREAM(0) decl(1) = D3DVSD_REG(0, D3DVSDT_FLOAT3) 'v0输入寄存器为顶点位置,三位浮点数 decl(2) = D3DVSD_REG(3, D3DVSDT_FLOAT3) 'v3输入寄存器为法线向量1,三位浮点数 decl(3) = D3DVSD_REG(6, D3DVSDT_FLOAT3) 'v6输入寄存器为法线向量2,三位浮点数 decl(4) = D3DVSD_END()
'创建顶点渲染
device.CreateVertexShader decl(0), shaderArray(0), shader, D3DUSAGE_SOFTWAREPROCESSING device.SetVertexShader shader '设置顶点渲染
...
D3DXMatrixMultiply m, matWorld, matView D3DXMatrixTranspose m, m device.SetVertexShaderConstant 1, m, 3 'c3常量寄存器为world * view 来计算view空间里的法线 D3DXMatrixMultiply m, matWorld, matView D3DXMatrixMultiply m, m, matProj D3DXMatrixTranspose m, m device.SetVertexShaderConstant 5, m, 4'c5常量寄存器为world * view * proj registroC(0) = 0 registroC(1) = 0.5 registroC(2) = 0 registroC(3) = 0 device.SetVertexShaderConstant 0, registroC(0), 1 'c0常量寄存器为一些常量 registroC(0) = -0.4 registroC(1) = 0.1 registroC(2) = -0.5 registroC(3) = -0.0001 device.SetVertexShaderConstant 10, registroC(0), 1 'c10常量寄存器为灯光方向和Z偏差 With cubo.mateX.diffuse coloreM(0) = .r: coloreM(1) = .g: coloreM(2) = .B: coloreM(3) = 0 device.SetVertexShaderConstant 9, coloreM(0), 1 'c9常量寄存器为颜色
End With
'设置只有ALPHA值小于等于0时才向屏幕输出:
device.SetRenderState D3DRS_ALPHATESTENABLE, True device.SetRenderState D3DRS_ALPHAFUNC, D3DCMP_LESSEQUAL device.SetRenderState D3DRS_ALPHAREF, (0)
device.DrawIndexedPrimitive D3DPT_TRIANGLELIST, ......
VS代码(shver2.txt):
; Inputs: v0 = Positi ; v3 = Normali1 ; v6 = Normali2 ; c0 = Costanti utili (0,0.5,xxx,xxx) ; c1-4 = WorldView matrix ; c5-9 = WorldViewProjection matrix
vs.1.0 ; 版本声明
m4x4 r6, v0 , c5 add r6.z,r6.z,c10.w ; 设置Z偏差 mov oPos,r6 ; 输出顶点位置
m4x4 r0 , v0 , c1 ; r0 = view空间的位置 m3x3 r1 , v3 , c1 ; r1 = view空间的法线1 m3x3 r4 , v6 , c1 ; r6 = view空间的法线2 dp3 r3.x , r0 , r1 ; 计算v*n1 dp3 r5.x , r0 , r4 ; 计算v*n2 dp3 r3.x , r3.x, r5.x ; 计算(v*n1)(v*n2) max r3.x ,r3.x , c0.x ; 将 r3.x 限制到0,(这样所有满足(v*n1)(v*n2)<0会成为0) mov oD0.w , r3.x ; 输出ALPHA值 mov oD0.xyz , c0.x ; 输出反射光颜色(0,黑色) 最后可以看一下代码,本人未作多少优化,加之VB本身原因所以速度较慢,但能处理材质。你可根据情况随意修改。
档案下载 |