【unity】网格描边方法

【unity】网格描边方法

介绍对模型四边网格的三种描边方法:包括纯Shader方法、创建网格方法和后处理方法。于增强场景中3D模型的轮廓,使其在视觉上更加突出和清晰。这种效果可以用于增强三维场景中的物体、角色或环境,使其在视觉上更加吸引人。

网格描边方法资源

Shader方法

使用GeometryShader方法对三角网进行计算,目的是保留距离最短的两条边。在进行计算时,首先需要建立一个float2 dist来储存点的信息。在进行插值后,需要保留边的dist,其中一个数值为0,以此为依据来绘制边。下图展示了顶点dist的赋值情况。

这种方法可以让我们在渲染三角网时,根据点之间的距离信息来动态地调整边的绘制,从而实现更加真实和精细的渲染效果。

实现效果

实现shader

Shader "Unlit/WireframeMesh"
{Properties{_MainTex("Texture", 2D) = "white" { }_WireColor("WireColor", Color) = (1, 0, 0, 1)_FillColor("FillColor", Color) = (1, 1, 1, 1)_WireWidth("WireWidth", Range(0, 1)) = 1}SubShader{Tags { "RenderType" = "Transparent" "Queue" = "Transparent" }LOD 100AlphaToMask On // 为此通道启用减法混合Pass{Blend SrcAlpha OneMinusSrcAlphaCull OffCGPROGRAM#pragma vertex vert#pragma geometry geom //添加几何阶段#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex: POSITION;float2 uv: TEXCOORD0;};struct v2g {float2 uv: TEXCOORD0;float4 vertex: SV_POSITION;};struct g2f{float2 uv: TEXCOORD0;float4 vertex: SV_POSITION;float2 dist: TEXCOORD1;float maxlenght : TEXCOORD2;};sampler2D _MainTex;float4 _MainTex_ST;float4 _FillColor, _WireColor;float _WireWidth, _Clip, _Lerp, _WireLerpWidth;//视口到几何v2g vert(appdata v) {v2g o;o.vertex = v.vertex;o.uv = TRANSFORM_TEX(v.uv, _MainTex);return o;}//几何到片元[maxvertexcount(3)]void geom(triangle v2g IN[3], inout TriangleStream < g2f > triStream){//读取三角网各个顶点float3 p0 = IN[0].vertex;float3 p1 = IN[1].vertex;float3 p2 = IN[2].vertex;//计算三角网每一边的长度float v0 = length(p1 - p2);float v1 = length( p2 - p0);float v2 = length( p0 - p1);//求出最长边float v_max = max(v2,max(v0, v1));//每一边减最长边,小于0时为0,等于0时为1float f0 = step(0, v0 - v_max);float f1 = step(0, v1 - v_max);float f2 = step(0, v2 - v_max);//赋值传到片元操作g2f OUT;OUT.vertex = UnityObjectToClipPos(IN[0].vertex);OUT.uv = IN[0].uv;OUT.maxlenght = v_max;OUT.dist = float2(f1, f2);triStream.Append(OUT);OUT.vertex = UnityObjectToClipPos( IN[1].vertex);OUT.uv = IN[1].uv;OUT.maxlenght = v_max;OUT.dist = float2(f2, f0);triStream.Append(OUT);OUT.vertex = UnityObjectToClipPos( IN[2].vertex);OUT.maxlenght = v_max;OUT.uv = IN[2].uv;OUT.dist = float2(f0, f1);triStream.Append(OUT);}//片元阶段fixed4 frag(g2f i) : SV_Target{fixed4 col = tex2D(_MainTex, i.uv );fixed4 col_Wire= col* _FillColor;//取dist最小值float d =  min(i.dist.x, i.dist.y);//d小于线宽是赋值线颜色,否则赋值背景颜色col_Wire = d < _WireWidth ? _WireColor : col_Wire;fixed4 col_Tex = tex2D(_MainTex, i.uv);return col_Wire;}ENDCG}}
}

该方法不支持webGL,原因webGL不支持GeometryShader。

介绍一个根据uv创建网格的方法,虽然支持webGL,但是局限性太大,不做详细介绍,附上shader

Shader "Unlit/WireframeUV"
{Properties{_MainTex ("Texture", 2D) = "white" {}_FillColor("FillColor", Color) = (1, 1, 1, 1)[HDR] _WireColor("WireColor", Color) = (1, 0, 0, 1)_WireWidth("WireWidth", Range(0, 1)) = 1}SubShader{Tags { "RenderType"="Opaque" }LOD 100AlphaToMask OnPass{Tags { "RenderType" = "TransparentCutout" }Blend SrcAlpha OneMinusSrcAlphaCull OffCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;fixed4 _FillColor;fixed4 _WireColor;float _WireWidth;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);return o;}fixed4 frag (v2f i) : SV_Target{fixed4 col = tex2D(_MainTex, i.uv);fixed2 uv2 = abs(i.uv - fixed2(0.5f, 0.5f));float minUV = max(uv2.x, uv2.y);col = minUV < 0.5- _WireWidth ? col* _FillColor : _WireColor;return col;}ENDCG}}
}

