在实时渲染的时候,通常我们会因为带宽,效率等原因,不会采样面数非常多的高精模型建模。但是又想渲染出细节非常多的场景来怎么办?各种办法,动态纹理,法向量纹理,曲面细分等技术是比较常用的技术。这里主要说曲面细分,在DX11渲染管道用了三个stage(阶段)来实现,曲面细分就是在GPU内部使用硬件加速的方式,动态生成顶点。其中:
Hull shader :相当细分前的预处理工作,定制一些细分规则和生成规则。
Tessellator :执行细分操作,根据Hull shader预置的细分规则。
Domain shader :在Tessellator 会生成对应的顶点和线,但是这些个顶点它是相对于控制点的,使用的是(U,V,W)坐标,这一步就是对顶点做一些处理来的。
(或许是涉及到硬件的非通用计算的原因,致使这样一个功能需要整出三个阶段,2个shader,如果只有一个API调用多好)
如果有一天,你在tess的时候失败了,请第一时间思考,是否有别的地方没有使用tess,这是首先需要排查的。
这个图很清晰的描述了整个曲面细分的过程。
1.HS input :第一步不是HS,而是HS之前的input操作,这里有个patch control points(补丁控制点)它是在原语里面设定的,IASetPrimitiveTopology方法D3D11_PRIMITIVE_TOPOLOGY枚举,里面有很多PATCHLIST为1-32。这说明在我们需要进行曲面细分的时候,我们会把原语定义为PATCHLIST系列类型,这个时候传入到vs里面的已经不是普通的顶点,而是补丁控制点,1-32对应的是控制点的个数,一般我们网格是三角形,三个顶点,所以会选择3。
2.HS(Hull Shader) 阶段 :这个阶段会并且的做两件事:1.根据输入的补丁控制点生成新的控制点,然后输出,输出时调用的是DS(Domain Shader),每个点调用一次。2.设置参数,计算的次数方式生成的结果等,参数又如下:
SV_TessFactor :这是个系统语义,定义Tessellation(曲面细分)的每个边缘上的一个补丁,它确定的是边。它的类型有三种:
(1) float[2] :生成的拓扑图为等值线。
float edges[2] : SV_TessFactor; output.edges[0] = tessAmount; // 第一个值在SV_TessFactor的线密度镶嵌的因素 output.edges[1] = tessAmount; // 第二个值是行了详细的Tessellation(曲面细分)系数
(2)float[3] :拓扑图为三角形。
float edges[3] : SV_TessFactor; output.edges[0] = tessAmount; // 第一个组件提供为U = 0边镶嵌因素的补丁 output.edges[1] = tessAmount; // 第二部分提供了镶嵌系数为v = 0边的补丁 output.edges[2] = tessAmount; // 第三部分提供了镶嵌系数为w = 0边的补丁
(3)float[4] :拓扑图为四边形。
float edges[4] : SV_TessFactor; // 为顺时针方向的边缘的排序,从u == 0的边缘,这是的补丁的左侧开始,并从v == 0边缘,这是顶部的补丁。(意思是(0,0)在左上方,u为纵轴,V为横轴) output.edges[0] = tessAmount; // 第一个组件提供为U = 0边镶嵌因素的补丁 output.edges[1] = tessAmount; // 第二部分提供了镶嵌系数为v = 0边的补丁 output.edges[2] = tessAmount; //第三个组成部分为U = 1的边缘镶嵌因素的补丁 output.edges[3] = tessAmount; // 第四部分提供了镶嵌系数为v == 1边缘的补丁
注意:这里必须使用数组float edges[4] ,不能使用float4 edges,上面的一样。另外就是三角形时,用到了W轴,四边形的时候只用到了U,V轴。等值线这个暂时不懂。tessAmount值的区间为[1,8]
SV_InsideTessFactor :这个也是系统语义,定义的是内部的细分规则,等值线不需要设置这个值。
float[2] :拓扑图为四边形。
float :拓扑图为三角形。
[domain("tri")] :这个指定细分的类型。值:quad,tri,isoline
[partitioning("integer")] :// 指定TS阶段细分是按照什么规则细分,一个有四种类型:Fractional_odd(1,3..63),Fractional_even(2,4...64),integer(1,2,3...64),Pow2。有这四种类型的主要原因是奇数生成的图形一致,偶数的一致,但是奇数和偶数的规则不一致。
[outputtopology("triangle_cw")] :指定拓扑类型,这些拓扑信息会被传输到PA block中去。值:line, triangle_cw(逆时针方向三角形), triangle_ccw(???)。
[outputcontrolpoints(3)] // 输出的控制点的数目。
[patchconstantfunc("ColorPatchConstantFunction")] // 当前HS中使用的常量函数,这个函数是自己定义的,引号里面是名字。
//////////////////////////////////////////////////////////////////////////////// // Patch 常量函数,决定tessellation因子,每个patch执行一次,所以是per patch的,不是per 控制点的 //////////////////////////////////////////////////////////////////////////////// ConstantOutputType ColorPatchConstantFunction(InputPatch<HullInputType, 3> inputPatch, uint patchId : SV_PrimitiveID) { ConstantOutputType output; // Set the tessellation factors for the three edges of the triangle. output.edges[0] = tessellationAmount; output.edges[1] = tessellationAmount; output.edges[2] = tessellationAmount; // Set the tessellation factor for tessallating inside the triangle. output.inside = tessellationAmount; return output; }
3.Tessellator :这个阶段就是生成点的阶段,上面已经描述清楚了,照着做就行了,这个阶段不需要做任何事情。
4.DS(Domain Shader):H和T阶段的每个补丁点都会调用一次DS,在DS里面输出最终的点。由于曲面细分会生成很多新的点,所以如果要使用这些shader,最好在DS里面对顶点的坐标进行转换,就是把VS里面的事情拿到DS里面来做。
代码:
//////////////////////////////////////////////////////////////////////////////// // Filename: color.hs //////////////////////////////////////////////////////////////////////////////// ///////////// // GLOBALS // ///////////// cbuffer TessellationBuffer { float tessAmount; float3 padding; }; ////////////// // TYPEDEFS // ////////////// struct HullInputType { float3 hiPosition : POSITION; float3 hiNormal: NORMAL; float3 hiTangent : TANGENT; float3 hiBinormal : BINORMAL; float2 hiTexture : TEXCOORD0; //纹理坐标 }; struct ConstantOutputType { float edges[3] : SV_TessFactor; float inside : SV_InsideTessFactor; }; struct DomainInputType { float3 diPosition : POSITION; float3 diNormal: NORMAL; float3 diTangent : TANGENT; float3 diBinormal : BINORMAL; float2 diTexture : TEXCOORD0; //纹理坐标 }; //////////////////////////////////////////////////////////////////////////////// // Patch 常量函数,决定tessellation因子,每个patch执行一次,所以是per patch的,不是per 控制点的 //////////////////////////////////////////////////////////////////////////////// ConstantOutputType ColorPatchConstantFunction(InputPatch<HullInputType, 3> inputPatch, uint patchId : SV_PrimitiveID) { ConstantOutputType output; // Set the tessellation factors for the three edges of the triangle. output.edges[0] = tessAmount; output.edges[1] = tessAmount; output.edges[2] = tessAmount; // Set the tessellation factor for tessallating inside the triangle. output.inside = tessAmount; return output; } //////////////////////////////////////////////////////////////////////////////// // Hull Shader //////////////////////////////////////////////////////////////////////////////// [domain("tri")] // 这个指定细分的类型,三角形,四边形神马的 [partitioning("integer")] // 指定TS阶段细分是按照什么规则细分,一个有四种类型:Fractional_odd(1,3..63),Fractional_even(2,4...64),integer(1,2,3...64),Pow2 [outputtopology("triangle_cw")] // 细分后的输出语义是逆时针方向三角形,这些拓扑信息会被传输到PA block中去 [outputcontrolpoints(3)] // 输出的控制点的数目,三角形是3个,也是hull shader被调用的次数 [patchconstantfunc("ColorPatchConstantFunction")] // 当前HS中使用的常量函数 上面的那个函数 DomainInputType PortHullShader(InputPatch<HullInputType, 3> patch, uint pointId : SV_OutputControlPointID, uint patchId : SV_PrimitiveID) { DomainInputType output; output.diPosition = patch[pointId].hiPosition; output.diNormal = patch[pointId].hiNormal; output.diTangent = patch[pointId].hiTangent; output.diBinormal = patch[pointId].hiBinormal; output.diTexture = patch[pointId].hiTexture; return output; }
//////////////////////////////////////////////////////////////////////////////// // Filename: color.ds //////////////////////////////////////////////////////////////////////////////// ///////////// // GLOBALS // ///////////// cbuffer MatrixBuffer { matrix mbWorld; matrix mbTransform; }; ////////////// // TYPEDEFS // ////////////// struct ConstantOutputType { float edges[3] : SV_TessFactor; float inside : SV_InsideTessFactor; }; struct DomainInputType { float3 diPosition : POSITION; float3 diNormal: NORMAL; float3 diTangent : TANGENT; float3 diBinormal : BINORMAL; float2 diTexture : TEXCOORD0; //纹理坐标 }; struct PixelInputType { float4 piPosition : SV_POSITION; //float4 color : COLOR; }; //////////////////////////////////////////////////////////////////////////////// // Domain Shader //////////////////////////////////////////////////////////////////////////////// //对三角形u, v, w表示细分点相对于三个控制点的u,v, w坐标,或者说是重心坐标, //我们可以根据这三个值计算出细分点的位置,然后转化为世界坐标系中点, //输出颜色也是用三个控制点的颜色根据u, v, w差值得到。(这个u和v是不是纹理坐标呢?) [domain("tri")] PixelInputType PortDomainShader(ConstantOutputType input, float3 uvwCoord : SV_DomainLocation, const OutputPatch<DomainInputType, 3> patch) { float3 vertexPosition; PixelInputType output; // Determine the position of the new vertex. //Baricentric Interpolation to find each position the generated vertices //基于重心坐标的顶点生成 vertexPosition = uvwCoord.x * patch[0].diPosition + uvwCoord.y * patch[1].diPosition + uvwCoord.z * patch[2].diPosition; // Calculate the position of the new vertex against the world, view, and projection matrices. output.piPosition = mul(float4(vertexPosition, 1.0f), mbTransform); //新生成顶点颜色也为各个控制点颜色组合 //output.color = uvwCoord.x * patch[0].color + uvwCoord.y * patch[1].color + uvwCoord.z * patch[2].color; return output; }
相关推荐
GPU Tessellation网格细分方法实现.pdf
镶嵌 Tessellation 是一个用于 3d 曲面细分的库,例如,它将根据体积的任何隐式函数创建一组三角形。 Tessellation 实现了 。例子创建一个单位球体并细分它: use nalgebra as na;//!struct UnitSphere { bbox : ...
简单的 OpenGL 曲面细分着色器示例用于从点顶点创建四边形的 OpenGL 曲面细分评估着色器的简单示例。 需要 SDL 和 OpenGL 4。 不会在没有修改的情况下在 OS X 之外运行(例如,在运行时手动获取 OpenGL 函数指针。)...
GPU tessellation
D3D11_Tessellation
jfa-cvt 质心 Voronoi 曲面细分的 Jump Flood 算法作者:Bo Zhou, J CUBE Inc. 日本东京简短的这是用于集中式 Voronoi Tessellation ( CVT ) 的 Jump Flood 算法 ( JFA ) 的简单实现,CPU 和 GPU 版本均可用。 您...
它有一些事后诸葛的字头语如“标准三角语言(Standard Triangle Language)”、“标准曲面细分语言(Standard Tessellation Language)”、“立体光刻语言(STereolithography Language)”和“(立体光刻曲面细分语言)”。...
Introduction to 3D Game Programming with Directx12 Chapter14 习题解答源码
GLSL Tessellation Shader简单示例,对一个四边形平面进行双线性插值细分
基于macOS平台,使用Xcode构建的OpenGL4.1 Tessellation Shader使用demo。
采用visual c++ 和 opengl绘制凹多边形的例子
瞬态扩散方程基于质心Voronoi剖分的一种移动有限体积方法,柴笑宇,朱立永,本文提出了一种求解瞬态扩散方程的移动有限体积方法。该方法利用基于质心Voronoi剖分的网格优化来代替经典移动网格方法中的网格移�
We report a quantitative image-processing method to evaluate the spatial distribution uniformity of particles using the Voronoi tessellation and Delaunay triangulation. Given the geometric particle ...
网格化(tessellation)即把凹多边形,包含洞,岛的多边型,或者自交叉多边型划分为简单凸多边形的过程.
采用visual c++ 和 opengl绘制凹多边形的例子
CSCI510---Tessellation
tessellation-case
在 Screensaver.java 中使用 main 运行程序。 创建六边形和菱形的镶嵌
Parallax是Kerbal Space Program的自定义地形着色器,结合了细分和位移映射以创建逼真的行星表面。 在此处观看预告片: : 特征 视差完全取代了普通游戏中使用的地形着色器,而具有更多更好的功能,其中包括: ...
现在越来越多的webgis开发都喜欢将面数据变成6边行格网的形式作为一种新效果 这里面包含地理处理工具和地图包 可以使用工具也可以直接运行模型