【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.rhkb.cn/news/191593.html

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

相关文章

【仿真动画】双机器人协作完成一个任务(切割)

场景 动画 两个机器人协同工作完成一个任务需要解决以下几个关键问题&#xff1a; 通信&#xff1a;两个机器人需要能够相互通信&#xff0c;以共享信息&#xff0c;例如位置、姿态、状态等。规划&#xff1a;需要对两个机器人的运动轨迹进行规划&#xff0c;确保两个机器人不会…

git 命令行回退版本

git 命令行回退版本 git 命令行回退版本命令: 1.切换到需要回退的分支 git checkout branch-v2.0.02.更新远程分支 git fetch3.找到需要回退版本的版本号git revert a6914da55ff40a09e67ac2426b86f1212e6580eb4.清除工作区缓存git clean -df5.强制提交git push -f

Blazor 附件上传和下载功能

效果图 page "/uploadFile" inject Microsoft.AspNetCore.Hosting.IWebHostEnvironment WebHostEnvironment inject ToastService ToastService inject DownloadService DownloadService<h3>UploadFile</h3><Button OnClick"ButtonClick" C…

windows10上使用Visual Studio对树莓派进行交叉编译示例

本文主要介绍通过Visual Studio对树莓派进行交叉编译的方法。 1 环境 宿主机&#xff1a; 系统&#xff1a;Windows10 开发平台&#xff1a;Visual Studio 2022 (我用的是社区版) VisualGDB: VisualGDB - Download (我下的试用版本) GNU工具链: Prebuilt GNU toolchain f…

JVM及其垃圾回收机制(GC)

目录 一.JVM内存区域划分 二.JVM类加载机制 类加载过程 类加载的时机 双亲委派模型 三.JVM垃圾回收机制&#xff08;GC) GC工作过程 1.找到垃圾/判断垃圾 &#xff08;1&#xff09;引用计数【python/PHP】 &#xff08;2&#xff09;可达性分析【Java】 2.对象释放…

读写分离(基于mycat)和全同步复制

一、mycat实现读写分离&#xff08;VIP机制&#xff09; &#xff08;一&#xff09;配置主从复制 &#xff08;二&#xff09;部署mycat 1、安装Java 2、下载mycat安装包 3、解压mycat包 4、设置变量环境 5、启动mycat &#xff08;三&#xff09;客户端连接数据库 1、安装…

Git的进阶操作,在idea中部署gie

&#x1f3c5;我是默&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; ​​ &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《git》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c;还是有一定基础的程序员&#xff0c;这…

解决pikachu中RCE中文乱码的问题

这个问题我在DVWA中的RCE栏目同样遇到过&#xff0c;今天在做pikachu的RCE的时候也遇到了&#xff0c;所以特此来解决一下这个问题&#xff0c;解决方法很简单&#xff0c;在源码中加入下一行代码。 $result iconv("GBK", "UTF-8", $result);加在68行前面…

keepalived 的安装部署及使用详细完整版

架构 1.安装 yum install keepalived -ysystemctl enable keepalivedsystemctl restart keepalivedsystemctl status keepalived2.部署配置样例 vim /etc/keepalived/keepalived.conf global_defs {router_id PROXYSQL_HAscript_user rootenable_script_security } vrrp_scri…

卫星通信和800MHz双管齐下,中国电信对中国移动发起新挑战

依靠国内某科技企业的宣传&#xff0c;卫星通信大热&#xff0c;中国电信也由此成为受益者&#xff0c;日前中国电信又大举招标25万座800MHz 5G基站&#xff0c;显示出中国电信积极以技术优势挑战中国移动。 一、中国电信急起直追 自从4G时代以来&#xff0c;中国电信就在国内通…

怎么用Postman脚本中发送请求

Postman的Collection(集合)/Folder(集合的子文件夹)/Request(请求)都有Pre-request script和Tests两个脚本区域, 分别可以在发送请求前和请求后使用脚本(基于Javascript实现各种操作) 在遇到有依赖的接口时,比如需要登录或者需要从前一个接口的结果中获取参数时,我们往往需要在…

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

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

ChatGPT-4:OpenAI的革命性升级

在人工智能领域&#xff0c;OpenAI这家公司凭借其创新性的技术&#xff0c;成为了备受瞩目的领导者。他们最近发布的ChatGPT-4&#xff0c;以其卓越的语言处理能力和先进的模型架构&#xff0c;引领了语言模型领域的革命性升级。 ChatGPT-4的模型容量相较于前一版本有了显著的提…

Jmeter之Bean shell使用详解

一、什么是Bean Shell BeanShell是一种完全符合Java语法规范的脚本语言,并且又拥有自己的一些语法和方法; BeanShell是一种松散类型的脚本语言(这点和JS类似); BeanShell是用Java写成的,一个小型的、免费的、可以下载的、嵌入式的Java源代码解释器,具有对象脚本语言特性,非常精…

stable diffusion为什么能用于文本到图像的生成

推荐基于稳定扩散(stable diffusion) AI 模型开发的自动纹理工具&#xff1a; DreamTexture.js自动纹理化开发包 - NSDT 稳定扩散获得如此多关注的原因 如果你还没有看过它&#xff1a;稳定扩散是一个文本到图像的生成模型&#xff0c;你可以输入一个文本提示&#xff0c;比如…

Project IDX简介——这是一项改进全栈、多平台应用程序开发的试验

如今&#xff0c;将应用程序从零开发到生产环境&#xff08;尤其是在移动、网络和桌面平台上运行良好的应用程序&#xff09;感觉就像构建一台 Rube Goldberg 机器。您必须在无尽的复杂性海洋中航行&#xff0c;将各种技术堆栈粘合在一起&#xff0c;以引导、编译、测试、部署和…

Elasticsearch7 入门 进阶

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

许战海方法论新成果《全球产业技术品牌竞争战略》研究报告正式发布

在全球化趋势下&#xff0c;产业技术正在加速迭代、产业结构也在持续优化。企业要想从愈发激烈的市场竞争中脱颖而出&#xff0c;需要更强大的竞争力。 在全球产业研究项目中&#xff0c;许战海咨询深入剖析了潍柴动力、利乐、奥迪、霍尼韦尔等一众在产业竞争中脱颖而出的企业…

高速高精运动控制,富唯智能AI边缘控制器助力自动化行业变革

随着工业大数据时代的到来&#xff0c;传统控制与决策方式无法满足现代数字化工厂对工业大数据分析与决策的需求&#xff0c;AI边缘控制器赋能现代化智慧工厂&#xff0c;实现工业智造与行业变革。 富唯智能AI边缘控制器&#xff0c;基于x86架构的IPC形态产品&#xff0c;通过…

C#中数组、ArrayList与List对象的区别及使用场景

在C#编程中&#xff0c;数组、ArrayList和List对象是常用的数据结构和容器。它们在存储和管理数据方面都有各自的特点和用途。本文将深入探讨这三者的区别&#xff0c;并通过实际的代码示例来说明它们的使用场景和优缺点。 目录 1.数组特点使用场景 2.ArrayList特点使用场景 3.…