用Unity实现Flat Shading

要实现平面着色,就要使每个三角面是平滑的,我们可以从像素的法线向量入手,让每个像素的法线都等于该三角面的法线,而不是来自于三个顶点法线的插值。为了求得平面法线,可以利用硬件提供的ddx和ddy函数,分别计算tangent和binormal向量,最后叉乘得到normal:

 float3 dpdx = ddx(i.worldPos);float3 dpdy = ddy(i.worldPos);i.normal = normalize(cross(dpdy, dpdx));

除此之外,我们还可以借助geometry shader,直接修改顶点的normal,再传入fragment shader:

[maxvertexcount(3)]
void MyGeometryProgram (triangle InterpolatorsVertex i[3],inout TriangleStream<InterpolatorsVertex> stream
) {float3 p0 = i[0].worldPos.xyz;float3 p1 = i[1].worldPos.xyz;float3 p2 = i[2].worldPos.xyz;float3 triangleNormal = normalize(cross(p1 - p0, p2 - p0));i[0].normal = triangleNormal;i[1].normal = triangleNormal;i[2].normal = triangleNormal;stream.Append(i[0]);stream.Append(i[1]);stream.Append(i[2]);
}

接下来,我们希望在平面着色的基础上为三角面加上线框。那么直观上来说,越靠近三角形边上的点需要进行额外的线框着色,而位于三角形内部的点则不受影响。这里可以借助三角形的重心坐标来进行判断,位于三角形边上的点,其重心坐标(x,y,z)中必定有一项为0。我们首先在geometry shader中添加重心坐标这一属性:

[maxvertexcount(3)]
void MyGeometryProgram (triangle InterpolatorsVertex i[3],inout TriangleStream<InterpolatorsVertex> stream
) {...i[0].barycentricCoordinates = float2(1, 0);i[1].barycentricCoordinates = float2(0, 1);i[2].barycentricCoordinates = float2(0, 0);...
}

这里只用了float2来表示重心坐标,是因为重心坐标有这样的性质:x+y+z=1。

有了重心坐标之后,我们就能利用它来绘制线框。首先,可以取重心坐标中的最小值,表示三角形中的点到边的最近距离,然后直接拿来计算颜色:

 float3 albedo = GetAlbedo(i);float3 barys;barys.xy = i.barycentricCoordinates;barys.z = 1 - barys.x - barys.y;float minBary = min(barys.x, min(barys.y, barys.z));return albedo * minBary;

雏形有了,但是黑色的地方太多了,原因是重心坐标中的最小值最大也就只有1/3,还得是在重心的位置。因此,需要把这个范围进行调整,我们可以使用smoothstep函数,只让距离很小的一部分点的着色变黑,然后平滑过渡到正常颜色:

 float3 albedo = GetAlbedo(i);float3 barys;barys.xy = i.barycentricCoordinates;barys.z = 1 - barys.x - barys.y;float minBary = min(barys.x, min(barys.y, barys.z));minBary = smoothstep(0, 0.1, minBary);return albedo * minBary;

看上去效果不错。不过美中不足的是,线框的宽度是各不相同的,这也很好理解,毕竟一个三角形所能覆盖的屏幕空间区域是不同的,覆盖面积大的三角形,重心坐标的变化就会慢一些,也就是经过多个像素才可能使重心坐标从0变化到0.1。那么,有办法统一三角形的线框宽度吗?这就需要我们把重心坐标的变化率考虑进来。变化率越小的,让线框变黑的阈值也应该越小。说到变化率,我们想起了之前提到的ddxddy函数:

 float3 albedo = GetAlbedo(i);float3 barys;barys.xy = i.barycentricCoordinates;barys.z = 1 - barys.x - barys.y;float minBary = min(barys.x, min(barys.y, barys.z));// 等价于 float delta = fwidth(minBary);float delta = abs(ddx(minBary)) + abs(ddy(minBary));minBary = smoothstep(0, delta, minBary);return albedo * minBary;

我们也可以进一步调节smoothstep的参数,来控制线框的宽度:

 minBary = smoothstep(delta, 2 * delta, minBary);

我们注意到,此时有些地方出现了很明显的锯齿。这是因为我们是先取的重心坐标的最小值,然后对其求导,但是这可能是不连续的。所以我们需要调整一下顺序,先分别对重心坐标的xyz求导:

 float3 albedo = GetAlbedo(i);float3 barys;barys.xy = i.barycentricCoordinates;barys.z = 1 - barys.x - barys.y;float3 deltas = fwidth(barys);barys = smoothstep(deltas, 2 * deltas, barys);float minBary = min(barys.x, min(barys.y, barys.z));return albedo * minBary;

效果拔群。那么最后,我们将一些可调节的参数暴露出来,方便我们随时调整表现:

 float3 albedo = GetAlbedo(i);float3 barys;barys.xy = i.barycentricCoordinates;barys.z = 1 - barys.x - barys.y;float3 deltas = fwidth(barys);float3 smoothing = deltas * _WireframeSmoothing;float3 thickness = deltas * _WireframeThickness;barys = smoothstep(thickness, thickness + smoothing, barys);float minBary = min(barys.x, min(barys.y, barys.z));return lerp(_WireframeColor, albedo, minBary);

这样我们就能够调整线框的颜色,过渡的平滑度,还有线框本身的宽度了:

如果你觉得我的文章有帮助,欢迎关注我的微信公众号:Game_Develop_Forever

Reference

[1] Flat and Wireframe Shading

用Unity实现Flat Shading相关推荐

  1. Unity Shader - ddx/ddy偏导函数测试,实现:锐化、高度图、Flat shading应用、高度生成法线

    文章目录 ddx, ddy 说明 DirectX - ddx, ddy OpenGL - dFdx, dFdy 伪代码表示 可用它来做什么 简单的边缘突出应用 Shader 边缘突出-锐化-增加差值 ...

  2. Q80:平坦着色(Flat Shading)和平滑着色(Smooth Shading)——“Q79:怎么用三角形网格(Triangle Mesh)细分曲面”(补充)

    80.1 概述 前面用三角形网格细分球面时,对单个三角形的着色方式采样的是"Flat Shading".即: 通过三角形三顶点的坐标计算出整个三角形的法向量. 这样就导致相邻两个三 ...

  3. 着色 Shading,漫反射,高光,环境光,Blinn-Phong 反射模型,Flat Shading,Gouraud Shading,图形管线 Graphics Pipeline渲染总结

    着色 Shading shading:The darkening or coloring of an illustration or diagram with parrel lines or a bl ...

  4. Unity Flat Shading实现低多边形(low-poly)效果

    首先要确保模型是低模,如果模型精度较高只通过这样改变mesh的顶点三角索引效果就不明显 获取模型的mesh网格,非蒙皮模型通过获取MeshFilter组件得到Mesh,带有蒙皮的模型通过Skinned ...

  5. DCC - Photoshop - Nvidia NormalMapFilter - 法线生成工具 - 顺便测试 Unity URP 12.1 中的 Decal System

    文章目录 NVIDIA Texture Tools Exporter 下载.安装 法线生成素材图 扣干净无用像素 使用 NVIDIA Normal Map Filter 生成贴图 配置好 URP Re ...

  6. 【Unity Shader】新书封面 — Low Polygon风格的渲染

    写在前面 最近又开心又担心,因为我的书马上就要上市了,开心当然是因为等了这么久终于可以如愿了,担心是因为不少人对它的期待都很大,我第一次写书,能力也有限,不知道能不能让大家满意,让大家也都喜欢上它.不 ...

  7. 图形学/OpenGL/3D数学/Unity

    1. 场景管理的数据结构: 总结,游戏开发最常用的空间数据结构是四叉/八叉树和BVH树,而BSP树基本上只能应用于编辑器上,k-d树则只能用在特殊问题应用场景. 2. 帧同步与状态同步: https: ...

  8. unity标准着色器入门-材质参数(二)

    金属模式:金属参数 当在金属工作流(不同于镜面工作流)下工作时,表面的反射成都和光照反应由金属性级别和平滑度级别来修改. 当使用这个工作流时,镜面反射仍然被生成:但依赖于给予Metallic和Smoo ...

  9. 关于matlab中pcolor显示图片时的shading设置问题

    在用pcolor进行显示图片时,要调用colormap,caxis,shading进行设置,才有可能还原图片本来的色彩 pcolor的用法是: pcolor(N) 其中N是一个矩阵,矩阵的行列数表示图 ...

最新文章

  1. 【组队学习】【26期】图神经网络
  2. JEESZ架构、分布式服务:Dubbo+Zookeeper+Proxy+Restful
  3. mysql if exist坑
  4. 专业软件测试面试题汇总
  5. 【问链-EOS公开课】第十三课 EOS插件机制深入解析
  6. macos 此服务器的证书无效_跨平台本地SSL证书生成工具,本地也能优雅的调试https...
  7. resnet结构_经典卷积网络(二)-ResNet
  8. H5项目常见问题汇总及解决方案
  9. wow 私服trinitycore
  10. python将对象放入列表_将所有python-rom对象放入列表
  11. 【每日算法Day 102】美团 AI 平台算法工程师面试编程题
  12. [Tyvj 模拟赛] 运
  13. JSP内置对象-out对象
  14. Python数据挖掘入门与实践-OneR分类算法
  15. 百城百店 宝瓷林.北京SKP商场品牌店开业
  16. Premiere Pro教程
  17. Realsense D435i +Opencv 获取彩色、深度、IMU数据并对齐
  18. 【完整的WebGIS教程】7.1 ArcGIS API for JS行政区划导航(上)
  19. 【嵌入式开发】向开发板中烧写Linux系统-型号S3C6410
  20. 假设某专业有若干个班,每个班有若干学生,每个学生包含姓名和分数,这样构成一棵树,如图1所示。假设树中每个结点的name域均不相同,该树采用孩子兄弟链存储结构,其结点类型定义如下:

热门文章

  1. Presto Cannot write to non-managed Hive table
  2. 两年数据对比柱形图_同期数据对比,你会做这样特殊的柱形图吗?趋势、差异值一目了然...
  3. Spring Boot Admin配置安全验证
  4. @SuppressWarnings(deprecation) java编程中方法上有这个注释是什么意思??
  5. 力扣 777. 在LR字符串中交换相邻字符
  6. 嵌入式软件开发之常用软件(六)
  7. 怎么计算机械连接的工程量,传力杆套筒工程量怎么算
  8. 微信账户体系科普:什么是UnionId、OpenId与wxopenid?
  9. Asp 操作Access数据库时出现死锁.ldb的解决方法
  10. 二维码门禁(基于微信小程序)