【TA100 】3.3 曲面细分与几何着色器---大规模草渲染

news2024/12/23 14:37:52

一、两者的应用列举

1-1.曲面细分着色器的应用

①海浪、雪地等

在这里插入图片描述

2著名的应用:和置换贴图(DIsplacement mapping,也叫位移贴图)结合使用

在这里插入图片描述
● 使用普通法线的模型,在边缘部分的凹凸感会不理想
● 如果使用置换贴图,因为它是真正改变物体的形状,所以边缘部分的凹凸感就会很真实
● 注意:使用置换贴图,对模型的面数有要求。
○ 正是这个原因,让它和曲面细分着色器有着很好的契合度。

③雪地里出现的脚印**

● 可以用曲面细分着色器进行优化

1-2.为什么不用复杂的模型,而要用曲面细分着色器?

● 曲面细分着色器可以根据距离/一些规则,动态的调整模型的复杂度,带来更好的性能。

2.几何着色器的应用

请添加图片描述

①几何动画

简单的几何动画、甚至可以做一些破碎的效果
②草地等效果(与曲面细分结合)

自定义草的画法,再和曲面细分着色器结合,就可以得到一个可以动态调整草密度的一个草地效果

二、从管线顺序来看

渲染流水线:
在这里插入图片描述

在这里插入图片描述
○ 整体顺序:顶点 → 曲面细分 → 几何 → 片元
■ 曲面细分又分为:Hull shader 、Tessellation Primitive Generator 、 Domain shader
● Hull shader主要作用:定义一些细分的参数(如:每条边上如何细分,内部三角形如何细分)
● Tessellation Primitive Generator,不可编程的
● Domain shader:经过曲面细分着色器细分后的点是位于重心空间的,这部分的作用就是把它转化到我们要用的空间。
○ 在D3D11 和 OpenGL中,名字/叫法有差异,问题不大

三、曲面细分着色器-Tessellation shaderTESS

1.TESS的输入和输出

输入
称为Patch,可以看成是多个顶点的集合,包含每个顶点的属性。(属性是所有顶点共享的,不是每个顶点有独自的属性)
功能
● 将图元进行细分。
○ 图元可以是三角形、矩形等
● 不同的图元,输入参数也不一样。
输出
细分后的顶点

2.TESS的流程

Hull shader → Tessellation Primitive Generator → Domain shader

Hull shader

● 定义细分的参数
○ Tessellation factor
请添加图片描述
○ Inner Tessellation factor
在这里插入图片描述

● (如果需要的话)可以对输入的Patch参数进行改变
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Shader "Unlit/TessShader"
{
    Properties
    {
        _TessellationUniform("TessellationUniform",Range(1,64)) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
        Pass
        {
            CGPROGRAM
            //定义2个函数 hull domain
            #pragma hull hullProgram
            #pragma domain ds
           
            #pragma vertex tessvert
            #pragma fragment frag

            #include "UnityCG.cginc"
            //引入曲面细分的头文件
            #include "Tessellation.cginc" 

            #pragma target 5.0
            
            struct VertexInput
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
            };

            struct VertexOutput
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
            };

            VertexOutput vert (VertexInput v)
            //这个函数应用在domain函数中,用来空间转换的函数
            {
                VertexOutput o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                o.tangent = v.tangent;
                o.normal = v.normal;
                return o;
            }

            //有些硬件不支持曲面细分着色器,定义了该宏就能够在不支持的硬件上不会变粉,也不会报错
            #ifdef UNITY_CAN_COMPILE_TESSELLATION
                //顶点着色器结构的定义
                struct TessVertex{
                    float4 vertex : INTERNALTESSPOS;
                    float3 normal : NORMAL;
                    float4 tangent : TANGENT;
                    float2 uv : TEXCOORD0;
                };

                struct OutputPatchConstant { 
                    //不同的图元,该结构会有所不同
                    //该部分用于Hull Shader里面
                    //定义了patch的属性
                    //Tessellation Factor和Inner Tessellation Factor
                    float edge[3] : SV_TESSFACTOR;
                    float inside  : SV_INSIDETESSFACTOR;
                };

                TessVertex tessvert (VertexInput v){
                    //顶点着色器函数
                    TessVertex o;
                    o.vertex  = v.vertex;
                    o.normal  = v.normal;
                    o.tangent = v.tangent;
                    o.uv      = v.uv;
                    return o;
                }

                float _TessellationUniform;
                OutputPatchConstant hsconst (InputPatch<TessVertex,3> patch){
                    //定义曲面细分的参数
                    OutputPatchConstant o;
                    o.edge[0] = _TessellationUniform;
                    o.edge[1] = _TessellationUniform;
                    o.edge[2] = _TessellationUniform;
                    o.inside  = _TessellationUniform;
                    return o;
                }

                [UNITY_domain("tri")]//确定图元,quad,triangle等
                [UNITY_partitioning("fractional_odd")]//拆分edge的规则,equal_spacing,fractional_odd,fractional_even
                [UNITY_outputtopology("triangle_cw")]
                [UNITY_patchconstantfunc("hsconst")]//一个patch一共有三个点,但是这三个点都共用这个函数
                [UNITY_outputcontrolpoints(3)]      //不同的图元会对应不同的控制点
              
                TessVertex hullProgram (InputPatch<TessVertex,3> patch,uint id : SV_OutputControlPointID){
                    //定义hullshaderV函数
                    return patch[id];
                }

                [UNITY_domain("tri")]//同样需要定义图元
                VertexOutput ds (OutputPatchConstant tessFactors, const OutputPatch<TessVertex,3>patch,float3 bary :SV_DOMAINLOCATION)
                //bary:重心坐标
                {
                    VertexInput v;
                    v.vertex = patch[0].vertex*bary.x + patch[1].vertex*bary.y + patch[2].vertex*bary.z;
			        v.tangent = patch[0].tangent*bary.x + patch[1].tangent*bary.y + patch[2].tangent*bary.z;
			        v.normal = patch[0].normal*bary.x + patch[1].normal*bary.y + patch[2].normal*bary.z;
			        v.uv = patch[0].uv*bary.x + patch[1].uv*bary.y + patch[2].uv*bary.z;

                    VertexOutput o = vert (v);
                    return o;
                }
            #endif

            float4 frag (VertexOutput i) : SV_Target
            {

                return float4(1.0,1.0,1.0,1.0);
            }
            ENDCG
        }
    }
    Fallback "Diffuse"
}

