曲面细分(subdivision)&曲面细分着色器&GPU的LOD

18 篇文章 15 订阅
12 篇文章 6 订阅

曲面细分是指将一个模型的面合理的分成更多小的面,从而提升模型精度,提高渲染效果 曲面简化是指将一个模型的面合理的合成更少的面,从而降低模型精度,为特定情形下提供使用(如LOD技术) 。这一过程是可以在CPU中完成的,但组织多个点的动态添加和删除,并且更新同步到着色器所需的vb中,这一过程比较消耗性能,并且实现起来也不能算是特别容易的事情。一个比较理想的做法应该是在GPU硬件中完成相关的操作(曲面细分做色器),并且给用户提供一些可编程的接口。

曲面细分着色器(GPU):

带着这一目的,DirectX 11引入了新特性——GPU曲面细分。之后,OpenGL4之后的版本也推出了曲面细分的相关硬件支持(OpenGL ES 3.2)。
Tesselation shader也可以翻译为镶嵌化技术;他的用途包括而不限于曲线曲面、头发、场地;
在这里插入图片描述

曲面细分着色器将复杂的曲面转换为简单的点,线,三角形等。它分为三部分:曲面细分控制着色器(Tessellation Control Shaders),曲面细分引擎(The Tessellation Engine),曲面细分求值着色器(Tessellation Evaluation Shaders)。

曲面细分控制着色器负责确定曲面细分等级。gl_TessLevelInner和gl_TessLevelOuter是内部变量,分别表示内外曲面细分等级(等级越高细分越精细)。gl_in和gl_out也是内部变量,分别表示输入输出数据。gl_InvocationID表示索引

曲面细分引擎是固定管线,将复杂的曲面转换为简单的点,线,三角形等。
曲面细分求值着色器(曲面细分评估着色器)
在细分后生成的顶点上都运行该着色器。所以,当曲面细分的精度越高,生成的顶点就越多,该着色器运行的次数就越多。其中设置细分模式的语句如下:

layout (triangles, equal_spacing, cw)  in

表示以等分边界的方式,顺时针生成三角形。 gl_TessCoord表示生成顶点的重心。

曲面细分算法:

在这里插入图片描述
当然除了细分与简化之外,还有另外一种同属一类的操作叫做曲面规则化(Mesh Regularization)其所作的便是将三角面都变的尽可能相同,从而也达到提升模型效果的目的
在这里插入图片描述

简单的Loop细分算法(Loop Subdivision):

(这里的loop是人名,可不是循环的意思噢) Loop细分是一种专门针对三角形面的细分方法,其核心步骤也十分容易理解, 连接每条边的中点生成一个新的三角形,原来的三角形就会被分割成4个三角形。
在这里插入图片描述

将所有的顶点分为两类,一类是新生成的顶点,一类是老的原来就有的顶点,对于新生成的顶点做如下处理这里新的顶点就是白色的那个顶点,其位置为周围4个顶点的权重之和,各顶点权重如图所示,其余边上的新顶点处理类似。
在这里插入图片描述
对于旧的顶点,做如下处理:
在这里插入图片描述

GPU的LOD:

一般的lod,主要是cpu的静态lod
引擎的地形分两步

  1. CPU中发生LOD以确定数据进到GPU中(比如现有的技术3Dtiles)!
  2. LOD 需要按照交互率去draw三角形;

当然基于以前CPU的LOD是会降低保真度,然而基于GPU的曲面细分着色器实现LOD能克服上述问题,在工程上最大的亮点就是虚幻的nanite技术就是基于此来实现的(下面以unity的曲面细分为例子)!

曲面细分策略:

固定量的曲面细分: 模型的面在屏幕上的大小大致相同,则向网格添加固定量的曲面细分(整个网格上的曲面细分级别相同);

基于距离的曲面细分:

  1. 曲面细分处于最大值时的距离(例如,10 米)。
  2. 曲面细分级别递减时的距离(例如,20 米)。

曲面细分函数在曲面细分之前将三角形三个角的顶点数据作为其三个参数。Unity 需要此数据来计算曲面细分级别,而这取决于顶点位置

基于边长的曲面细分:
纯粹基于距离的曲面细分仅在三角形大小非常相似时才能起到有效的作用。在具有小三角形的游戏对象被过度曲面细分,而具有大三角形的游戏对象则曲面细分不足。一种改善方法是根据屏幕上的三角形边长来计算曲面细分级别。Unity 应该将更大的细分因子应用于更长的边。

