Contents

  • 光滑表面的轮廓
  • 增加轮廓上的不透明度
  • 在shader中实现方程
  • shader 代码
  • 更多的艺术控制
  • 总结

本教程介绍了曲面法线向量的变换。
本教程的目的是实现一种效果:半透明对象的轮廓比该对象的其余部分更不透明。即使没有照明,这也增加了三维形状的印象。事实证明,转换法线对于获得此效果至关重要。

光滑表面的轮廓

在光滑表面的情况下,轮廓表面上的点可通过法线矢量来表示特征,该法线矢量平行于观察平面,因此正交于观察者的方向。在左图中,图顶部轮廓上的蓝色法线矢量平行于观察平面,而其他法线矢量指向观察者(或相机)的方向更多。通过计算指向观察者的方向和法线向量并测试他们是否(几乎)彼此正交,因此我们可以测试点是否(几乎)在轮廓上。
更具体的说,如果V是观察者的归一化方向,N是归一化的表面法线向量,如果点积结果为0:V·N=0,那么者两个向量是正交的。在实际中,这种情况是很少的。但是如果点积V·N结果接近0,我们可以假设该点接近轮廓。

增加轮廓上的不透明度

为了达到我们想要的效果,因此,当点积V·N结果接近0,我们应该增加不透明度α。有多种方法可以增加观察者方向和法线矢量点积很小的不透明度。这是其中之一:从material的常规不透明度的alpha中增加不透明度alpha‘:
*

α ′ = m i n ( 1 , α ∣ V ⋅ N ∣ ) α' = min(1,\frac{\alpha }{|V\cdot N|}) α′=min(1,∣V⋅N∣α​)

检查这样的方程的极端情况是有意义的。考虑一个接近轮廓的点的情况 V·N ≈ 0.在这种情况下,常规不透明度α将被一个小的正数除。(请注意,CPU通常会非常好的处理被0除的情况,因此,我们不必担心这一点)。因此,无论α的值是多少,α与小的正数的比例都会更大。取最小值函数min将使得所产生的不透明度不会大于1。
另一方面,对于远离轮廓的点,我们有V·N≈1。也就是说这些点的透明度不会发生太大的变化。这正是我们想要的。因此,我们刚刚检查了该方程至少是合理的。

在shader中实现方程

为了在着色器中实现类似α的方程,第一个问题应该是:应该在顶点着色器中实现还是在片元着色器中实现?在某些情况下,答案很明确,因为该实现需要纹理映射,而纹理映射通常仅在片段着色器中可用。但是,在许多情况下,没有通用的答案。顶点着色器的实现往往更快(因为顶点通常少于片段),但图像质量较低(因为法向矢量和其他顶点属性可能在顶点之间突然改变)。因此,如果您最关心性能,则在顶点着色器中实现可能是更好的选择。另一方面,如果您最关心图像质量,则在片段着色器中实施可能是更好的选择。
下一个问题是:方程应在哪个坐标系中实现?同样,也没有通用答案。 但是,在世界坐标中实现通常是Unity中的一个不错的选择,因为在世界坐标中指定了许多统一变量。
在实现方程式之前的最后一个问题是:我们从哪里得到方程式的参数?常规的不透明度alpha是通过一个shader的属性指定的。法线向量是标准顶点输入参数。观察者方向可以在顶点着色器中计算,可以看作是该顶点在世界坐标中的位置到世界空间中的相机位置(即_WorldSpaceCameraPos)的方向,该向量又Unity提供。
因此,在执行方程式之前,我们只需要将顶点位置和法线向量转换到世界空间即可。Unity提供了从对象空间到世界空间的变换矩阵unity_ObjectToWorld及其逆unity_WorldToObject。基本结果是通过将点和方向乘以变换矩阵即可对其进行变换。 将modelMatrix设置为unity_ObjectToWorld

 output.viewDir = normalize(_WorldSpaceCameraPos - mul(modelMatrix, input.vertex).xyz);

另外,通过将法线向量与转置的逆变换矩阵相乘来变换法向量。由于Unity为我们提供了逆变换矩阵(unity_WorldToObject),因此更好的选择是将法线向量从左侧乘以逆矩阵,这等效于将其从右侧乘以转置后的逆矩阵:

output.normal = normalize(mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);

现在,我们拥有编写着色器所需的所有内容。

shader 代码

Shader "Cg silhouette enhancement" {Properties {_Color ("Color", Color) = (1, 1, 1, 0.5) // user-specified RGBA color including opacity}SubShader {Tags { "Queue" = "Transparent" } // draw after all opaque geometry has been drawnPass { ZWrite Off // don't occlude other objectsBlend SrcAlpha OneMinusSrcAlpha // standard alpha blendingCGPROGRAM #pragma vertex vert  #pragma fragment frag #include "UnityCG.cginc"uniform float4 _Color; // define shader property for shadersstruct vertexInput {float4 vertex : POSITION;float3 normal : NORMAL;};struct vertexOutput {float4 pos : SV_POSITION;float3 normal : TEXCOORD0;float3 viewDir : TEXCOORD1;};vertexOutput vert(vertexInput input) {vertexOutput output;float4x4 modelMatrix = unity_ObjectToWorld;float4x4 modelMatrixInverse = unity_WorldToObject; output.normal = normalize(mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);output.viewDir = normalize(_WorldSpaceCameraPos - mul(modelMatrix, input.vertex).xyz);output.pos = UnityObjectToClipPos(input.vertex);return output;}float4 frag(vertexOutput input) : COLOR{float3 normalDirection = normalize(input.normal);float3 viewDirection = normalize(input.viewDir);float newOpacity = min(1.0, _Color.a / abs(dot(viewDirection, normalDirection)));return float4(_Color.rgb, newOpacity);}ENDCG}}
}

请注意,我们在顶点着色器中(因为我们希望在方向之间进行插值,而对每个方向都没有施加更多或更少的权重)和片元着色器的开始处(因为插值可以在一定程度上扭曲我们的归一化)归一化了顶点输出参数output.normaloutput.viewDir。但是,在许多情况下,不需要在顶点着色器中对output.normal进行归一化。 同样,在大多数情况下,片段着色器中的output.viewDir归一化也是不必要的。

更多的艺术控制

尽管所描述的轮廓增强是基于物理模型的,但是却缺乏艺术上的控制。即,CG艺术家无法轻易创建比物理模型建议的更细或更粗的轮廓。为了进行更多的艺术控制,您可以引入另一个(正)浮点数属性,并采用点积| V·N |。 在上面的公式中使用它之前,请使用此数字的幂(使用内置的Cg函数pow(float x,float y))。这将使CG艺术家可以独立于基色的不透明度创建更薄或更厚的轮廓。

总结

本节中,我们讨论了:

  • 如何找到光滑表面的轮廓(使用法线向量和视图方向的点积)
  • 如何增强这些轮廓的不透明度。( α ′ = m i n ( 1 , α ∣ V ⋅ N ∣ ) α' = min(1,\frac{\alpha }{|V\cdot N|}) α′=min(1,∣V⋅N∣α​))
  • 如何在着色器中实现方程式。
  • 如何将点和法线向量从对象空间转换为世界空间(对法向向量使用转置逆模型矩阵)。
  • 如何计算观察方向(从摄影机位置到顶点位置的差)
  • 如何插值归一化方向(即两次归一化:在顶点着色器和片段着色器中)。
  • 如何对轮廓的厚度提供更多的艺术控制。

Cg Programming In Unity Silhouette Enhancement(Wiki翻译自用)相关推荐

  1. Cg Programming In Unity Projection of Bumpy Surfaces

    Contents 改进法线贴图 视差映射解释 实现 完整的着色器代码 总结 本教程涵盖(单步)视差映射. 它扩展并基于"凹凸表面的照明"部分. 请注意,本教程旨在教您此技术的工作原 ...

  2. 《The C Programming Language》(2nd Ed) Introduction 翻译

    <The C Programming Language>(2nd Ed) Introduction 翻译 说明: 1.       本人非专业翻译人员,信达雅三种境界,可以达到" ...

  3. Functional Programming For The Rest of Us 翻译,重译 (剩人们的函数式编程)

    Functional Programming For The Rest of Us 翻译,重译 (剩人们的函数式编程) 原作者: Slava Akhmechet(译者注:这哥们不像marting fo ...

  4. Cg Programming/Unity/Specular Highlights镜面高光

    本教程涵盖了使用Phone反射模型的逐顶点光照(也叫做高洛德着色).它在章节"漫反射"中通过两个额外的术语扩展了着色器代码:环境光和镜面反射.这三个术语一起构成了Phone反射模型 ...

  5. Cg Programming/Unity/Lighting Textured Surfaces光照纹理表面

    本教程涵盖了纹理表面的逐顶点光照. 它结合了章节"纹理球体"和章节"镜面高光"的着色器代码,使用由一张贴图决定的漫反射材质颜色来计算光照.如果你没有读过那些章节 ...

  6. Cg Programming/Unity/Brushed Metal拉丝金属

    本教程涵盖了各向异性镜面高光. 这是几个关于光照教程中的其中一个教程,这个光照超出了Phone反射模型的范围.但是,它是基于章节"镜面高光"(逐顶点光照)以及章节"光滑镜 ...

  7. Cg Programming/Unity/Reflecting Surfaces反射表面

    本教程介绍了反射贴图(以及实现它的立方体贴图). 这是Unity中使用立方体贴图的环境映射的一系列小教程中的第一篇.本章基于章节"平滑镜面高光"中介绍的逐像素光照以及章节" ...

  8. 开源数据中心资产管理系统openDCIM 官方WIKI翻译

    为什么80%的码农都做不了架构师?>>>    对openDCIM 官方WIKI的翻译 客户端要求 不支持IE8或更早版本 IE9及其以后版本, Chrome, Mozilla, o ...

  9. Catlike Coding Unity教程系列 中文翻译 Basics篇(二)Building a Graph

    建立一个函数图像 可视化的数学 原文地址:https://catlikecoding.com/unity/tutorials/basics/building-a-graph/ 本次教程的主要内容: 创 ...

最新文章

  1. 1.3.8 excel for mysql_实时生成并下载大数据量的EXCEL文件,用PHP如何实现
  2. VMware-使用VMware在本地搭建多个CentOS虚机
  3. HDU5900 QSC and Master(区间DP + 最小费用最大流)
  4. JDK1.8源码(二)——java.lang.Integer 类
  5. leetcode 368. 最大整除子集(dp)
  6. HTCondor运行Java文件
  7. tshark筛选、过滤特定ip的数据包
  8. 【干货】2021年中国主要经济指标预测-中国科学院.pdf(附下载链接)
  9. django restframework Serializers
  10. css两张图片怎么合在一起_PhotoShop怎么把两张图片合成一张?怎么用ps把两张图片合成一张?...
  11. TensorFlow 变量共享: get_variable
  12. S7-1500 SD卡格式化
  13. hibernate二级缓存机制
  14. 织梦DEDE一键搬迁网站模板数据到DSCMS教程
  15. 乐高Mindstorms使用ev3dev进行编程
  16. 最新iOS面试题:APP性能优化(①系列更新)
  17. sdut - 英文金曲大赛
  18. 大数据分析引擎-Doris简要介绍
  19. 钉钉机器人关键词应答_Gmail 邮件推送到钉钉群机器人(详细)教程|国内接收 Gmail 邮件...
  20. 自然场景文字检测方案总结

热门文章

  1. 路由导航守卫和路由拦截
  2. JavaSwing_5.6: 系统托盘(System Tray)
  3. linux 硬盘相关命令,Linux 磁盘相关命令整理
  4. 云平台分为三种类型IaaS, PaaS和SaaS
  5. Sentinel使用教程
  6. 学习C++的笔记(算法与数据结构要求(2)(我不是黑瞎子掰棒子
  7. 基于冲突搜索的多机器人路径规划(Matlab代码实现)
  8. 斯坦福命名实体识别(Stanford Named Entity Recognizer)
  9. Github加速设定
  10. python由谁创造出来的_编程语言简史:都是谁在什么时候创造出来的呢?