Tessellation Primitive Generator

● 这部分是不可编程、无法控制的
● 进行细分操作

Domain shader

● 对细分后的点进行处理,从重心空间(Barycentric coordinate system)转换到屏幕空间
在这里插入图片描述

Demo

和置换贴图结合
● 基本原理
○ 通过置换贴图的深度,来把顶点沿着它的法线方向进行移动,以此来对mash进行形变。
● 代码部分和上个Demo的区别也就是在顶点shader部分对顶点进行了位移、和一些计算法线的参数。(因为顶点位移后没有对应的法线贴图,所以需要自己计算一下,具体怎么算先不讲,属于置换贴图部分的知识)

//曲面细分Demo2:与置换贴图结合使用
Shader "Unlit/Tess_Diss_Shader"
{
    Properties
    {
        _MainTex("MainTex",2D) = "white"{}
        _DisplacementMap("_DisplacementMap",2D)="gray"{}
        _DisplacementStrength("DisplacementStrength",Range(0,1)) = 0
        _Smoothness("Smoothness",Range(0,5))=0.5
        _TessellationUniform("TessellationUniform",Range(1,64)) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" 
               "LightMode"="ForwardBase"}
        LOD 100
        Pass
        {
            CGPROGRAM
            //定义2个函数 hull domain
            #pragma hull hullProgram
            #pragma domain ds
           
            #pragma vertex tessvert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            //引入曲面细分的头文件
            #include "Tessellation.cginc" 

            #pragma target 5.0
            float _TessellationUniform;
            sampler2D _MainTex;
            float4 _MainTex_ST;

            sampler2D _DisplacementMap;
            float4 _DisplacementMap_ST;
            float _DisplacementStrength;
            float _Smoothness;

            struct VertexInput
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
            };

            struct VertexOutput
            {
                float2 uv : TEXCOORD0;
                float4 pos : SV_POSITION;
                float4 worldPos:TEXCOORD1;
                half3 tspace0 :TEXCOORD2;
                half3 tspace1 :TEXCOORD3;
                half3 tspace2 :TEXCOORD4;
            };

            VertexOutput vert (VertexInput v)
            //这个函数应用在domain函数中,用来空间转换的函数
            {
                VertexOutput o;
                o.uv = TRANSFORM_TEX(v.uv,_MainTex);
                //Displacement
                //由于并不是在Fragnent shader中读取图片,GPU无法获取mipmap信息,因此需要使用tex2Dlod来读取图片,使用第四坐标作为mipmap的level,这里取了0
                float Displacement = tex2Dlod(_DisplacementMap,float4(o.uv.xy,0.0,0.0)).g;
                Displacement = (Displacement-0.5)*_DisplacementStrength;
                v.normal = normalize(v.normal);
                v.vertex.xyz += v.normal * Displacement;

                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex);

                //计算切线空间转换矩阵
                half3 vNormal = UnityObjectToWorldNormal(v.normal);
                half3 vTangent = UnityObjectToWorldDir(v.tangent.xyz);
                //compute bitangent from cross product of normal and tangent
                half tangentSign = v.tangent.w * unity_WorldTransformParams.w;
                half3 vBitangent = cross(vNormal,vTangent)*tangentSign;
                //output the tangent space matrix
                o.tspace0 = half3(vTangent.x,vBitangent.x,vNormal.x);
                o.tspace1 = half3(vTangent.y,vBitangent.y,vNormal.y);
                o.tspace2 = half3(vTangent.z,vBitangent.z,vNormal.z);
                return o;
            }

            //有些硬件不支持曲面细分着色器,定义了该宏就能够在不支持的硬件上不会变粉,也不会报错
            #ifdef UNITY_CAN_COMPILE_TESSELLATION
                //顶点着色器结构的定义
                struct TessVertex{
                    float4 vertex : INTERNALTESSPOS;
                    float3 normal : NORMAL;
                    float4 tangent : TANGENT;
                    float2 uv : TEXCOORD0;
                };

                struct OutputPatchConstant { 
                    //不同的图元,该结构会有所不同
                    //该部分用于Hull Shader里面
                    //定义了patch的属性
                    //Tessellation Factor和Inner Tessellation Factor
                    float edge[3] : SV_TESSFACTOR;
                    float inside  : SV_INSIDETESSFACTOR;
                };

                TessVertex tessvert (VertexInput v){
                    //顶点着色器函数
                    TessVertex o;
                    o.vertex  = v.vertex;
                    o.normal  = v.normal;
                    o.tangent = v.tangent;
                    o.uv      = v.uv;
                    return o;
                }

                //float _TessellationUniform;
                OutputPatchConstant hsconst (InputPatch<TessVertex,3> patch){
                    //定义曲面细分的参数
                    OutputPatchConstant o;
                    o.edge[0] = _TessellationUniform;
                    o.edge[1] = _TessellationUniform;
                    o.edge[2] = _TessellationUniform;
                    o.inside  = _TessellationUniform;
                    return o;
                }

                [UNITY_domain("tri")]//确定图元,quad,triangle等
                [UNITY_partitioning("fractional_odd")]//拆分edge的规则,equal_spacing,fractional_odd,fractional_even
                [UNITY_outputtopology("triangle_cw")]
                [UNITY_patchconstantfunc("hsconst")]//一个patch一共有三个点,但是这三个点都共用这个函数
                [UNITY_outputcontrolpoints(3)]      //不同的图元会对应不同的控制点
              
                TessVertex hullProgram (InputPatch<TessVertex,3> patch,uint id : SV_OutputControlPointID){
                    //定义hullshaderV函数
                    return patch[id];
                }

                [UNITY_domain("tri")]//同样需要定义图元
                VertexOutput ds (OutputPatchConstant tessFactors, const OutputPatch<TessVertex,3>patch,float3 bary :SV_DOMAINLOCATION)
                //bary:重心坐标
                {
                    VertexInput v;
                    v.vertex = patch[0].vertex*bary.x + patch[1].vertex*bary.y + patch[2].vertex*bary.z;
			        v.tangent = patch[0].tangent*bary.x + patch[1].tangent*bary.y + patch[2].tangent*bary.z;
			        v.normal = patch[0].normal*bary.x + patch[1].normal*bary.y + patch[2].normal*bary.z;
			        v.uv = patch[0].uv*bary.x + patch[1].uv*bary.y + patch[2].uv*bary.z;

                    VertexOutput o = vert (v);
                    return o;
                }
            #endif

            float4 frag (VertexOutput i) : SV_Target
            {
                float3 lightDir =_WorldSpaceLightPos0.xyz;
                float3 tnormal = UnpackNormal (tex2D (_DisplacementMap, i.uv));
                half3 worldNormal;
                worldNormal.x=dot(i.tspace0,tnormal);
                worldNormal.y= dot (i.tspace1, tnormal);
                worldNormal.z=dot (i.tspace2, tnormal);
                float3 albedo=tex2D (_MainTex, i.uv). rgb;
                float3 lightColor = _LightColor0.rgb;
                float3 diffuse = albedo * lightColor * DotClamped(lightDir,worldNormal);
                float3 viewDir = normalize (_WorldSpaceCameraPos. xyz-i. worldPos. xyz);
                float3 halfVector = normalize(lightDir + viewDir);
                float3 specular = albedo * pow (DotClamped (halfVector, worldNormal), _Smoothness * 100);
                float3 result = specular + diffuse;
                return float4(result, 1.0);

                return float4(result,1.0);
            }
            ENDCG
        }
    }
    Fallback "Diffuse"
}

四、几何着色器-Geometry shader (GS)

1.GS的输入和输出

输入
● 输入为单个图元(三角形、矩形、线等等)
● 根据不同的图元,shader中会出现不同的顶点数量
输出
● 输出也为图元(一个或者多个)
● 同时还要定义输出的最大顶点数
● 输出的图元需要自己一个点一个点的自己去构建,顺序很重要(这个着色器最主要的功能:自己构建图元)

2.流程

● 输入输出结构
● 定义最大输出定点数
● 几何着色器

五、其他资料
● https://www.cnblogs.com/mazhenyu/p/3831986.html几何着色器
● catlike-曲面细分着色器:
○ https://catlikecoding.com/unity/tutorials/advanced-rendering/tessellation/

后续贴个实现链接

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

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

相关文章

HarmonyOS学习路之开发篇—Java UI框架(自定义组件与布局 二)

自定义布局 当Java UI框架提供的布局无法满足需求时&#xff0c;可以创建自定义布局&#xff0c;根据需求自定义布局规则 常用接口 Component类相关接口 接口名称 作用 setEstimateSizeListener 设置测量组件的侦听器 setEstimatedSize 设置测量的宽度和高度 onEstima…

极速了解GPT生态

第一部分&#xff1a; 1、chatGPT:一个大语言模型&#xff0c;可以通过API去访问&#xff0c;下面很多是根据API去访问&#xff0c;然后来进行集成。 2、Vector store&#xff0c;你也可以叫是Vector search&#xff0c;主要目就是存储各种向量&#xff0c;然后去计算向量的各…

【Linux】基础IO——文件描述符:缓冲区的理解

上个月学校考试&#xff0c;进行课程复习&#xff0c;一直没有更新博客&#xff0c;现考试结束&#xff0c;继续保持更新&#xff0c;欢迎大家关注&#xff01; 目录 1 模仿C库自主封装简单的文件接口2 对缓冲区的理解2.1 数据刷新到磁盘的过程分析2.2 如何强制刷新内核 1 模仿…

00后是真的卷不过,工作没两年,跳槽到我们公司起薪17K都快接近我了

在程序员职场上&#xff0c;什么样的人最让人反感呢? 是技术不好的人吗?并不是。技术不好的同事&#xff0c;我们可以帮他。 是技术太强的人吗?也不是。技术很强的同事&#xff0c;可遇不可求&#xff0c;向他学习还来不及呢。 真正让人反感的&#xff0c;是技术平平&…

go+vue自建运维管理平台

文章目录 鲁班运维平台容器管理集群管理namespace管理节点管理工作负载存储管理网络管理配置管理事件中心 kuboard 鲁班运维平台 这个平台和spug很像&#xff0c;感觉就像是spug运维平台的容器版本。 但是如果是容器平台则选择的余地很大&#xff0c;优秀的如kubersphere、kub…

NeRF+SLAM论文阅读笔记

CVPR 2023 Co-SLAM: Joint Coordinate and Sparse Parametric Encodings for Neural Real-Time SLAM input&#xff1a; RGB-D contribution&#xff1a; 1.场景表示&#xff1a;多分辨率哈希网格&#xff08;加速&保留高频特征&#xff09; 2.编码方式&#xff1a;one-b…

STM32 实现简单定时任务调度器,动态创建任务,两种思路实现流水灯

代码实现和硬件没关系&#xff0c;所以并不限于STM32&#xff0c;Arduino 之类的其他地方也能用&#xff0c;只要有一个能获取时间的函数就行&#xff0c;或者说&#xff0c;只要有一个会随着时间自动增加的变量就行&#xff0c;时间单位无所谓&#xff0c;所以确实想的话&…

排它锁和共享锁.md

介绍 排它锁&#xff08;Exclusive&#xff09;&#xff0c;又称为X 锁&#xff0c;写锁。 共享锁&#xff08;Shared&#xff09;&#xff0c;又称为S 锁&#xff0c;读锁。 X和S锁之间有以下的关系&#xff1a; SS可以兼容的&#xff0c;XS、SX、XX之间是互斥的 显式加锁…

Django-带参数的路由编写(一)【不用正则表达式匹配的简单带参数路由】