Phong曲面细分:
将修改细分面的位置,使得生成的表面稍微跟随网格法线。这是使简单多边形网格变得更加平滑的一种非常有效的方法。

 Shader "Unity Shaders Book/myshader/Tessellation Sample" {
            Properties {
                _MainTex ("Base (RGB)", 2D) = "white" {}
                _DispTex ("Disp Texture", 2D) = "gray" {}
                _NormalMap ("Normalmap", 2D) = "bump" {}
                _Displacement ("Displacement", Range(0, 1.0)) = 0.3
                _Color ("Color", color) = (1,1,1,0)
                _SpecColor ("Spec color", color) = (0.5,0.5,0.5,0.5)
            }
            SubShader {
                Tags { "RenderType"="Opaque" }
                LOD 300
            
                CGPROGRAM
                #pragma surface surf BlinnPhong addshadow fullforwardshadows vertex:disp nolightmap
                #pragma target 4.6

                struct appdata {
                    float4 vertex : POSITION;
                    float4 tangent : TANGENT;
                    float3 normal : NORMAL;
                    float2 texcoord : TEXCOORD0;
                };

                sampler2D _DispTex;
                float _Displacement;

                void disp (inout appdata v)
                {
                    float d = tex2Dlod(_DispTex, float4(v.texcoord.xy,0,0)).r * _Displacement;
                    v.vertex.xyz += v.normal * d;
                }

                struct Input {
                    float2 uv_MainTex;
                };

                sampler2D _MainTex;
                sampler2D _NormalMap;
                fixed4 _Color;

                void surf (Input IN, inout SurfaceOutput o) {
                    half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
                    o.Albedo = c.rgb;
                    o.Specular = 0.2;
                    o.Gloss = 1.0;
                    o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_MainTex));
                }
                ENDCG
            }
            FallBack "Diffuse"
        }

unity效果图
phong的曲面细分

参考资料

支持 DX11/OpenGL Core 曲面细分的表面着色器 - Unity 手册
地形材质 | 虚幻引擎文档
如何在一瞬间画出几十亿三角形?关于UE5 的深度探讨
《深入理解opengl、webgl、opengles》第六、第十章节

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对OpenMV,可以使用其图像处理和计算功能来实现线段细分。OpenMV是一款嵌入式视觉开发板,具有强大的图像处理能力。 以下是一个示例代码,演示如何在OpenMV上将线段细分为指定数量的小线段: ```python import sensor, image # 初始化摄像头 sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QVGA) sensor.skip_frames(time = 2000) # 从图像中提取线段 def extract_line_segments(img): img.lens_corr(1.8) # 适应镜头畸变 img.to_grayscale() # 转为灰度图像 img.find_edges(image.EDGE_CANNY) # 使用Canny算子检测边缘 return img.find_line_segments() # 将线段细分为指定数量的小线段 def subdivide_line_segment(line, num_subdivisions): start_x, start_y, end_x, end_y = line.x1(), line.y1(), line.x2(), line.y2() segment_length = ((end_x - start_x)**2 + (end_y - start_y)**2)**0.5 subdivision_length = segment_length / num_subdivisions points = [(start_x, start_y)] for i in range(1, num_subdivisions): t = i * subdivision_length / segment_length x = int((1 - t) * start_x + t * end_x) y = int((1 - t) * start_y + t * end_y) points.append((x, y)) points.append((end_x, end_y)) return points # 主循环 while(True): img = sensor.snapshot() # 获取图像 lines = extract_line_segments(img) # 提取线段 for line in lines: points = subdivide_line_segment(line, 5) # 将线段细分为5个小线段 for i in range(len(points) - 1): img.draw_line(points[i], points[i + 1], color=(255, 0, 0), thickness=2) # 在图像上绘制小线段 img.show() # 显示图像 ``` 这段代码使用OpenMV库中的函数提取图像中的线段,并使用自定义函数将每个线段细分为5个小线段。然后,它在图像上绘制这些小线段。 请注意,这只是一个简单的示例,以展示OpenMV的线段细分功能。具体的应用场景和需求可能需要更复杂的算法和处理步骤。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值