您的位置:首页 > 其它

Shader学习的基础知识(二十六)描边效果

2019-03-15 18:20 483 查看
版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ww1351646544/article/details/88578927

这里我们学习怎么通过深度、法线纹理和Roberts算子来做描边效果。
Roberts算子的本质就是计算左上和右下角的差值作为估算边缘的依据。
解释都在代码里
摄像机脚本部分

using UnityEngine;
using System.Collections;

public class EdgeDetectNormalsAndDepth : PostEffectsBase {

public Shader edgeDetectShader;
private Material edgeDetectMaterial = null;
public Material material {
get {
edgeDetectMaterial = CheckShaderAndCreateMaterial(edgeDetectShader, edgeDetectMaterial);
return edgeDetectMaterial;
}
}

[Range(0.0f, 1.0f)]
public float edgesOnly = 0.0f;

public Color edgeColor = Color.black;

public Color backgroundColor = Color.white;

//控制采样距离
public float sampleDistance = 1.0f;
//影响相差多少时会被认识是遍边界
public float sensitivityDepth = 1.0f;
//和上面一样,影响相差多少时会被认识是遍边界
public float sensitivityNormals = 1.0f;

void OnEnable() {
//取深度加法线
GetComponent<Camera>().depthTextureMode |= DepthTextureMode.DepthNormals;
}

//ImageEffectOpaque只对队列2500以内的物体产生影响
[ImageEffectOpaque]
void OnRenderImage (RenderTexture src, RenderTexture dest) {
if (material != null) {
material.SetFloat("_EdgeOnly", edgesOnly);
material.SetColor("_EdgeColor", edgeColor);
material.SetColor("_BackgroundColor", backgroundColor);
material.SetFloat("_SampleDistance", sampleDistance);
material.SetVector("_Sensitivity", new Vector4(sensitivityNormals, sensitivityDepth, 0.0f, 0.0f));

Graphics.Blit(src, dest, material);
} else {
Graphics.Blit(src, dest);
}
}
}

基类代码

using UnityEngine;
using System.Collections;

[ExecuteInEditMode]
[RequireComponent (typeof(Camera))]
public class PostEffectsBase : MonoBehaviour {

//检查各种资源是否满足,我们调用
protected void CheckResources()
{
bool isSupported = CheckSupport();

if (isSupported == false) {
NotSupported();
}
}

//检查是否支持
protected bool CheckSupport() {
if (SystemInfo.supportsImageEffects == false || SystemInfo.supportsRenderTextures == false) {
Debug.LogWarning("This platform does not support image effects or render textures.");
return false;
}

return true;
}

//不支持的处理
protected void NotSupported() {
enabled = false;
}

protected void Start() {
CheckResources();
}

/// <summary>
/// 后期处理
/// </summary>
/// <param name="shader">该特效使用的Shader</param>
/// <param name="material">用于处理的材质</param>
/// <returns></returns>
protected Material CheckShaderAndCreateMaterial(Shader shader, Material material) {
if (shader == null) {
return null;
}

if (shader.isSupported && material && material.shader == shader)
return material;

if (!shader.isSupported) {
return null;
}
else {
material = new Material(shader);
material.hideFlags = HideFlags.DontSave;
if (material)
return material;
else
return null;
}
}
}

Shader部分

Shader "Custom/TestShader30" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_EdgeOnly ("Edge Only", Float) = 1.0
_EdgeColor ("Edge Color", Color) = (0, 0, 0, 1)
_BackgroundColor ("Background Color", Color) = (1, 1, 1, 1)
_SampleDistance ("Sample Distance", Float) = 1.0
_Sensitivity ("Sensitivity", Vector) = (1, 1, 1, 1)
}
SubShader {
//CGINCLUDE内的代码相当于声明在后面所有的Pass中了
CGINCLUDE

#include "UnityCG.cginc"

sampler2D _MainTex;
half4 _MainTex_TexelSize;
fixed _EdgeOnly;
fixed4 _EdgeColor;
fixed4 _BackgroundColor;
float _SampleDistance;
half4 _Sensitivity;
//深度加法线纹理
sampler2D _CameraDepthNormalsTexture;

struct v2f {
float4 pos : SV_POSITION;
half2 uv[5]: TEXCOORD0;
};

v2f vert(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);

//屏幕的纹理采样
half2 uv = v.texcoord;
o.uv[0] = uv;

//根据平台做反转
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
uv.y = 1 - uv.y;
#endif

//使用Roberts算子时需要采样的纹理坐,_SampleDistance是采样距离
o.uv[1] = uv + _MainTex_TexelSize.xy * half2(1,1) * _SampleDistance;
o.uv[2] = uv + _MainTex_TexelSize.xy * half2(-1,-1) * _SampleDistance;
o.uv[3] = uv + _MainTex_TexelSize.xy * half2(-1,1) * _SampleDistance;
o.uv[4] = uv + _MainTex_TexelSize.xy * half2(1,-1) * _SampleDistance;

return o;
}

//这个函数用于计算两个纹理的差值,返回0(有边界)或1(无边界)
half CheckSame(half4 center, half4 sample) {
//两个样点的深度和法线采样,只取了xy因为只需要知道差异,而不用具体
half2 centerNormal = center.xy;
float centerDepth = DecodeFloatRG(center.zw);
half2 sampleNormal = sample.xy;
float sampleDepth = DecodeFloatRG(sample.zw);

//两个值相减后绝度值可以知道两个值法向量的差距
half2 diffNormal = abs(centerNormal - sampleNormal) * _Sensitivity.x;
//法线差距是否小于0.1
int isSameNormal = (diffNormal.x + diffNormal.y) < 0.1;
//两个值相减后绝度值可以知道两个值深度的差距
float diffDepth = abs(centerDepth - sampleDepth) * _Sensitivity.y;
// 深度的差距是否小于0.1
int isSameDepth = diffDepth < 0.1 * centerDepth;

// return:
// 1 - if normals and depth are similar enough
// 0 - otherwise
return isSameNormal * isSameDepth ? 1.0 : 0.0;
}

//在下面的Pass中有指定,这个是片元着色器
fixed4 fragRobertsCrossDepthAndNormal(v2f i) : SV_Target {
//取得卷积算子的值
half4 sample1 = tex2D(_CameraDepthNormalsTexture, i.uv[1]);
half4 sample2 = tex2D(_CameraDepthNormalsTexture, i.uv[2]);
half4 sample3 = tex2D(_CameraDepthNormalsTexture, i.uv[3]);
half4 sample4 = tex2D(_CameraDepthNormalsTexture, i.uv[4]);

half edge = 1.0;

//只要有一个返回0则为边界
edge *= CheckSame(sample1, sample2);
edge *= CheckSame(sample3, sample4);

//融合一下颜色
fixed4 withEdgeColor = lerp(_EdgeColor, tex2D(_MainTex, i.uv[0]), edge);
fixed4 onlyEdgeColor = lerp(_EdgeColor, _BackgroundColor, edge);

//lerp(色块一,色块二,色块二占比)
return lerp(withEdgeColor, onlyEdgeColor, 0);
}

ENDCG

Pass {
//上面的CGINCLUDE会把内容拷贝到所有的Pass中
ZTest Always Cull Off ZWrite Off

CGPROGRAM

#pragma vertex vert
#pragma fragment fragRobertsCrossDepthAndNormal

ENDCG
}
}
FallBack Off
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: