Low Polygon风格的渲染

转自http://blog.csdn.net/candycat1992/article/details/51511433

原理

Low polygon风格的渲染也被称为flat shading。虽然把这篇文章归到Shader类别里,但其实是完全可以用非Shader的方法来解决的。下面两张图片,左边是我们不希望得到的结果,而右边是我们想要得到的效果。

这里写图片描述 这里写图片描述

做美术的同学都知道上面模型的区别就是“硬边”和“软边”的问题。左图里就是软边的效果,软边意味着相邻三角形之间共用顶点,这些被共用顶点的法线(蓝线)通常是根据毗连三角形的面法线加权平均计算得到的,如下图所示(图片来源):

这里写图片描述

由于共用顶点的存在,光栅化阶段的插值使得这些三角面片中间每个点的点法线都是有三个顶点的法线插值得到的,而这三个法线各不相同, 使得面片有平滑过渡的效果,从而形成软边。在其他风格的渲染中,模型大多都会使用软边,这不仅可以减少顶点数目提高性能,还能让渲染效果更加平滑。

但flat shading显然不想要平滑的渲染效果,我们希望就算光栅化插值后同一个三角面片中每个点的法线都应该是相同的(都等于面法线),只有这样它们光照计算的结果才会是相同的。如下图所示(图片来源):

这里写图片描述

要实现这样的效果就是把所有相连的边在建模软件中设置成硬边,Hard Edge。这样软件背后就会进行拆顶点的工作,每个三角形有各自属于自己的三个顶点,不与他人共用,这三个顶点的点法线计算不会受毗连三角形的影响,而仅仅是由该三角面片的面法线决定。

因此,我们有了解决方案一:在建模软件中把边设置成硬边

直接使用Unity

值得高兴的是,就算你在建模软件里忘记了设置硬边,Unity也是可以直接帮我们做到软硬边的转换的。它的原理其实就是重新拆点、重新计算点法线。

这里写图片描述

在我们导入一个模型后,可以在模型的导入面板中的Normals & Tangents块中,把Normals设置成Calculate模式、把Smoothing Angle设置成0就可以得到硬边的效果。如上图所示。

这样的模型可以直接使用任何普通的Shader进行渲染,就会得到flat shading的效果。

程序产生的网格

之前群里有人问程序产生的网格怎么实现这种效果。原理也是一样的,我们只需要保证不要共用顶点、正确计算顶点法线即可。在我的NPR项目里,有这样一个例子,把任何模型强制转化成没有共用顶点的模型。其中关键代码是长这个样子的:

Vector3[] oldVerts = mesh.vertices;
Vector4[] oldTangents = mesh.tangents;
Vector2[] oldUVs = mesh.uv;
int[] triangles = mesh.triangles;
Vector3[] newVerts = new Vector3[triangles.Length];
Vector4[] newTangents = new Vector4[triangles.Length];
Vector2[] newUVs = new Vector2[triangles.Length];
for (int i = 0; i < triangles.Length; i++) {
    newVerts[i] = oldVerts[triangles[i]];
    newTangents[i] = oldTangents[triangles[i]];
    newUVs[i] = oldUVs[triangles[i]];
    triangles[i] = i;
}
mesh.vertices = newVerts;
mesh.tangents = newTangents;
mesh.triangles = triangles;
mesh.uv = newUVs;
mesh.RecalculateBounds();
mesh.RecalculateNormals();
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

因此,如果你是通过程序方法产生网格的,可以使用类似上面的代码来进行网格转换,再使用普通的Shader即可。

更复杂的方法:Geometry Shader

上面转化硬边的方法会增加顶点数目,有一种完全不需要对网格进行任何处理的方法就是使用Geometry Shader。这种方法的原理就是在光栅化前、在Geometry Shader里给每个顶点增加一个属性,面法线faceNormal。由于Geometry Shader中可以知道同一个三角面片中的所有三个顶点的信息,因此我们可以为它们计算一个相同的面法线值。这样,即便在经过光栅化插值后,同一个三角面片中的面法线也是一样的。关键代码如下:

[maxvertexcount(3)]
void geom(triangle v2g IN[3], inout TriangleStream<g2f> triStream) {
    float3 A = IN[1].worldPos.xyz - IN[0].worldPos.xyz;
    float3 B = IN[2].worldPos.xyz - IN[0].worldPos.xyz;
    float3 fn = normalize(cross(A, B));

    g2f o;
    o.pos = IN[0].pos;
    o.uv = IN[0].uv;
    o.worldPos = IN[0].worldPos;
    o.faceNormal = fn;
    triStream.Append(o);

    o.pos = IN[1].pos;
    o.uv = IN[1].uv;
    o.worldPos = IN[1].worldPos;
    o.faceNormal = fn;
    triStream.Append(o);

    o.pos = IN[2].pos;
    o.uv = IN[2].uv;
    o.worldPos = IN[2].worldPos;
    o.faceNormal = fn;
    triStream.Append(o);
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

这种方法有点小题大做,因为geometry shader是SM 4.0中的特性,移动终端大多达不到这样的要求。


参考链接:

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值