创建网格方法

这个方法支持在内置built-in管线中使用,实现原理和shader方法类似,不同的是需要构建线网格。根据原有三角网格抽取其中最短两条重新绘制。

实现效果

因为CommandBuffer方法暂时无法设置线宽,用了一些后处理方法

实现的方法


using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;public class CamerDrawMeshDemo : MonoBehaviour
{[SerializeField]MeshFilter meshFilter;CommandBuffer cmdBuffer;[SerializeField]Material cmdMat1;// Start is called before the first frame updatevoid Start(){//创建一个CommandBuffercmdBuffer = new CommandBuffer() { name = "CameraCmdBuffer" };Camera.main.AddCommandBuffer(CameraEvent.AfterForwardOpaque, cmdBuffer);DarwMesh();}//绘制网格void DarwMesh(){cmdBuffer.Clear();Mesh  m_grid0Mesh = meshFilter.mesh;//读取原有网格,这里需要开启网格可读写cmdBuffer.DrawMesh(CreateGridMesh(m_grid0Mesh), Matrix4x4.identity, cmdMat1);}//创建网格Mesh CreateGridMesh(Mesh TargetMesh){Vector3[] vectors= getNewVec(TargetMesh.vertices);//模型坐标转换到世界坐标Vector3[] getNewVec(Vector3[] curVec){int count = curVec.Length;Vector3[] vec = new Vector3[count];for (int i = 0; i < count; i++){//坐标转型,乘上变化矩阵vec[i] =(Vector3)(transform.localToWorldMatrix* curVec[i])+transform.position;}return vec;}int[] triangles = TargetMesh.triangles;List<int> indicesList = new List<int>(2);//筛选绘制边for (int i = 0; i < triangles.Length; i+=3){Vector3 vec;int a = triangles[i];int b = triangles[i+1];int c = triangles[i+2];vec.x = Vector3.Distance(vectors[a], vectors[b]);vec.y = Vector3.Distance(vectors[b], vectors[c]);vec.z = Vector3.Distance(vectors[c], vectors[a]);addList(vec, a,b,c);}void addList(Vector3 vec,int a,int b,int c){if (vec.x< vec.y|| vec.x <vec.z){indicesList.Add(a);indicesList.Add(b);}if (vec.y < vec.x || vec.y < vec.z){indicesList.Add(b);indicesList.Add(c);}if (vec.z < vec.x || vec.z < vec.y){indicesList.Add(c);indicesList.Add(a);}}int[] indices = indicesList.ToArray();//创建网格Mesh mesh = new Mesh();mesh.name = "Grid ";mesh.vertices = vectors;mesh.SetIndices(indices, MeshTopology.Lines, 0);return mesh;}
}

后处理方法

利用深度纹理和法线纹理来比较相邻像素之间的相似性,以判断它们是否位于物体的边缘,并进而实现描边效果。具体而言,该算法会对相邻像素的深度值和法线值进行比较,若它们之间的差异超过一定阈值,则认为这两个像素位于物体的边缘上。通过这一方法,我们可以在渲染时对边缘进行特殊处理,以实现描边效果。

实现效果

实现方法

建立后渲染脚本挂载在主相机上

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SceneOnlineDemo : MonoBehaviour
{public  Shader OnlineShader;Material material;[ColorUsage(true, true)]public Color ColorLine;public Vector2 vector;public float LineWide;// Start is called before the first frame updatevoid Start(){material = new Material(OnlineShader);GetComponent<Camera>().depthTextureMode |= DepthTextureMode.DepthNormals;}void Update(){}void OnRenderImage(RenderTexture src, RenderTexture dest){if (material != null){material.SetVector("_ColorLine", ColorLine);material.SetVector("_Sensitivity", vector);material.SetFloat("_SampleDistance", LineWide);Graphics.Blit(src, dest, material);}else{Graphics.Blit(src, dest);}}
}

后处理shader挂载在SceneOnlineDemo 脚本上

