您现在的位置是:主页 > news > 企业网站优化服务主要围绕哪些要素/2023b站推广大全
企业网站优化服务主要围绕哪些要素/2023b站推广大全
admin2025/5/16 23:45:55【news】
简介企业网站优化服务主要围绕哪些要素,2023b站推广大全,网站建设公司多少钱,两个 wordpress 合并前段时间美术想要一些角色描边的效果,一开始做了外描边,但是美术想要身体内也有描边效果 类似这种描边,如果用外描边是没办法实现的。 这时候就需要能描述内描边的算法。 从实现来看,包括内描边的算法可以做的方式包括ÿ…
前段时间美术想要一些角色描边的效果,一开始做了外描边,但是美术想要身体内也有描边效果
类似这种描边,如果用外描边是没办法实现的。
这时候就需要能描述内描边的算法。
从实现来看,包括内描边的算法可以做的方式包括:
1.身体分开描边:
也就是衣服一次外描边,身体一次外描边,头发一次外描边。。。。
好处:容易操作,而且效果可以控制。
坏处:drawcall会多很多,相当于每个配件都多一个dc,相当于多了一倍的dc。如果角色多,那就很恐怖了。。。
2.算法描边:
(1)卷积描边:
这种算法是用了边缘检测的方式来实现的,边缘检测的目的是识别数字图像中亮度变化明显的点。
一般的卷积有Sobel, Laplacian, Canny filter等。
Sobel Kernel通常采用两个3x3的矩阵来表示。由于Sobel滤波是检测一个方向上(一阶)的色彩或者亮度的梯度变化,所以需要在水平和垂直方向进行两次卷积。其两个卷积核(Sobel算子 - Sobel Operator)分别如下:
我们分别获得水平和垂直的结果后将其相加
,这样通过判断G是否大于一个阈值,我们就能判断这个像素是否属于边缘。
另一方面,Laplacian算子是一个二阶的微分算子,所以只需一个卷积核就能够检测到边缘,其缺点是对于噪声(Noise)比较敏感,如果图像中有比较多的噪声可能处理出来的结果会有很多噪点。
传统的实现方式就不说了,网上有很多相关的算法。
因为他们都是3x3的矩阵,所以我们运算时需要9次采样。如果我们运行在手机上会法线卡顿明显(gpu运算量加大)
那么如果我们想要一个折中的算法,而且又能体现出内描边的效果,我就放弃了3x3的矩阵,用2x2的矩阵来实现。
卷积计算这里有个特点:9个数值相加后为0,这样才能保证我们的权重总值时1。
那么基于这个特点,我们用2x2的矩阵时就可以参考这个特点了。
算法原理是:
float2x2 sobel= float2x2(1, 1,1, 1);
这里申请了2x2的矩阵,全是1,这个矩阵主要用于采样邻近的四个斜对角的点的像素。然后我们需要再采样一个正常的当前像素乘(-4)的值来平衡整个权重
int forLen = 2;for (int m = 0; m < forLen; m++)for (int n = 0; n < forLen; n++){sobelH += tex2D(_MainTex, i.uv + offsets[m * forLen + n] * adjacentPixel) *laplacianOperator[m][n];}sobelH += tex2D(_MainTex, i.uv) * -1 * forLen * forLen;
这就是整个shader中最耗时的地方了做了五次采样。
其他都是正常的算法了。
另外想说的是,sobel算子他需要算开根号
我们其实并不需要,只需要算平方后我们把值整体缩小就好了。(或者输入时让美术输入更大的值起步)
整个算法如下:
struct v2f_outline
{float2 uv: TEXCOORD0;float4 pos : SV_POSITION;float3 normal : TEXCOORD1;
};v2f_outline vert_sobal(appdata v){v2f_outline o;o.pos = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);return o;}
float4 fragColor_sobel(v2f_outline i) : COLOR{half offset = 1;float2 offsets[4] = {float2(-offset, offset),float2(offset, offset),float2(-offset, -offset),float2(offset, -offset)};float2x2 sobel = float2x2(1, 1,1, 1);float4 sobelH = float4(0, 0, 0, 0);float2 adjacentPixel = _MainTex_TexelSize.xy * _AdjacentPixel;int forLen = 2;for (int m = 0; m < forLen; m++)for (int n = 0; n < forLen; n++){sobelH += tex2D(_MainTex, i.uv + offsets[m * forLen + n] * adjacentPixel) *sobel[m][n];}sobelH += tex2D(_MainTex, i.uv) * -1 * forLen * forLen;float sobel = sobelH * sobelH;float4 sceneColor = tex2D(_MainTex, i.uv);float edgeMask = saturate(sobel);float3 EdgeMaskColor = float3(edgeMask, edgeMask, edgeMask);float3 finalColor = saturate((EdgeMaskColor * _OutlineColor.rgb) + (sceneColor.rgb - EdgeMaskColor));return float4(_OutlineColor.rgb, sceneColor.a - 1 + EdgeMaskColor.r);}Pass{Name "SIMPLE_TRANSPARENT"Tags{"LightMode" = "LightweightForward"}CGPROGRAM#pragma vertex vert#pragma fragment fragENDCG}
最后看看效果
另外,关于锯齿可以通过msaa或做模糊来处理
如果想要描边选择区域可以控制,可以用_CameraDepthTexture获取深度信息,然后根据不同的深度显示不同的数据