先睹为快

引入

  这周学习了一种对模型的 Wireframe 做描边的着色器,其核心思想是:在几何着色器中,先将三维空间中每个顶点的坐标转换为屏幕坐标系下的平面坐标,然后求这个平面三角形的面积,继而求出各顶点到对边的高,并塞到一个另外两维用零补足的三维数组中。如此一来,经过几何着色器到片元着色器的插值后,这个三维数组中存的就变成了各像素点到临近三条边的距离。接下来只需让这三维中的任意一维小于某个值时画出 wireframe 的颜色,就完成了 wireframe 的描边。

  它的效果如下:

  它的几何着色器与片元着色器代码如下:

[maxvertexcount(3)]
void geom(triangle v2g IN[3], inout TriangleStream<g2f> triStream)
{float2 WIN_SCALE = float2(_ScreenParams.x / 2.0, _ScreenParams.y / 2.0);//frag positionfloat2 p0 = WIN_SCALE * IN[0].pos.xy / IN[0].pos.w;float2 p1 = WIN_SCALE * IN[1].pos.xy / IN[1].pos.w;float2 p2 = WIN_SCALE * IN[2].pos.xy / IN[2].pos.w;//barycentric positionfloat2 v0 = p2 - p1;float2 v1 = p2 - p0;float2 v2 = p1 - p0;//triangles areafloat area = abs(v1.x * v2.y - v1.y * v2.x);g2f OUT;OUT.pos = IN[0].pos;OUT.uv = IN[0].uv;OUT.dist = float3(area / length(v0),0,0);triStream.Append(OUT);OUT.pos = IN[1].pos;OUT.uv = IN[1].uv;OUT.dist = float3(0,area / length(v1),0);triStream.Append(OUT);OUT.pos = IN[2].pos;OUT.uv = IN[2].uv;OUT.dist = float3(0,0,area / length(v2));triStream.Append(OUT);}half4 frag(g2f IN) : COLOR
{//distance of frag from triangles centerfloat d = min(IN.dist.x, min(IN.dist.y, IN.dist.z));//fade based on dist from centerfloat I = exp2(-4.0 * d * d);return lerp(_Color, _WireColor, I);
}

进入正题

  对于 wireframe 描边这个命题,咱首先想到的是另一种解法。现在想来这其实算上面这种解法的青春版。

  既然从几何到片元的过程中会做插值,又正好要用到三个维度,那么直接把每个像素需要的信息存到 rgb 里岂不美哉?把每个面片的三个顶点设为红、绿、蓝,之后的插值就丢给着色器处理了。当然啦,本质也还是把 COLOR 当成三维数组来用,不过可视化的中间步骤更容易理解不是嘛。

  代码如下:

Properties
{_Color ("Color", Color) = (1,1,1,1)_FrameColor ("Wireframe Color", Color) = (1,0,0,1)_Epsilon ("Wireframe Width", Range(0, 0.34)) = 0.005
}
SubShader
{Tags { "RenderType"="Opaque" }Pass{CGPROGRAM#include "UnityCG.cginc"#pragma target 4.0#pragma vertex vert#pragma geometry geom#pragma fragment fragstatic const fixed4 COLOR[3] = {fixed4(1,0,0,1),fixed4(0,1,0,1),fixed4(0,0,1,1),};fixed4 _Color, _FrameColor;float _Epsilon;struct v2g{float4 vertex : POSITION;float2 uv : TEXCOORD0;float3 normal : NORMAL;};struct g2f{float4 vertex : SV_POSITION;float2 uv : TEXCOORD0;float3 normal : NORMAL;fixed4 color : COLOR;};v2g vert(appdata_base v){v2g o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = v.texcoord;o.normal = v.normal;return o;}[maxvertexcount(3)]void geom(triangle v2g v[3], inout TriangleStream<g2f> triStream){g2f o;for (uint i = 0; i < 3; i++){o.vertex = v[i].vertex;o.uv = v[i].uv;o.normal = v[i].normal;o.color = COLOR[i];triStream.Append(o);}triStream.RestartStrip();}fixed4 frag(g2f i) : COLOR{//Raw RBG Colorreturn i.color;}ENDCG}
}

  效果就是这样:

  显然地,这还没描边呢。所以我们再把片元着色器稍加修改,让它不要再显示闪瞎眼的 RGB 了:

fixed4 frag(g2f i) : COLOR
{//return i.color;float minColor = min(i.color.r, min(i.color.g, i.color.b));if (minColor < _Epsilon)return _FrameColor;elsereturn _Color;
}

  这几行的意思是,只要 rgb 中有一个分量小于设定好的 ε,就当描边线,否则就还是原本的颜色。效果如下:

  你或许发现了,虽然也描出了边,但该效果与开头提到的人家的效果有重要的差别。

  再放两张远景对比一下(均无光照)。这是我们的效果:

  这是人家的效果:

  出现这种立体感的差别,是因为我们的代码将描边画在了模型上,而人家的代码将描边画在了屏幕上。这意味着后者的线条在屏幕上永远是等粗的,所以在面片越密集的地方,线条占的份量也会越大。

  看起来好像是我们的效果更廉价喔?其实我们的效率是更高的,比人家少了好几次除法、幂运算啥的,或者说实际上我们除了做了几次比较,其他什么运算也没有。而且这种算法可以更自由地调整线条宽度,所以还是看个人的需求来选择啦。

扩展应用

  该算法不止能描 wireframe 边,还可以有以下多种变体效果。

  在上色时用最小的颜色分量作 weight 进行插值:

  在上色时用最大的颜色分量作 weight 进行插值:

  再加上描边:

Unity Shader 基于 RGB 插值的 Wireframe 描边着色器相关推荐

  1. unity Shader Lab(cg hlsl glsl)着色器入门教程 以及 vs2019 支持unity shader语法(更新中2019.9.5)

    前言: 如果你对cg glsl hlsl 顶点着色器 片段着色器 表面着色器 固定渲染管线 等等有所疑惑,或是想学会unity的渲染,看这一篇就足够了.另外我博客的shader分类中还有很多shade ...

  2. Unity Shader 学习笔记(一)关于“表面着色器”切换渲染管线Shader不可用的问题

    Shader主流上分为两类: 表面着色器(surface shader):更高级的封装,减少人工工作量,能实现大部分效果,缺点是自定义程度不 高,相对局限: 片段着色器(fragment shader ...

  3. [Shader] Shader Cookbook 创建你的第一个着色器[1]

      本章将涵盖一些在今天的游戏开发阴影管道中发现的更常见的扩散技术.让我们想象一个立方体,它被均匀地涂成白色,在一个有定向光的3D环境中.即使在每张脸上使用的颜色是相同的,它们也会有不同的白色深浅,这 ...

  4. Unity Shader 卡通渲染 实时模型动画描边的研究

    前言 卡通渲染也叫非真实感渲染(英文简写:NPR),"描边"在图形学和数字图像里都叫边缘检测.因此你可以在很多文献网站上面找到很多这类文献,但最后我发现基于图形学使用的方式基本都是 ...

  5. Shader攻占笔记(四)卡通着色器

    卡通着色器 卡通光照 描边 -- 全息描边 描边 -- 顶点膨胀描边 沿着法线 在顶点与法线之间抉择 高光改进 卡通光照 课本上介绍的卡通着色器有使用坡度图(ramp map)和直接程序截断两种方法. ...

  6. unity黑白滤镜_unity颜色分级图像滤镜着色器Fast Mobile Color Grading 1.0

    unity颜色分级图像滤镜着色器Fast Mobile Color Grading 1.0,包含7个着色器,可用于手机游戏或桌面游戏.大多数着色器都经过优化,可在移动设备上平滑运行,同时保持图像良好的 ...

  7. 【Unity Shader】学习顶点/片元着色器

    上一篇博客重点放在了Unity Shader的基本结构,分别介绍了它包含的三个语义块,最后简单介绍了Unity Shader的形式:表面着色器.顶点/片元着色器和固定函数着色器. 趁热打铁,今天接着上 ...

  8. 《Unity Shader入门精要》笔记02 第1章+第2章

    基础篇 第1章+第2章 --本系列是基于人民邮电出版社<Unity Shader入门精要>(冯乐乐著 )的自学Unity Shader笔记,如果您发现了本文的纰漏,还望不吝指正. 基础篇 ...

  9. 《Unity Shader入门精要》学习笔记

    简单说明 为控制渲染过程提供一层抽象,避免许多繁琐配置:用ShaderLab编写,使用一些嵌套在{}的语义: 定义了要显示材质所需的全部,不仅仅是着色器代码 Unity Shader != Shade ...

最新文章

  1. Linux进程间通信(二):信号集函数 sigemptyset()、sigprocmask()、sigpending()、sigsuspend()...
  2. 客户/服务器模型:编程思想
  3. lisp读写cass属性_130507Auto Lisp在CASS图形要素规范化中的应用
  4. 2020-12-15 CPU设计复盘
  5. ie8 ajaxSubmit 上传文件提示下载
  6. python的open函数百度百科,open函数
  7. Django----bootstrap导航栏的使用
  8. 回顾 2017, Java 有哪些值得关注的重大变化?
  9. SVN服务器搭建详解
  10. python程序的扩展名是perl程序的扩展名是_Python 程序扩展名(py, pyc, pyw, pyo, pyd)及发布程序时的选择...
  11. epoll编程实例客户端_深入底层探析网络编程之多路复用器(select,poll,epoll)
  12. 1.3 线性回归的sklearn实现
  13. 计算机磁盘修复工具,电脑自带chkdsk磁盘修复工具使用教程
  14. fiddler mac教程_fiddler for Mac
  15. PostgreSQL string_to_array函数应用
  16. QT获取HDMI视频采集数据
  17. html计算梯形的面积,数学教案计算梯形的面积
  18. 阿里云网盘内测申请_最新阿里云网盘官方申请地址,哪里可以获得阿里网盘内测码?9月23日截至...
  19. 松下P2卡数据恢复-删除/格式化100%完美恢复-解决方案
  20. python基础篇:字符画生成~甜心教主

热门文章

  1. 面试最常问的设计模式
  2. 数字化时代的市场营销
  3. 带翻转特效的会员登录注册html页面源码
  4. springboot框架下利用websocket实现即时通讯
  5. 详谈!抖音蓝V认证的常见问题总结
  6. Java50道经典编程题:(十三)数字谜题 ——循环语句及判断符的使用
  7. SQL SERVER 服务器登录名、数据库用户名、服务器角色、数据库角色、DENY、sp_addlogin ETC.
  8. HTML+CSS+JavaScript+Ajax+ECharts实现疫情实时监控大屏-2设计与实现
  9. ArcServer 9.3 ecp、 ArcSDE 9.3 ecp、 ArcIMS 9.3 ecp、 ArcEngine 9.3 ecp
  10. 利用PHP语言开发手机app后台服务器的框架是什么?或者说开发流程是怎么样的?