news 2026/5/1 8:54:05

《Unity Shader》13.4 再谈边缘检测

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
《Unity Shader》13.4 再谈边缘检测

(1)(2)

把Scene12_6另存为 Scene_13_4

(3)

(4)

https://github.com/candycat1992/Unity_Shaders_Book/blob/master/Assets/Scripts/Chapter13/EdgeDetectNormalsAndDepth.cs

EdgeDetectNormalsAndDepth.cs

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] 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); } } }

Chapter13-EdgeDetectNormalAndDepth.shader

Shader "Custom/Chapter13-EdgeDetectNormalAndDepth" { 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) //_Sensitivity的xy分量分别对应了法线和深度的检测灵敏度,zw分量则没有实际用途 } SubShader { CGINCLUDE #include "UnityCG.cginc" sampler2D _MainTex; half4 _MainTex_TexelSize; //需要对邻域像素进行纹理采样 fixed _EdgeOnly; fixed4 _EdgeColor; fixed4 _BackgroundColor; float _SampleDistance; half4 _Sensitivity; sampler2D _CameraDepthNormalsTexture; //深度+法线纹理_CameraDepthNormalsTexture struct v2f { float4 pos : SV_POSITION; half2 uv[5]: TEXCOORD0; }; v2f vert(appdata_img v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, 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 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; } //计算采样纹理坐标的代码从片元着色器中转移到顶点着色器 half CheckSame(half4 center, half4 sample) { half2 centerNormal = center.xy; float centerDepth = DecodeFloatRG(center.zw); half2 sampleNormal = sample.xy; float sampleDepth = DecodeFloatRG(sample.zw); // difference in normals // do not bother decoding normals - there's no need here half2 diffNormal = abs(centerNormal - sampleNormal) * _Sensitivity.x; int isSameNormal = (diffNormal.x + diffNormal.y) < 0.1; // difference in depth float diffDepth = abs(centerDepth - sampleDepth) * _Sensitivity.y; // scale the required threshold by the distance int isSameDepth = diffDepth < 0.1 * centerDepth; // return: // 1 - if normals and depth are similar enough // 0 - otherwise return isSameNormal * isSameDepth ? 1.0 : 0.0; } 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; edge *= CheckSame(sample1, sample2); edge *= CheckSame(sample3, sample4); fixed4 withEdgeColor = lerp(_EdgeColor, tex2D(_MainTex, i.uv[0]), edge); fixed4 onlyEdgeColor = lerp(_EdgeColor, _BackgroundColor, edge); return lerp(withEdgeColor, onlyEdgeColor, _EdgeOnly); }//采样点的对应值相减并取绝对值,再乘以灵敏度参数,把差异值的每个分量相加再和一个阈值比较,如果它们的和小于阈值,则返回1,说明差异不明显,不存在一条边界;否则返回0。最后,我们把法线和深度的检查结果相乘,作为组合后的返回值 ENDCG Pass { ZTest Always Cull Off ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment fragRobertsCrossDepthAndNormal ENDCG } } FallBack Off }

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 8:48:56

Gemini3ProImage(nano banana 2 )异步调用接口(API)生成图片

什么是 Gemini3ProImage"Nano Banana 2"&#xff1f;&#x1f9e0; 这不是一次简单的版本迭代&#xff0c;而是架构的重构。 Gemini 3 Pro Image 搭载了最新的 Nano Banana 2 核心。Nano Banana 2 引入了革命性的**“思维链&#xff08;Thinking Process&#xff09…

作者头像 李华
网站建设 2026/4/20 8:13:27

Java计算机毕设之基于springboo的小区车辆管理系统车位信息、临时车辆信息、IC卡管理(完整前后端代码+说明文档+LW,调试定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/4/24 1:07:18

【Vue知识点总结】Vue 路由中的 hidden: true:路由控制技巧

一、什么是 hidden: true? 在 Vue Router 中, hidden: true 是一个自定义的元信息字段(meta field),它本身不是 Vue Router 原生的配置项,而是开发团队约定俗成的一种用法。通常我们会这样配置路由: {path: /login,component: Login,meta: {hidden: true // 这里的 hi…

作者头像 李华
网站建设 2026/5/1 8:31:19

系统思考:以客户为中心

周一的学习实验室&#xff0c;有小伙伴提到“控场力”。我当下的判断是&#xff1a;控场本身并不是能力&#xff0c;而是系统良性运转后的外显结果。 顺着这个判断继续拆&#xff0c;我们发现一个关键变量反复出现——是否真正以学员为中心、以客户为中心。 这让我想到企业里那…

作者头像 李华
网站建设 2026/5/1 4:56:30

计算机Java毕设实战-基于小程序的上班企业考勤签到签退下班打卡系统设计与实现【完整源码+LW+部署说明+演示视频,全bao一条龙等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/5/1 8:40:02

书单推荐之豆包高效学习:AI时代的教育破局指南

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 &#x1f38f;&#xff1a;你只管努力&#xff0c;剩下的交给时间 &#x1f3e0; &#xff1a;小破站 书单推荐之豆包高效学习&#xff1a;AI时代的教育破局指南 为什么这本书值得每个家庭拥有&#x…

作者头像 李华