一种简易的卡通渲染方法(上)

《Introduction to 3D Game Programming with DirectX 9.0》的2.5节介绍了一种简易的卡通渲染方法:


卡通渲染是一种特定类型的非写实渲染(non-photorealistic rendering),有时被称作风格化渲染(
stylistic rendering)。

卡通渲染主要有两个特征:
1.明暗间过渡是不连贯,非平滑过渡。
2.轮廓边一般会被勾出。

要实现卡通着色,我们采用Lander在2000年3月发表在Game Developer Magazine的文章“Shades of Disney: Opaquing a 3D World”中所描述的方法。它像这样工作:我们创建一个带强度级别的灰度纹理,它包含我们需要的不同的着色强度。图2.3显示了我们在样例程序中使用的这个纹理。

CSDN_Dev_Image_2004-5-132029552.gif
图 2.3:用来保存着色强度的着色纹理。注意观察不连续的着色间过渡和纹理着色强度必须从左到右增加。

然后在顶点着色器中,我们执行标准散射点积运算(standard diffuse calculation dot product)来确定顶点法线N和光线向量L之间角度的余弦,用以确定顶点接收到多少光线:

s=L·N

如果s<0,就表示光线向量和顶点法线之间的角度大于90度,也就表示该表面接收不到光线。因此,如果s<0,我们就让s=0。所以s ∈ [0, 1]。

现在,在通常的散射光照模型中,我们使用s来标记颜色向量,这样顶点颜色就根据接收到的光照的量变暗:

diffuseColor = s(r, g, b, a)

但是,这将会导致从亮到暗之间着色的平滑过渡。这是与我们期望的卡通着色相反的。我们想要一个(对卡通渲染在两至四种着色间工作良好的)在一些不同着色间的不连续的过渡。

反其道而行之,不使用s来标记颜色向量,我们准备使用s作为早先提到的强度纹理的u纹理坐标——如图2.3。

注意:标量(scalar)s必定是一个有效的纹理坐标,因为s ∈ [0, 1],这是通常纹理坐标的区间。

按这种方式,顶点不会被平滑着色,而是不连续的。例如,强度纹理可能被分成3种着色。

注意:我们还为卡通着色关闭了纹理过滤,因为这种过滤会试图使着色过渡变平滑。这对于我们要求的不连续过渡是多余的。


下面是HLSL相关代码

None.gif //  File: toon.txt
None.gif
//  Desc: Vertex shader that lights geometry so it appears to be
None.gif
//        drawn in a cartoon style.
None.gif
None.gif
//
None.gif
//  Globals
None.gif
//
None.gif
extern  matrix WorldViewMatrix;
None.gif
extern  matrix WorldViewProjMatrix;
None.gif
extern  vector Color;
None.gif
extern  vector LightDirection;
ExpandedBlockStart.gifContractedBlock.gif
static  vector Black  =   dot.gif {0.0f0.0f0.0f0.0f} ;
None.gif
None.gif
//
None.gif
//  Structures
None.gif
//
ExpandedBlockStart.gifContractedBlock.gif
struct  VS_INPUT dot.gif {
InBlock.gif     vector position : POSITION;
InBlock.gif     vector normal   : NORMAL;
ExpandedBlockEnd.gif}
;
None.gif
ExpandedBlockStart.gifContractedBlock.gif
struct  VS_OUTPUT dot.gif {
InBlock.gif     vector position : POSITION;
InBlock.gif     float2 uvCoords : TEXCOORD;
InBlock.gif     vector diffuse  : COLOR;
ExpandedBlockEnd.gif}
;
None.gif
None.gif
//
None.gif
//  Main
None.gif
//
ExpandedBlockStart.gifContractedBlock.gif
VS_OUTPUT Main(VS_INPUT input) dot.gif {
InBlock.gif      
// zero out each member in output
InBlock.gif
      VS_OUTPUT output = (VS_OUTPUT)0;
InBlock.gif
InBlock.gif      
// transform vertex position to homogenous clip space
InBlock.gif
      output.position = mul(input.position, WorldViewProjMatrix);
InBlock.gif
InBlock.gif      
//
InBlock.gif      
// Transform lights and normals to view space.  Set w
InBlock.gif      
// components to zero since we're transforming vectors.
InBlock.gif      
// Assume there are no scalings in the world
InBlock.gif      
// matrix as well.
InBlock.gif      
//
InBlock.gif
      LightDirection.w = 0.0f;
InBlock.gif      input.normal.w   
= 0.0f;
InBlock.gif      LightDirection   
= mul(LightDirection, WorldViewMatrix);
InBlock.gif      input.normal     
= mul(input.normal, WorldViewMatrix);
InBlock.gif
InBlock.gif      
//
InBlock.gif      
// Compute the 1D texture coordinate for toon rendering.
InBlock.gif      
//
InBlock.gif
      float u = dot(LightDirection, input.normal);
InBlock.gif
InBlock.gif      
//
InBlock.gif      
// Clamp to zero if u is negative because u
InBlock.gif      
// negative implies the angle between the light
InBlock.gif      
// and normal is greater than 90 degrees.  And
InBlock.gif      
// if that is true then the surface receives
InBlock.gif      
// no light.
InBlock.gif      
//
InBlock.gif
      if(u < 0.0f)
InBlock.gif         u 
= 0.0f;
InBlock.gif
InBlock.gif      
//
InBlock.gif      
// Set other tex coord to middle.
InBlock.gif      
//
InBlock.gif
      float v = 0.5f;
InBlock.gif
InBlock.gif      output.uvCoords.x 
= u;
InBlock.gif      output.uvCoords.y 
= v;
InBlock.gif
InBlock.gif      
// save color
InBlock.gif
      output.diffuse = Color;
InBlock.gif      
InBlock.gif      
return output;
ExpandedBlockEnd.gif}

两点注解:
1 我们假设世界矩阵没有执行任何缩放。因为如果它执行,它就会弄乱乘以它的顶点的长度和方向。
2 我们总是设置v纹理坐标为纹理的中点。这意味着我们仅使用纹理中一条单一的线,那就是说我们可以使用1D强度纹理来代替2D的那个纹理。不管怎样,1D和2D纹理都能工作。本例中,我们使用了2D纹理而不是1D纹理,这是没有什么特别的原因的。

转载于:https://www.cnblogs.com/Pointer/archive/2004/08/19/34831.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值