Shader "Unlit/SceneOnlineShader"
{Properties{_MainTex ("Texture", 2D) = "white" {}[HDR] _ColorLine("ColorLine", Color) = (1,1,1,1)   //颜色,一般用fixed4_Sensitivity("Sensitivity", Vector) = (1, 1, 1, 1)    //xy分量分别对应法线和深度的检测灵敏度,zw分量没有实际用途_SampleDistance("Sample Distance", Float) = 1.0}SubShader{Tags { "RenderType" = "Opaque" }LOD 100Pass{ZTest Always Cull Off ZWrite OffCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"sampler2D _MainTex;half4 _MainTex_TexelSize;sampler2D _CameraDepthNormalsTexture;    //深度+法线纹理sampler2D _CameraDepthTexture;fixed4 _ColorLine;float _SampleDistance;half4 _Sensitivity;struct v2f{half2 uv[5]: TEXCOORD0;float4 vertex : SV_POSITION;};v2f vert (appdata_img v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);half2 uv = v.texcoord;o.uv[0] = uv;#if UNITY_UV_STARTS_AT_TOPif (_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);// 法线相差half2 diffNormal = abs(centerNormal - sampleNormal) * _Sensitivity.x;int isSameNormal = (diffNormal.x + diffNormal.y) < 0.1;// 深度相差float diffDepth = abs(centerDepth - sampleDepth) * _Sensitivity.y;// 按距离缩放所需的阈值int isSameDepth = diffDepth < 0.1 * centerDepth;// return:// 1 - 如果法线和深度足够相似// 0 - 相反return isSameNormal * isSameDepth ? 1.0 : 0.0;}fixed4 frag (v2f i) : SV_Target{fixed4 col = tex2D(_MainTex,  i.uv[0]);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(_ColorLine, col, edge);return withEdgeColor;}ENDCG}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/142016.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端---CSS的盒模型

文章目录 什么是盒模型&#xff1f;设置边框设置内边距设置外边距块级元素水平居中 什么是盒模型&#xff1f; 页面上的每个HTML元素都是一个一个的“盒子”&#xff0c;这些盒子由&#xff1a;内容、内边距、边框、外边距组成。 我们可以和住的房子联系起来&#xff0c;更好…

Python实现WOA智能鲸鱼优化算法优化卷积神经网络回归模型(CNN回归算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 鲸鱼优化算法 (whale optimization algorithm,WOA)是 2016 年由澳大利亚格里菲斯大学的Mirjalili 等提…

STM32H750之FreeRTOS学习--------(六)FreeRTOS的列表和列表项

六、FreeRTOS的列表和列表项 文章目录 六、FreeRTOS的列表和列表项列表相关结构体列表项相关结构体迷你列表项列表相关API函数介绍初始化列表vListInitialise()函数vListInitialiseItem()函数vListInsert()函数 vListInsertEnd()函数 uxListRemove() 列表就是一个双向链表&…

21.计算老师的工资

&#xff08;定义结构体数组存放教师的财务信息&#xff08;教工卡号&#xff0c;应发工资&#xff0c;个人所得税&#xff0c;实发工资&#xff09;具体要求如下从键盘输入5个教师的教工卡号&#xff0c;应发工资 计算每人的个人所得税&#xff08;应发工资10%&#xff09;&am…

Prometheus+Ansible+Consul实现服务发现

一、简介 1、Consul简介 Consul 是基于 GO 语言开发的开源工具&#xff0c;主要面向分布式&#xff0c;服务化的系统提供服务注册、服务发现和配置管理的功能。Consul 提供服务注册/发现、健康检查、Key/Value存储、多数据中心和分布式一致性保证等功能。 在没有使用 consul 服…

C语言仅凭自学能到什么高度?

今日话题&#xff0c;C语言仅凭自学能到什么高度&#xff1f;学习C语言的决定我确实非常推荐&#xff0c;毕竟它是编程领域的“通用工具”&#xff0c;初学者可以尝试并在发现编程的乐趣后制定长期学习计划。至于能够达到何种高度&#xff0c;这实在无法准确回答。即使是经验丰…

ARM64 linux并发与同步之经典自旋锁

1.3 经典自旋锁 在实际项目中临界区数据有可能会修改一个数据结构或者链表中的数据&#xff0c;在整个过程中要保证原子性&#xff0c;才不会影响数据的有效性&#xff0c;这个过程使用原子变量不合适&#xff0c;需要使用锁机制来完成&#xff0c;自旋锁&#xff08;spinlock&…

Elasticsearch7 入门 进阶

1、全文检索 1.1、数据分类 按数据分类的话&#xff0c;主要可以分为以下三类&#xff1a; 结构化数据&#xff1a;固定格式、有限长度&#xff0c;比如mysql存的数据非结构化数据&#xff1a;不定长、无固定格式&#xff0c;比如邮件、Word文档、日志等半结构化数据&#xf…

【Opencv】cv::dnn::NMSBoxes()函数详解

本文通过原理和示例对cv::dnn::NMSBoxes&#xff08;&#xff09;进行解读&#xff0c;帮助大家理解和使用。 原理 cv::dnn::NMSBoxes是OpenCV库中的一个函数&#xff0c;用于在目标检测中处理多个预测框。在目标检测中&#xff0c;模型可能会为同一个物体生成多个预测框&…

传统企业数字化转型都要面临哪些挑战?_数据治理平台_光点科技

数字化转型已经成为传统企业发展的必经之路&#xff0c;但在这个过程中&#xff0c;企业往往会遭遇多方面的挑战。 1.文化和组织惯性 最大的挑战之一是企业文化和组织惯性的阻力。传统企业往往有着深厚的历史和根深蒂固的工作方式&#xff0c;员工和管理层可能对新的数字化工作…

海外媒体发稿:彭博社发稿宣传中,5种精准营销方式

在如今的信息发生爆炸时期&#xff0c;营销方式多种多样&#xff0c;但是充分体现精准营销并针对不同用户群体的需求并非易事。下面我们就根据彭博社发稿营销推广为例子&#xff0c;给大家介绍怎样根据不同用户人群方案策划5种精准营销方式。 1.界定总体目标用户人群在制订精准…

Spring IOC - Bean的生命周期之实例化

在Spring启动流程文章中讲到&#xff0c;容器的初始化是从refresh方法开始的&#xff0c;其在初始化的过程中会调用finishBeanFactoryInitialization方法。 而在该方法中则会调用DefaultListableBeanFactory#preInstantiateSingletons方法&#xff0c;该方法的核心作用是初始化…

【nlp】2.4 GRU模型

GRU模型 1 GRU介绍2 GRU的内部结构图2.1 GRU结构分析2.2 Bi-GRU介绍2.3 使用Pytorch构建GRU模型2.4 GRU优缺点3 RNN及其变体1 GRU介绍 GRU(Gated Recurrent Unit)也称门控循环单元结构, 它也是传统RNN的变体, 同LSTM一样能够有效捕捉长序列之间的语义关联, 缓解梯度消失或爆…

0基础学习VR全景平台篇第120篇:极坐标处理接缝 - PS教程

上课&#xff01;全体起立~ 大家好&#xff0c;欢迎观看蛙色官方系列全景摄影课程&#xff01; 紧跟上节课&#xff0c;我们已经学会了怎么利用PS蒙版工具来对航拍全景图补天。但是在后续工作学习中&#xff0c;我们会遇到天空这部分存在部分接缝的问题&#xff0c;如图&…

使用Docker本地安装部署Drawio绘图工具并实现公网访问

目录 前言 1. 使用Docker本地部署Drawio 2. 安装cpolar内网穿透工具 3. 配置Draw.io公网访问地址 4. 公网远程访问Draw.io 前言 提到流程图&#xff0c;大家第一时间可能会想到Visio&#xff0c;不可否认&#xff0c;VIsio确实是功能强大&#xff0c;但是软件为收费&…

Zephyr-7B论文解析及全量训练、Lora训练

文章目录 一、Zephyr&#xff1a;Direct Distillation of LM Alignment1.1 开发经过1.1.1 Zephyr-7B-alpha1.1.2 Zephyr-7B-beta 1.2 摘要1.3 相关工作1.4 算法1.4.1 蒸馏监督微调&#xff08;dSFT&#xff09;1.4.2 基于偏好的AI反馈 (AIF&#xff09;1.4.3 直接蒸馏偏好优化&…

英伟达中国特供芯片是缩水版;华为 Mate60 Pro 国产零件价值占比 47%丨 RTE 开发者日报 Vol.84

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE &#xff08;Real Time Engagement&#xff09; 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文…

数据分析的流程:CRISP-DM方法和SEMMA方法

CRISP-DM方法 SEMMA方法 角色与职责&#xff1a;EDIT数字化模型

VMware 虚拟机开启后黑屏问题的解决方式

很好&#xff0c;现在是vm 虚拟机节目的连续剧了 首先&#xff0c;我们安装好了&#xff0c;vm软件。 其次&#xff0c;我们在vm中创建了虚拟机。 再其次&#xff0c;我们解决了&#xff0c;开启虚拟机计算机自动重启的问题。 最后我们遇到了这个问题&#xff1a;虚拟机开启后整…

软路由R4S+iStoreOS实现公网远程桌面局域网内电脑

软路由R4SiStoreOS实现公网远程桌面局域网内电脑 文章目录 软路由R4SiStoreOS实现公网远程桌面局域网内电脑简介 一、配置远程桌面公网地址配置隧道 二、家中使用永久固定地址 访问公司电脑具体操作方法是&#xff1a;2.1 登录页面2.2 再次配置隧道2.3 查看访问效果 简介 上篇…