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 }
相关文章推荐
- Shader学习的基础知识( 三十一)水波效果
- shader学习基础之十二矩阵的左乘还是右乘所导致的效果问题
- Shader学习的基础知识( 三十四)表面着色器上
- Shader学习的基础知识(二十八)卡通风格渲染
- Shader学习的基础知识(二十九)素描风格渲染
- shader学习基础之十四,毛玻璃效果解析
- shader学习之基础纹理透明效果
- Shader学习的基础知识(二十七)全局雾效
- Shader学习的基础知识(四)法线贴图
- Shader学习的基础知识( 三十二)渲染优化概括
- Shader学习的基础知识(五)渐变纹理
- Shader学习的基础知识( 三十三)渲染优化具体
- JS 面试知识学习历程(第二天) -- JS基础知识(上)
- Tensorflow学习笔记之一 —— 基础知识篇
- shader学习笔记 - 水底效果
- JNI基础知识学习汇总
- Java-2-学习历程2:基础知识1,2,3文档、完整版视频资源、电子书籍下载
- salesforce 零基础学习(二十七)VF页面等待(loading)效果制作
- [学习笔记] Java核心技术 卷一:基础知识 Java 的基本程序设计结构(一)
- 学习C#基础知识这段时间