Unity URP 如何写基础的曲面细分着色器

左边是默认Cube在网格模式下经过曲面细分的结果,右边是原状态。

曲面细分着色器在顶点着色器、几何着色器之后,像素着色器之前。

它的作用时根据配置信息生成额外的顶点以切割原本的面片。

关于这部分有一个详细的英文教程,感兴趣可以看一下。

https://catlikecoding.com/unity/tutorials/advanced-rendering/tessellation/

以下是完整代码

Shader "Kerzh/KerzhCgShaderTemplate"
{Properties{_Color("Color", Color) = (1,1,1,1)_TessellationUniform ("Tessellation Uniform", Vector) = (1,1,1,1)}SubShader{Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalRenderPipeline" }Pass{CGPROGRAM#pragma vertex vert#pragma hull hull#pragma domain domain#pragma geometry geom#pragma fragment frag#pragma target 4.6#include "UnityCG.cginc"#include  "Assets/TA/ShaderLib/CgincInclude//CommonCgInc.cginc"#include  "Assets/TA/ShaderLib/CgincInclude//CustomTessellation.cginc"MeshData vert (MeshData input){return input;}//如果不正确配置会报错[UNITY_domain("tri")]  //  正在处理三角形   "tri", "quad", or "isoline"[UNITY_outputcontrolpoints(3)]  //  每个面片输出的顶点为3个[UNITY_outputtopology("triangle_cw")]  //  当创建三角形时应是顺时针还是逆时针,这里应是顺时针  "point", "line", "triangle_cw", or "triangle_ccw"[UNITY_partitioning("integer")]  //  如何细分切割面片   "integer", "pow2", "fractional_even", or "fractional_odd"[UNITY_patchconstantfunc("patchConstantFunction")]  //  细分切割部分还必须提供函数处理,每个面片调用一次MeshData hull (InputPatch<MeshData, 3> patch, uint id : SV_OutputControlPointID)  //  每个顶点调用一次,如果是处理三角形就是调用三次{//  如果_TessellationUniform输入值为1,不产生任何变化,但当输入值为2时,具体发生了什么//  对于一个三角形(因为我们配置的是处理三角形),根据三条边的二等分位置添加额外的一个顶点,如果是3就是三等分位置添加两个顶点//  对于一个三角形(因为我们配置的是处理三角形),根据三条个顶点添加一个中心顶点//  根据patchConstantFunction赋值分配生成顶点的插值权重return patch[id];}//  barycentricCoordinates分别代表各个点的插值权重,每个面片调用一次,对细分后的三角顶点形进行处理(也就是说原顶点不经过此处理?)[UNITY_domain("tri")]  //  正在处理三角形MeshData domain(TessellationFactors factors, OutputPatch<MeshData, 3> patch, float3 barycentricCoordinates : SV_DomainLocation)  {MeshData data;  //  新的插值权重顶点#define MY_DOMAIN_PROGRAM_INTERPOLATE(fieldName) data.fieldName = \patch[0].fieldName * barycentricCoordinates.x + \patch[1].fieldName * barycentricCoordinates.y + \patch[2].fieldName * barycentricCoordinates.z;//对所有信息利用插值权重进行插值计算MY_DOMAIN_PROGRAM_INTERPOLATE(vertex)MY_DOMAIN_PROGRAM_INTERPOLATE(normalOS)MY_DOMAIN_PROGRAM_INTERPOLATE(tangentOS)MY_DOMAIN_PROGRAM_INTERPOLATE(uv1)MY_DOMAIN_PROGRAM_INTERPOLATE(uv2)MY_DOMAIN_PROGRAM_INTERPOLATE(vertexColor)return data;}//最大的顶点数,这里比如你要生成三个三角形面片,那么一个面片需要三个顶点,就是9个顶点,一般来讲这里直接填多一点即可,不过可能填太多了会导致内存占用?[maxvertexcount(9)]void geom (triangle MeshData input[3],inout TriangleStream<VOutData> triStream){//原样转换过去VOutData output[3];for(int i=0;i<3;i++){VOutData p0;p0 = FillBaseV2FData(input[i]);output[i] = p0;}triStream.RestartStrip(); //  重新开始一个新的三角形triStream.Append(output[0]);triStream.Append(output[1]);triStream.Append(output[2]);return;//验证准确性用  勿删MeshData centerMeshData;centerMeshData.vertex = (input[0].vertex + input[1].vertex + input[2].vertex)/3.0;centerMeshData.uv1 = (input[0].uv1 + input[1].uv1 + input[2].uv1)/3.0;centerMeshData.uv2 = (input[0].uv2 + input[1].uv2 + input[2].uv2)/3.0;centerMeshData.tangentOS = (input[0].tangentOS + input[1].tangentOS + input[2].tangentOS)/3.0;centerMeshData.normalOS = (input[0].normalOS + input[1].normalOS + input[2].normalOS)/3.0;centerMeshData.vertexColor = (input[0].vertexColor + input[1].vertexColor + input[2].vertexColor)/3.0;centerMeshData.vertex += float4((centerMeshData.normalOS * 0.35), 0);VOutData center = FillBaseV2FData(centerMeshData);//  根据这三个点分别和中心点制造三角形输出triStream.RestartStrip(); //  重新开始一个新的三角形triStream.Append(output[1]);triStream.Append(center);triStream.Append(output[0]);triStream.RestartStrip();  //  重新开始一个新的三角形triStream.Append(output[2]);triStream.Append(center);triStream.Append(output[1]);triStream.RestartStrip(); //  重新开始一个新的三角形triStream.Append(output[0]);triStream.Append(center);triStream.Append(output[2]);}float4 _Color;float4 frag (VOutData i) : SV_Target{return _Color;}ENDCG}}
}

依赖文件CommonCgInc.cginc:

#ifndef CommonCgInc
#define CommonCgIncfloat3 FromScript_LocalPositionWS;
float3 FromScript_LocalRotationWS;
float3 FromScript_LocalScaleWS;//输入结构
struct MeshData
{float4 vertex : POSITION;float2 uv1 : TEXCOORD0;float2 uv2 : TEXCOORD1;float4 tangentOS :TANGENT;float3 normalOS : NORMAL;float4 vertexColor : COLOR;
};//传递结构
struct VOutData
{float4 pos : SV_POSITION; // 必须命名为pos ,因为 TRANSFER_VERTEX_TO_FRAGMENT 是这么命名的,为了正确地获取到Shadowfloat2 uv1 : TEXCOORD0;float3 tangentWS : TEXCOORD1;float3 bitangentWS : TEXCOORD2;float3 normalWS : TEXCOORD3;float3 posWS : TEXCOORD4;float3 posOS : TEXCOORD5;float3 normalOS : TEXCOORD6;float4 vertexColor : TEXCOORD7;float2 uv2 : TEXCOORD8;
};//传递结构赋值
VOutData FillBaseV2FData(MeshData input)
{VOutData output;output.pos = UnityObjectToClipPos(input.vertex);output.uv1 = input.uv1;output.uv2 = input.uv2;output.normalWS = normalize(UnityObjectToWorldNormal(input.normalOS));output.posWS = mul(unity_ObjectToWorld, input.vertex);output.posOS = input.vertex.xyz;output.tangentWS = normalize(UnityObjectToWorldDir(input.tangentOS));output.bitangentWS = cross(output.normalWS, output.tangentWS) * input.tangentOS.w; //乘上input.tangentOS.w 是unity引擎的bug,有的模型是 1 有的模型是 -1,必须这么写output.normalOS = input.normalOS;output.vertexColor = input.vertexColor;return output;
}// Hue, Saturation, Value
// Ranges:
//  Hue [0.0, 1.0]
//  Sat [0.0, 1.0]
//  Lum [0.0, HALF_MAX]
// //HSV色彩模型转为RGB色彩模型  FROM::color.hlsl
float3 HSV2RGB( float3 hsv ){const float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);float3 p = abs(frac(hsv.xxx + K.xyz) * 6.0 - K.www);return hsv.z * lerp(K.xxx, saturate(p - K.xxx), hsv.y);
}
//RGB色彩模型转为HSV色彩模型  FROM::color.hlsl
float3 RGB2HSV(float3 rgb)
{float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);float4 p = lerp(float4(rgb.bg, K.wz), float4(rgb.gb, K.xy), step(rgb.b, rgb.g));float4 q = lerp(float4(p.xyw, rgb.r), float4(rgb.r, p.yzx), step(p.x, rgb.r));float d = q.x - min(q.w, q.y);float e = 1.0e-10;return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}//Gooch光照模型  ⻛格化的着⾊模型  强调冷暖色调  FROM::render4th P146
float3 GoochModel(float3 baseColor, float3 highLightColor, float3 normalWs, float3 lightDirWs, float3 viewDirWs){normalWs = normalize(normalWs);lightDirWs = normalize(lightDirWs);float3 coolColor = float3(0,0,0.55) + 0.25*baseColor;float3 warmColor = float3(0.3,0.3,0) + 0.25*baseColor;float size = clamp(100*(dot(reflect(lightDirWs, normalWs), viewDirWs) - 97), 0, 1);float4 halfLambert = dot(normalWs, lightDirWs) * 0.5 + 0.5;return  size*highLightColor + (1-size)*(halfLambert*warmColor + (1 - halfLambert)* coolColor);
}//添加虚拟点光源  _VirtualLightFade越大,衰减越快
float3 CalculatePointVirtualLight(float3 _VirtualLightPos, float3 positionOS, float _VirtualLightFade, float3 _VirtualLightColor)
{float virtualLightDis = distance(_VirtualLightPos,positionOS);float3 virtualLight = exp(-_VirtualLightFade*virtualLightDis)*_VirtualLightColor;//TODO:也许补充方向衰减return virtualLight;
}//切线空间计算视差uv 根据视角方向以深度反追命中点uv
float2 CalculateRealUVAfterDepth(float2 originUV, float3 viewDirTS, float depth)
{//计算视角方向和深度方向的cos值float cosTheta = dot(normalize(viewDirTS), float3(0,0,-1));  //  一般来讲unity是左手坐标系,但在切线和观察空间较为特殊是右手坐标系,不过这并不影响z轴方向的判断//根据深度差算出两点间距离float dis = depth / cosTheta;//算出应用深度差后对应点位float3 originUVPoint = float3(originUV, 0);float3 afrerDepthUVPoint = originUVPoint + normalize(viewDirTS) * dis;//返回应用深度差后对应UVreturn afrerDepthUVPoint.xy;
}
#endif

依赖文件CustomTessellation.cginc:

// Tessellation programs based on this article by Catlike Coding:
// https://catlikecoding.com/unity/tutorials/advanced-rendering/tessellation/
// 关于参数的详细说明
// https://zhuanlan.zhihu.com/p/479792793#include  "Assets/TA/ShaderLib/CgincInclude//CommonCgInc.cginc"//细分切割函数对于每个面片调用一次,对于每一个面片,比如三角形:每条边需要一个切割因子,内部需要一个切割因子
struct TessellationFactors {float edge[3] : SV_TessFactor;  //  对应三条边的切割因子float inside : SV_InsideTessFactor;  //  对应内部的切割因子
};float4 _TessellationUniform;  //  细分因子,当值为1时不发生细分切割。因子可以分别设置,比如边因子是1内部因子是5,那就不会在边上生成顶点,会在中心生成五个顶点
//自定义的细分切割方法,每个面片调用一次
TessellationFactors patchConstantFunction (InputPatch<MeshData, 3> patch)
{TessellationFactors f;f.edge[0] = _TessellationUniform.x;f.edge[1] = _TessellationUniform.y;f.edge[2] = _TessellationUniform.z;f.inside = _TessellationUniform.w;return f;
}

其中,通过

#pragma hull hull
#pragma domain domain

定义hull和domain着色器,其中hull负责切割,domain用于根据权重处理细分后的顶点数据。

hull部分:

//细分切割函数对于每个面片调用一次,对于每一个面片,比如三角形:每条边需要一个切割因子,内部需要一个切割因子
struct TessellationFactors {float edge[3] : SV_TessFactor;  //  对应三条边的切割因子float inside : SV_InsideTessFactor;  //  对应内部的切割因子
};float4 _TessellationUniform;  //  细分因子,当值为1时不发生细分切割。因子可以分别设置,比如边因子是1内部因子是5,那就不会在边上生成顶点,会在中心生成五个顶点
//自定义的细分切割方法,每个面片调用一次
TessellationFactors patchConstantFunction (InputPatch<MeshData, 3> patch)
{TessellationFactors f;f.edge[0] = _TessellationUniform.x;f.edge[1] = _TessellationUniform.y;f.edge[2] = _TessellationUniform.z;f.inside = _TessellationUniform.w;return f;
}//如果不正确配置会报错[UNITY_domain("tri")]  //  正在处理三角形   "tri", "quad", or "isoline"[UNITY_outputcontrolpoints(3)]  //  每个面片输出的顶点为3个[UNITY_outputtopology("triangle_cw")]  //  当创建三角形时应是顺时针还是逆时针,这里应是顺时针  "point", "line", "triangle_cw", or "triangle_ccw"[UNITY_partitioning("integer")]  //  如何细分切割面片   "integer", "pow2", "fractional_even", or "fractional_odd"[UNITY_patchconstantfunc("patchConstantFunction")]  //  细分切割部分还必须提供函数处理,每个面片调用一次MeshData hull (InputPatch<MeshData, 3> patch, uint id : SV_OutputControlPointID)  //  每个顶点调用一次,如果是处理三角形就是调用三次{//  如果_TessellationUniform输入值为1,不产生任何变化,但当输入值为2时,具体发生了什么//  对于一个三角形(因为我们配置的是处理三角形),根据三条边的二等分位置添加额外的一个顶点,如果是3就是三等分位置添加两个顶点//  对于一个三角形(因为我们配置的是处理三角形),根据三条个顶点添加一个中心顶点//  根据patchConstantFunction赋值分配生成顶点的插值权重return patch[id];}

这里做的操作比较少,只是传入每个面片的切割因子,一个面片(三角形)的每条边需要一个切割因子,内部需要一个切割因子,一共四个切割因子。

domain部分:

			//  barycentricCoordinates分别代表各个点的插值权重,每个面片调用一次,对细分后的三角顶点形进行处理(也就是说原顶点不经过此处理?)[UNITY_domain("tri")]  //  正在处理三角形MeshData domain(TessellationFactors factors, OutputPatch<MeshData, 3> patch, float3 barycentricCoordinates : SV_DomainLocation)  {MeshData data;  //  新的插值权重顶点#define MY_DOMAIN_PROGRAM_INTERPOLATE(fieldName) data.fieldName = \patch[0].fieldName * barycentricCoordinates.x + \patch[1].fieldName * barycentricCoordinates.y + \patch[2].fieldName * barycentricCoordinates.z;//对所有信息利用插值权重进行插值计算MY_DOMAIN_PROGRAM_INTERPOLATE(vertex)MY_DOMAIN_PROGRAM_INTERPOLATE(normalOS)MY_DOMAIN_PROGRAM_INTERPOLATE(tangentOS)MY_DOMAIN_PROGRAM_INTERPOLATE(uv1)MY_DOMAIN_PROGRAM_INTERPOLATE(uv2)MY_DOMAIN_PROGRAM_INTERPOLATE(vertexColor)return data;}

这里则是根据传入的边和内部的切割因子的权重,对于新增加的顶点,使用这个方法中的规则填充顶点的对应信息,以完成切割。

MY_DOMAIN_PROGRAM_INTERPOLATE

是定义了一个宏用于重复处理所有的属性,还是要根据实际情况对应处理方法。

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

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

相关文章

HCIP —— BGP 的社团属性