在某urls.py文件有如下的路由配置语句&#xff1a; urlpatterns [path(app2/show/<int:id>/,views.show_id), ]语句&#xff1a; path(app2/show/<int:id>/,views.show_id),中的<int:id>就是带参数的URL中的参数部分&#xff0c;其语法格式如下&#xff1a…

PPT中如何做出透视圆的效果?

看两个例子 一个是上部这种垂直的圆环。 一个是下部这种圆。 它们都据有一定的透视感,上部用于表示流量,下部用于表示出“某一领域”的意镜。 向下延展的圆环透视效果 先说这个扁平的圆的例子,它有4个圆,画的技巧如下: 就是4个圆环;把4个圆环互相叠加;把上和下在“中…

2023年网络安全保姆级入门学习路线,建议收藏!

作为一个工作多年的网络安全渗透工程师&#xff0c;我知道对于零基础小白来说&#xff0c;网络安全可能是一个非常陌生而且有些恐怖的领域。但是不用担心&#xff0c;只要你愿意花费时间和精力去学习&#xff0c;你也能成为一个优秀的网络安全专家。 网络安全学习路线 首先&a…

顺序队列和链队列

队列也是一种线性结构&#xff0c;不同于栈的是队列为先进先出的数据结构&#xff0c;遵循一边入队一边出队。 顺序队列的底层使用的是数组&#xff0c;因此需预先申请一块足够大的内存空间初始化顺序队列。除此之外&#xff0c;为了满足顺序队列中数据从队尾进&#xff0c;队头…

接口测试和功能测试的区别

目录 前言&#xff1a; 一、测试目的不同 二、测试内容不同 三、测试重点不同 四、总结 前言&#xff1a; 接口测试和功能测试是软件测试中的两种不同类型。接口测试侧重于测试不同模块之间的接口&#xff0c;而功能测试则注重测试完整的业务功能。 一、测试目的不同 接…

Vue中如何进行文件打印与PDF导出

Vue中如何进行文件打印与PDF导出 在Vue应用中&#xff0c;有时候需要将页面内容打印出来或者导出为PDF格式&#xff0c;以满足用户的需求。本文将介绍如何在Vue应用中实现文件打印和PDF导出的功能。 文件打印 文件打印是指将页面内容输出到打印机上&#xff0c;将其打印成纸质…

Vue中如何进行地图热点展示与交互(如热力图)

Vue中如何进行地图热点展示与交互&#xff08;如热力图&#xff09; 随着大数据和可视化技术的发展&#xff0c;地图热点展示越来越受到人们的关注。在Vue应用中&#xff0c;我们通常需要实现地图热点的展示和交互&#xff0c;以便更好地呈现数据和分析结果。本文将介绍在Vue中…

MySQL的高级操作(每一次「欢喜」都值得纪念)

文章目录 一、案例扩展二、克隆表1、方法一2、方法二 三、清空表四、创建临时表七、补充七、补充 一、案例扩展 use kgc; create table if not exists info ( id int(4) zerofill primary key auto_increment, #指定主键的第二种方式 name varchar(10) not null default 匿…

eclipse中java代码在控制台输出的中文内容是乱码怎么解决

eclipse中创建了一个maven工程&#xff0c;用System.out在控制台输出内容&#xff0c;但中文内容显示乱码&#xff1a; 解决方法&#xff1a; 右键单击工程&#xff0c;选择Run As->Run Configurations: 点击Common这个tab页&#xff0c;Encoding选择Use system encoding&…

分布式定时任务框架 PowerJob

业务背景 1.1 为什么需要使用定时任务调度 &#xff08;1&#xff09;时间驱动处理场景&#xff1a;整点发送优惠券&#xff0c;每天更新收益&#xff0c;每天刷新标签数据和人群数据。 &#xff08;2&#xff09;批量处理数据&#xff1a;按月批量统计报表数据&#xff0c;批…

vue练习

附加练习-1.帅哥美女走一走 目标: 点击按钮, 改变3个li的顺序, 在头上的就到末尾. 提示: 操作数组里的顺序, v-for就会重新渲染li 代码演示 <template><div><ul><li v-for"item in myArr" :key"item">{{ item }}</li></…

GCC命令与参数详解

GCC 命令与参数详解 无论是 C 还是 C 程序&#xff0c;将其从源代码转变为可执行代码的过程&#xff0c;具体可分为预处理 Preprocessing&#xff0c;编译 Compilation&#xff0c;汇编 Assembly&#xff0c;链接 Linking 这四个阶段。 默认情况下 GCC 指令会直接将源代码历经…