首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何绘制三维模型的轮廓?

如何绘制三维模型的轮廓?
EN

Game Development用户
提问于 2014-01-05 18:33:45
回答 7查看 77.7K关注 0票数 56

如何绘制三维模型的轮廓?我指的是最近一款口袋妖怪游戏的效果,它的周围似乎有一个单像素的轮廓:

EN

回答 7

Game Development用户

发布于 2014-10-25 21:43:01

我不认为这里的任何其他答案都能达到“口袋妖怪X/Y”的效果。我不知道具体是怎么做的,但我想出了一种方法,看上去就像是他们在游戏中所做的。

在口袋妖怪X/Y中,轮廓是在轮廓边缘和其他非轮廓边缘绘制的(比如赖丘的耳朵在下面的截图中与他的头部相遇)。

看着搅拌机里的瑞秋网,你可以看到耳朵(上面橙色的突出显示)只是一个单独的、不相连的物体,它与头部相交,造成表面法线的突然变化。

在此基础上,我尝试根据法线生成大纲,这需要在两次传递中呈现:

第一关:渲染模型(纹理和细胞阴影)没有轮廓,并渲染相机空间法线到第二个渲染目标。

第二关:从第一关开始对法线进行全屏边缘检测.

下面的前两幅图像显示了第一次通过的输出。第三是大纲本身,最后是最终的综合结果。

这是我第二次使用的用于边缘检测的OpenGL片段着色器。这是我能想到的最好的方法,但可能有更好的方法。它可能也不是很好的优化。

代码语言:javascript
复制
// first render target from the first pass
uniform sampler2D uTexColor;
// second render target from the first pass
uniform sampler2D uTexNormals;

uniform vec2 uResolution;

in vec2 fsInUV;

out vec4 fsOut0;

void main(void)
{
  float dx = 1.0 / uResolution.x;
  float dy = 1.0 / uResolution.y;

  vec3 center = sampleNrm( uTexNormals, vec2(0.0, 0.0) );

  // sampling just these 3 neighboring fragments keeps the outline thin.
  vec3 top = sampleNrm( uTexNormals, vec2(0.0, dy) );
  vec3 topRight = sampleNrm( uTexNormals, vec2(dx, dy) );
  vec3 right = sampleNrm( uTexNormals, vec2(dx, 0.0) );

  // the rest is pretty arbitrary, but seemed to give me the
  // best-looking results for whatever reason.

  vec3 t = center - top;
  vec3 r = center - right;
  vec3 tr = center - topRight;

  t = abs( t );
  r = abs( r );
  tr = abs( tr );

  float n;
  n = max( n, t.x );
  n = max( n, t.y );
  n = max( n, t.z );
  n = max( n, r.x );
  n = max( n, r.y );
  n = max( n, r.z );
  n = max( n, tr.x );
  n = max( n, tr.y );
  n = max( n, tr.z );

  // threshold and scale.
  n = 1.0 - clamp( clamp((n * 2.0) - 0.8, 0.0, 1.0) * 1.5, 0.0, 1.0 );

  fsOut0.rgb = texture(uTexColor, fsInUV).rgb * (0.1 + 0.9*n);
}

在绘制第一遍之前,我将法线的渲染目标清除为一个面向摄像机的矢量:

代码语言:javascript
复制
glDrawBuffer( GL_COLOR_ATTACHMENT1 );
Vec3f clearVec( 0.0, 0.0, -1.0f );
// from normalized vector to rgb color; from [-1,1] to [0,1]
clearVec = (clearVec + Vec3f(1.0f, 1.0f, 1.0f)) * 0.5f;
glClearColor( clearVec.x, clearVec.y, clearVec.z, 0.0f );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

我在某个地方读到(我会在评论中添加一个链接),任天堂3DS使用的是固定功能的管道,而不是着色器,所以我想这不可能是游戏中的方式,但现在我确信我的方法已经足够接近了。

票数 37
EN

Game Development用户

发布于 2014-01-06 00:21:00

这种效果在游戏中特别常见,它使用的是细胞遮阳效果,但实际上是可以独立于细胞着色风格应用的东西。

您所描述的是所谓的“特征边缘渲染”,并且是在一般的过程中突出显示各种轮廓和模型的轮廓。关于这个问题,有许多技术和论文可供使用。

一个简单的技术是只渲染轮廓边缘,最外面的轮廓。这可以简单地用模具写呈现原始模型,然后在粗线框模式下重新呈现它,只有在没有模板值的情况下。看这里用于示例实现。

这将不会突出显示内部轮廓和折痕边缘,尽管(如您的图片所示)。通常,为了有效地做到这一点,您需要提取关于网格边缘的信息(基于边缘两侧人脸法线的不连续性),并建立一个表示每个边缘的数据结构。

然后,您可以编写着色器,以挤压或以其他方式呈现这些边缘,作为规则的几何超过你的基本模型(或与它一起)。使用边缘的位置和相对于视图向量的相邻面的法线来确定是否可以绘制特定的边缘。

你可以在网上找到更多的讨论,细节和论文,以及各种例子。例如:

票数 18
EN

Game Development用户

发布于 2014-01-06 02:03:30

对于光滑的模型(非常重要),这种效果相当简单。在你的片段/像素着色器中,你将需要被阴影的片段的法线。如果它非常接近垂直(dot(surface_normal,view_vector) <= .01 -你可能需要玩这个阈值),那么把碎片涂成黑色,而不是它通常的颜色。

这种方法“消耗”了一些模型来做概要。这可能是你想要的,也可能不是你想要的。很难从口袋妖怪的图片中看出这是在做什么。这取决于您是否希望将大纲包含在角色的任何轮廓中,或者是否希望将轮廓包含在轮廓中(这需要一种不同的技术)。

亮点将出现在表面的任何部分,从正面过渡到背面,包括“内部边缘”(就像绿色口袋妖怪或它的头上的腿--其他一些技术不会给它们增加任何轮廓)。

具有硬的、非光滑的边缘(如立方体)的对象将不会通过这种方法在所需的位置得到高亮显示。这意味着在某些情况下,这种方法根本不是一种选择;我不知道口袋妖怪模型是否光滑。

票数 9
EN
页面原文内容由Game Development提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://gamedev.stackexchange.com/questions/68401

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档