目录 BGP 的社团属性 1.0X00000000 --- internet 2.0XFFFFFF02 --- no - advertise 3.0XFFFFFF01 --- no - export 4.0XFFFFFF03 --- no-export-subconfed 配置&#xff1a; 第一步&#xff1a;使用路由策略执行对流量打上社团属性 第二步&#xff1a;在对等体通告路由之…

【目标检测经典算法】R-CNN、Fast R-CNN和Faster R-CNN详解系列二:Fast R-CNN图文详解

RCNN算法详解&#xff1a;【目标检测经典算法】R-CNN、Fast R-CNN和Faster R-CNN详解系列一&#xff1a;R-CNN图文详解 学习视频&#xff1a;Faster RCNN理论合集 Fast RCNN 概念辨析 1. RoI 在Fast R-CNN中&#xff0c;RoI&#xff08;Region of Interest&#xff0c;感兴…

【数据可视化】动手用matplotlib绘制关联规则网络图

下载文中数据、代码、绘图结果 文章目录 关于数据绘图函数完整可运行的代码运行结果 关于数据 如果想知道本文的关联规则数据是怎么来的&#xff0c;请阅读这篇文章 绘图函数 Python中似乎没有很方便的绘制网络图的函数。 下面是本人自行实现的绘图函数&#xff0c;如果想…

零、自然语言处理开篇

目录 0、NLP任务的基础——符号向量化 0.0 词袋模型 0.1 查表/One-hot编码 0.2 词嵌入模型/预训练模型 0.2.0 Word2Vec &#xff08;0&#xff09;CBOW &#xff08;1&#xff09;Skip-gram 0.2.1 GloVe 0.2.2 WordPiece 0.2.3 BERT 0.2.4 ERNIE NLP学习笔记系列&am…

OKHttpRetrofit

完成一个get请求 1.导入依赖 implementation("com.squareup.okhttp3:okhttp:3.14.")2.开启viewBinding android.buildFeatures.viewBinding true 3.加网络权限 和 http明文请求允许配置文件 <?xml version"1.0" encoding"utf-8"?> &l…

利用国产库libhv动手写一个web_server界面(一)

目录 一.实现要求 流程图 测试libhv中的http服务 1.启动http服务端 2.启动http客户端 3.网址访问 4.状态图 5.时序图 结果展示 1.基本的登录界面 2.简易的配置ip及其端口的界面 3.设置成功后返回 这是一个关于webserver HTTP SERVER http server 模块的制作 一.实…

力扣串题:验证回文串2

整体思路&#xff1a;先找到可能存在问题的点&#xff0c;然后判断&#xff0c;如果一切正常则左指针会来到字符串中部 bool isValidPalindrome(char *s, int i, int j) {while (i < j) {if (s[i] ! s[j]) {return false;}i;j--;}return true; }bool validPalindrome(char …

11.Java---语法总结之一个小项目

图书管理系统 Java学习了很久了,今天将运用之前学习的所有东西整理做个小小的小项目. 1.首先是各种包和操作方法建好 2.然后是项目的大框架搭好 3.然后就开始实现各个部分了 看看最后的运行结果吧! 管理员测试 1.登录&显示图书的运行结果 2.查找&新增图书的运行结…

2024年视频号带货蓝海项目真的可做吗?

在数字经济的浪潮下&#xff0c;视频号带货作为一种新兴的电商模式&#xff0c;近年来备受瞩目。随着5G技术的普及和移动设备的更新换代&#xff0c;视频平台用户规模持续增长&#xff0c;为视频号带货提供了广阔的舞台。然而&#xff0c;面对2024年这个未来节点&#xff0c;我…

9个免费游戏后端平台

在这篇文章中&#xff0c;您将看到 九个免费的游戏服务平台提供商&#xff0c;这可以帮助您开始在线多人游戏&#xff0c;而无需预先投入大量资金。 每个提供商都有非常独特的功能&#xff0c;因此成本应该只是决定时要考虑的方面之一。 我还从低预算项目的角度对免费提供商进…

《JAVA与模式》之原型模式

