失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > [Unity]Shader利用Geometry处理实现描边效果

[Unity]Shader利用Geometry处理实现描边效果

时间:2023-03-30 07:52:23

相关推荐

[Unity]Shader利用Geometry处理实现描边效果

1.原理

利用几何着色器,生成新的顶点,用新生成的顶点,构建描边,在用模板测试剔除原模型,如下图

灰色三角形是模型原来的一个三角面,用几何着色器,延顶点法线向外,创建出新的顶点A,B,C。已知一个面的构成需要三个顶点,依次连接顶点,总共创建了6个新的面,这六个面就构成灰色三角形的描边。再用模板测试的原理对模型进行Mask,得到描边。

2.代码

Properties{[HDR]_OutlineColor ("TintColor", Color) = (1,1,1,1)_OutlineWidth ("OutlineWidth", Range(0.001, 0.01)) = 0.001}

属性定义了描边的颜色,描边的宽度。

struct a2v {float4 vertex : POSITION; float3 normal : NORMAL;};

输入模型的顶点,法线。

struct v2gf {float4 g_vertex : TEXCOORD0;float3 g_normal : TEXCOORD1;float4 pos : SV_POSITION;};

输出顶点,法线,裁剪空间下的顶点。

v2gf vert(a2v v) {v2gf o;o.g_vertex = v.vertex;o.g_normal = v.normal;o.pos = UnityObjectToClipPos(v.vertex);return o;}

顶点和法线处理,直接传入模型空间下的顶点,后面在几何处理中,再进行空间变换。

v2gf Offset(v2gf o) {v2gf extruded = o;float3 vNormal = mul((float3x3)UNITY_MATRIX_IT_MV, o.g_normal.xyz);float2 vOffset = TransformViewToProjection(vNormal.xy);vOffset.xy = normalize(vOffset.xy);extruded.pos = UnityObjectToClipPos(o.g_vertex);extruded.pos.xy += vOffset.xy * extruded.pos.w * _OutlineWidth;return extruded;}

在齐次裁剪空间下对顶点延法线进行偏移,得到新的顶点坐标,顶点在齐次裁剪空间下,w的值反应了和相机的远近关系,故乘以extruded.pos.w,当相机拉远和拉近,有宽度的变化。

#define APPEND(o)\outputStream.Append(o);[maxvertexcount(18)]void geom(triangle v2gf inputTriangle[3], inout TriangleStream<v2gf> outputStream) {v2gf extrusionTriangle0 = Offset(inputTriangle[0]);v2gf extrusionTriangle1 = Offset(inputTriangle[1]);v2gf extrusionTriangle2 = Offset(inputTriangle[2]);APPEND(inputTriangle[0]);APPEND(extrusionTriangle0);APPEND(inputTriangle[1]);APPEND(extrusionTriangle0);APPEND(extrusionTriangle1);APPEND(inputTriangle[1]);APPEND(inputTriangle[1]);APPEND(extrusionTriangle1);APPEND(extrusionTriangle2);APPEND(inputTriangle[1]);APPEND(extrusionTriangle2);APPEND(inputTriangle[2]);APPEND(inputTriangle[2]);APPEND(extrusionTriangle2);APPEND(inputTriangle[0]);APPEND(extrusionTriangle2);APPEND(extrusionTriangle0);APPEND(inputTriangle[0]);}

由原理部分的图,我们需要输出新的6个面,以及6*3=18个顶点,来构成描边模型。前面先处理了新的顶点,通过输出流,写入新的顶点来构成三角面。

下面是完整代码,用了两个Pass,对模板测试进行配置。

Shader "Custom/Unlit/CustomSilhouette"{Properties{[HDR]_OutlineColor ("TintColor", Color) = (1,1,1,1)//_OutlineWidth ("OutlineWidth", Range(0.001, 0.01)) = 0.001}CGINCLUDE#pragma target 4.0#include "UnityCG.cginc"fixed4 _OutlineColor;float _OutlineWidth;struct a2v {float4 vertex : POSITION;float3 normal : NORMAL;};struct v2gf {float4 g_vertex : TEXCOORD0;float3 g_normal : TEXCOORD1;float4 pos : SV_POSITION;};v2gf vert(a2v v) {v2gf o;o.g_vertex = v.vertex;o.g_normal = v.normal;o.pos = UnityObjectToClipPos(v.vertex);return o;}v2gf Offset(v2gf o) {v2gf extruded = o;float3 vNormal = mul((float3x3)UNITY_MATRIX_IT_MV, o.g_normal.xyz);float2 vOffset = TransformViewToProjection(vNormal.xy);vOffset.xy = normalize(vOffset.xy);extruded.pos = UnityObjectToClipPos(o.g_vertex);extruded.pos.xy += vOffset.xy * extruded.pos.w * _OutlineWidth;return extruded;}#define APPEND(o)\outputStream.Append(o);[maxvertexcount(18)]void geom(triangle v2gf inputTriangle[3], inout TriangleStream<v2gf> outputStream) {v2gf extrusionTriangle0 = Offset(inputTriangle[0]);v2gf extrusionTriangle1 = Offset(inputTriangle[1]);v2gf extrusionTriangle2 = Offset(inputTriangle[2]);APPEND(inputTriangle[0]);APPEND(extrusionTriangle0);APPEND(inputTriangle[1]);APPEND(extrusionTriangle0);APPEND(extrusionTriangle1);APPEND(inputTriangle[1]);APPEND(inputTriangle[1]);APPEND(extrusionTriangle1);APPEND(extrusionTriangle2);APPEND(inputTriangle[1]);APPEND(extrusionTriangle2);APPEND(inputTriangle[2]);APPEND(inputTriangle[2]);APPEND(extrusionTriangle2);APPEND(inputTriangle[0]);APPEND(extrusionTriangle2);APPEND(extrusionTriangle0);APPEND(inputTriangle[0]); }fixed4 frag(v2gf o) : SV_Target{return _OutlineColor;}fixed4 Null(v2gf o) : SV_Target{return float4(1.0, 0.0, 1.0, 1.0);}ENDCGSubShader{Tags{"RenderType" = "Outline"}Pass{Tags {"LightMode" = "Always" }ColorMask 0//不输出颜色Cull OffZWrite Off//不进行深度写入。Stencil//模板测试总是通过,并且写入参考值1{Ref 1Comp alwaysPass replace}CGPROGRAM#pragma vertex vert#pragma fragment NullENDCG}Pass{Tags {"LightMode" = "Always" }Cull OffZWrite OnStencil//模板测试中参考值不等于1的像素将会通过,进行外轮廓描边。{Ref 1Comp notequalPass keepFail keep}CGPROGRAM#pragma vertex vert#pragma geometry geom#pragma fragment fragENDCG}}}

3.效果

加了Bloom效果,看着就非常高大上了,这个Shader适用于你不想修改原模型的着色效果,又想加入一个描边效果的情况,但骨骼动画就要考虑在原Shader上处理,在本例中可以把第一个Pass改成渲染模型纹理,关键代码如下:

fixed4 Null(v2gf o) : SV_Target{return tex2D(_MainTex, o.uv);//return float4(1.0, 0.0, 1.0, 1.0);}

Pass{Tags {"LightMode" = "Always" }//ColorMask 0Cull BackStencil{Ref 1Comp alwaysPass replace}CGPROGRAM#pragma vertex vert#pragma fragment NullENDCG}

或者传入骨骼动画后的模型数据进行同步,也是可以的。

如果觉得《[Unity]Shader利用Geometry处理实现描边效果》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。