系列文章目录 文章目录 系列文章目录前言一、原型模式的结构二、简单形式的原型模式三、登记形式的原型模式四、克隆满足的条件五、浅克隆和深克隆前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用…

初识kubernetes

Kubernetes核心概念 Master Master节点主要负责资源调度(Scheduler)&#xff0c;控制副本(Replication Controller)&#xff0c;和提供统一访问集群的入口(API Server)。---核心节点也是管理节点 Node Node是Kubernetes集群架构中运行Pod的服务节点&#xff08;亦叫agent或min…

北京碳中和经营许可证办理条件及流程与我们的专业服务

各位老板好&#xff0c;随着全球气候变化问题的日益严重&#xff0c;碳中和成为了企业社会责任和可持续发展的关键一环。在北京&#xff0c;越来越多的企业开始关注并投入到碳中和的行列中。为了规范市场秩序&#xff0c;确保碳中和活动的合法性和有效性&#xff0c;北京地区实…

Splitpanes拆分窗格插件使用

目录 基本用法 纵向排列 遍历渲染 动态拆分宽度 项目开发中用到了拆分窗格(就是下面的效果&#xff0c;可以拆分网页&#xff0c;我们项目通常都是用左右两块拆分&#xff0c;可以通过拖动图标进行左右拖动)&#xff0c;于是就发现了一个很好用的插件&#xff1a;Splitpane…

Day37:安全开发-JavaEE应用JNDI注入RMI服务LDAP服务JDK绕过调用链类

目录 JNDI注入-RMI&LDAP服务 JNDI远程调用-JNDI-Injection JNDI远程调用-marshalsec JNDI-Injection & marshalsec 实现原理 JNDI注入-FastJson漏洞结合 JNDI注入-JDK高版本注入绕过 思维导图 Java知识点&#xff1a; 功能&#xff1a;数据库操作&#xff0c;文…

一款好用的AI工具——边界AICHAT(三)

目录 3.23、文档生成PPT演示3.24、AI文档翻译3.25、AI翻译3.26、论文模式3.27、文章批改3.28、文章纠正3.29、写作助手3.30、文言文翻译3.31、日报周报月报生成器3.32、OCR-DOC办公文档识别3.33、AI真人语音合成3.34、录音音频总结3.35、域方模型市场3.36、模型创建3.37、社区交…

每日汇评:如果支撑位守住2145美元,黄金可能反弹至纪录高位

金价在周二因美国CPI数据火爆而暴跌后保持稳定&#xff1b; 美元和美债收益率在美元/日元下跌中暂停反弹&#xff1b; 随着美国CPI的出炉&#xff0c;市场焦点转向周四的零售销售和PPI数据&#xff1b; 金价在2160美元附近盘整&#xff0c;周二从2195美元的纪录高位回调约1%。由…

白嫖AWS云服务器,验证、注册指南

背景 不知道你想不想拥有一台属于自己的云服务器呢&#xff0c;拥有一台自己的云服务器可以建站&#xff0c;可以在上面搭建个人博客&#xff0c;今天我就来教大家如何申请亚马逊 AWS 免费云服务器&#xff0c;这个云服务器可以长达12个月的免费。而且到期后可以继续换个账号继…

【阿里云系列】-基于云效构建部署NodeJS项目到ACK

准备工作 01、编写Dockerfile文件可以根据不同的环境&#xff0c;新建不同的Dockerfile文件&#xff0c;比如Dockerfile-PROD # Deliver the dist folder with NginxFROM nginx:stable-alpine ENV LANGC.UTF-8 ENV TZAsia/ShanghaiCOPY dist/ /usr/share/nginx/html COPY ngi…

react的diff源码

react 的 render 阶段&#xff0c;其中 begin 时会调用 reconcileChildren 函数&#xff0c; reconcileChildren 中做的事情就是 react 知名的 diff 过程 diff 算法介绍 react 的每次更新&#xff0c;都会将新的 ReactElement 内容与旧的 fiber 树作对比&#xff0c;比较出它们…