大数据技术与实践实验报告总结_True Impostor技术原理总结与实践

ca5743b88b7f710a29d5566b86a0736c.png

True Impostor技术原理总结与实践

基本介绍

Impostor,原意“伪装者”,是一种使用极简单的mesh来模拟真实mesh模型的一种优化技术,可以高效的在场景中绘制大量同类的模型而不需要绘制大量的多边形。Impostor技术是介于Billboard和mesh之间的一种模型,在节省顶点的同时实现模型全角度细节的展示。Impostor将2维纹理映射到一张矩形mesh上,模拟高精度模型的假象,实现方法可以是实时的,也可以预先计算好数据并保存在内存中。Impostor模型实际上只在某些角度下是完全精确的(采样角度),在相机移动角度改变的过渡过程中精度必定会降低,直到切换到另一个完全精确的角度。 Impostor支持原模型的自发光、反射、折射等特性,在采样足够的情况下基本可以以假乱真。

Billboard,Multi-Billboard

传统的Billboard公告牌技术原理很多简单,使用一张四边形mesh来展示一张纹理贴图,并监视相机的位置,通过差积运算得到方向向量让mesh始终朝向相机,实现伪装模型的效果,这种方法在远处的且是Y轴中心对称模型的情况下效果不错,例如一些单一的树木或者草。

单一的Billboard只能展示模型的一个面,之后衍生出像星形billboard的方法,例如模拟某种草,将多个billboard星状叠插在一起,展示模型更复杂的细节。与此同时随着叠加的billboard的数量增多,顶点的数量也会增多,mesh也会更加复杂,增加内存消耗。

Billboard只适用于远处的对模型细节要求不高的情况,可以显著降低性能要求。Billboard也是在很多需要降低性能要求,绘制大规模模型的情况下(例如草原、森林)普遍应用的一种方法,是一种image based renderring的思路。此外这种简单的billboard难以支持真正的光照阴影等效果。

Impostor纹理空间

纹理空间映射是Impostor技术的基础。映射主要分成三种:Spherical(球面空间),Octahedron(八面体空间),Hemi-Octahedron(半八面体空间)。

空间映射实现了二维数据和三维数据的相互转换,即数据的压缩解压过程。Impostor采样采集的实际是三维数据,但要通过压缩保存到一张二维纹理贴图上,最后在绘制Impostor的时候需要解压二维纹理数据实现不同角度展示对应的模型贴图。

如下图展示的是Spherical球面空间二维纹理的映射,在同一纬度上对应顶点的数量是相同的,导致越接近两极顶点越密集,两极的顶点对应在二维纹理的上下两边缘,顶点分布不均匀,导致纹理上下两边缘部分的数据过于密集大量重复造成精度浪费。

fa67da24871c01c60d5e31c47665e80c.png

因此为了节约精度,会选择另外两种映射方法Octahedron和Hemi-Octahedron。

下面的gif动画展示了这两种空间映射的直观过程。可见这两种映射空间上顶点分布很均匀,避免了球面空间映射造成的精度浪费。此外,关于这两种的选择也要分情况:对于那些不需要展示模型底部的情况,为了进一步优化推荐选择Hemi-Octahedron空间映射。而如果需要全方位展示模型则只能选择Octahedron。

5749b03d2c9e410ec807eba2c5975ed2.gif

例如下面代码是Octahedron空间纹理映射变换函数:

float2 VectortoOctahedron( float3 N )
            {
                N /= dot( 1.0, abs( N ) ); // 等同于:N/= abs(N.x)+abs(N.y)+abs(N.z)
                if( N.z <= 0 )
                {
                    N.xy = ( 1 - abs( N.yx ) ) * ( N.xy >= 0 ? 1.0 : -1.0 );
                }
                return N.xy;
            }

            float3 OctahedronToVector( float2 Oct )
            {
                float3 N = float3( Oct, 1.0 - dot( 1.0, abs( Oct ) ) );
                if(N.z< 0 )
                {
                    N.xy = ( 1 - abs( N.yx) ) * (N.xy >= 0 ? 1.0 : -1.0 );
                }
                return normalize( N);
            }

使用示例:VectortoOctahedron(objectCameraDirection.xzy)*0.5 + 0.5,其中objectCameraDirection是模型坐标系camear相对于模型的方向向量,计算结果即是二维纹理上对应的纹理坐标。

Impostor空间烘焙采样

Impostor采样主要根据设置的采样精度、纹理空间映射算法等均匀的从不同角度对模型进行截取快照,输出diffuse贴图(物体颜色和透明度)、法线贴图(用于之后计算光照阴影等)、Mask贴图(用于clip裁剪掉背景)等通道数据(一般设置的情况)。

Amplify Impostor插件烘焙Impostor的方法很简单,在模型上加上AmplifyImpostor.cs脚本,创建Impostor对象并设置到Impostor Assets上,然后设置纹理分辨率(Texture Size)、理采样帧数(Axis Frames),编辑Imspostor的mesh形状(Billboard mesh),以及选择烘焙设置(Bake Preset)等。最后点击Bake Impostor就可以实现一键烘焙。

6c7614c1aaab9639457880290c90dd0c.png

例如选择基本的Custom Preset输出的结果为三张贴图:AlbedoAlpha、NormalDepth、Mask。 插件提供的另一种用于标准延迟渲染通道的StandardDefferd设置输出则为四张贴图:AlbedoAlpha、NormalDepth、SpecularSmoothness、EmissionOcclusion。

Amplify Impostor基本使用教程

视差映射POM

纹理空间是通过多角度采样得到的不同角度的纹理贴图,然后根据相机的位置来更换不同的贴图,但是在贴图直接更换的瞬间会有不连续的突变,为了实现平滑的过渡达到True Impostor的效果,需要加入视差映射POM(Parallax mapping)技术。

视差映射方法的基本原理是,根据当前相机位置确定当前对应的采样贴图片段,然后将当前贴图临近的贴图进行加权融合采样,实现平滑过渡,达到接近真实模型的效果。以Octahedron空间为例,每个贴图片段邻近的有三个片段,对三个邻近片段进行加权融合得到最终的采样颜色。

对整个纹理空间进行POM是一个比较耗费的过程,一般至少需要9x9的采样空间才能实现平滑过渡。

Billborad Impostor简化版实现

Impostor技术通过多角度采样纹理空间变换加上视差纹理映射POM实现Impostor模型不同视角的平滑展示。但是POM是一个比较费的过程,在游戏中如果不需要用Impostor来展示近处的细节模型(近处的模型直接替换真实的mesh模型),则可以去掉POM过程,在相机角度变换时直接替换纹理贴图即可。

Demo中使用AmplifyImpostor插件烘焙出三张贴图(bake preset选择custom)作为Impostor数据,demo中的BillboardImpostor.shader实现了简化版的Impostor,只加入了简单的漫反射效果。

完整代码如下:

// 用来展示BillboardImpostor模型
Shader "ImpostorDemo/BillboardImpostor"
{
    Properties
    {
        [NoScaleOffset]_Albedo("Impostor Albedo & Alpha", 2D) = "white" {}
        [NoScaleOffset]_Normals("Impostor Normal & Depth", 2D) = "white" {}
        [NoScaleOffset]_Mask("Mask", 2D) = "white" {}
        [HideInInspector]_AI_Frames("Impostor Frames", Float) = 0
        [HideInInspector]_AI_ImpostorSize("Impostor Size", Float) = 0
        _AI_Clip("Impostor Clip", Range( 0 , 1)) = 0.5

        _DiffuseColor ("DiffuseColor", Color) = (1,1,1,1)
        _Diffuse ("Diffuse", Range(0,1)) = 1.0
    }

    SubShader
    {
        CGINCLUDE
        #pragma target 3.0
        #define UNITY_SAMPLE_FULL_SH_PER_PIXEL 1
        ENDCG
        Tags { "RenderType"="Opaque" "Queue"="Geometry" "DisableBatching"="True" "ImpostorType"="Octahedron" }
        Cull Back
        Pass
        {
            ZWrite On
            Name "ForwardBase"
            Tags { "LightMode"="ForwardBase" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            uniform sampler2D _Albedo; // 颜色
            uniform sampler2D _Normals; // 法线
            uniform sampler2D _Mask;// mask
            uniform float _AI_Frames; // 切片规模
            uniform float _AI_ImpostorSize; // impostor resolution
            uniform float _AI_Clip; // 透明背景裁剪程度

            // 漫反射强度
            uniform float4 _DiffuseColor;
            uniform float _Diffuse;

            // Octahedron空间纹理映射变换
            float2 VectortoOctahedron( float3 N )
            {
                N /= dot( 1.0, abs( N ) ); // 等同于:N/= abs(N.x)+abs(N.y)+abs(N.z)
                if( N.z <= 0 )
                {
                    N.xy = ( 1 - abs( N.yx ) ) * ( N.xy >= 0 ? 1.0 : -1.0 );
                }
                return N.xy;
            }

            float3 OctahedronToVector( float2 Oct )
            {
                float3 N = float3( Oct, 1.0 - dot( 1.0, abs( Oct ) ) );
                if(N.z< 0 )
                {
                    N.xy = ( 1 - abs( N.yx) ) * (N.xy >= 0 ? 1.0 : -1.0 );
                }
                return normalize( N);
            }

            inline void OctaImpostorVertex( inout appdata_full v, inout float4 uvsFrame1)
            {
                float framesXY = _AI_Frames;
                float prevFrame = framesXY - 1;
                float2 fractions = 1.0 / float2( framesXY, prevFrame );
                float fractionsFrame = fractions.x;
                float fractionsPrevFrame = fractions.y;
                float UVscale = _AI_ImpostorSize;

                float3 worldOrigin = float3(unity_ObjectToWorld[0].w, unity_ObjectToWorld[1].w, unity_ObjectToWorld[2].w);

                float3 worldCameraPos = _WorldSpaceCameraPos;

                // 模型空间:origin到相机的方向
                float3 objectCameraDirection = normalize( mul( worldCameraPos - worldOrigin, (float3x3)unity_WorldToObject ) );
                // 模型空间:相机的位置
                float3 objectCameraPosition = mul( unity_WorldToObject, float4( worldCameraPos, 1 ) ).xyz;
                float3 upVector = float3( 0,1,0 );
                //float3 objectCameraDirection = UNITY_MATRIX_V[2].xyz;

                // 模型水平竖直向量
                float3 objectHorizontalVector = normalize( cross( objectCameraDirection, upVector ) );
                float3 objectVerticalVector = cross( objectHorizontalVector, objectCameraDirection );

                float2 uvExpansion = ( v.texcoord.xy - 0.5f ) * UVscale;
                float3 billboard = objectHorizontalVector * uvExpansion.x + objectVerticalVector * uvExpansion.y;

                float2 cameraPos = VectortoOctahedron(objectCameraDirection.xzy)*0.5 + 0.5;
                float colFrame = round(abs(cameraPos.x) * (framesXY-1));
                float rowFrame = round(abs(cameraPos.y) * (framesXY-1));
                // 纹理坐标
                uvsFrame1 = 0;
                uvsFrame1.xy = (v.texcoord.xy + float2(colFrame, rowFrame)) * fractionsFrame;

                // 顶点
                v.vertex.xyz = billboard;
                v.normal.xyz = objectCameraDirection;
            }

            struct v2f_surf {
                UNITY_POSITION(pos);
                float4 UVsFrame117 : TEXCOORD5;
                float4 viewPos17 : TEXCOORD6;
            };

            // 顶点
            v2f_surf vert (appdata_full v ) {
                UNITY_SETUP_INSTANCE_ID(v);
                v2f_surf o;
                UNITY_INITIALIZE_OUTPUT(v2f_surf,o);
                UNITY_TRANSFER_INSTANCE_ID(v,o);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
                OctaImpostorVertex( v, o.UVsFrame117 ); ///
                o.pos = UnityObjectToClipPos(v.vertex); // 顶点投影坐标

                // 漫反射
                float3 worldlight = normalize(_WorldSpaceLightPos0.xyz);
                float3 worldnormal = normalize(mul((float3x3)unity_ObjectToWorld,v.normal));
                float3 diffuse = _LightColor0.rgb * _DiffuseColor * _Diffuse * saturate(dot(worldnormal,worldlight));
                o.viewPos17 = float4(diffuse,0);
                return o;
            }

            // 片段着色
            fixed4 frag (v2f_surf IN) : SV_Target {             
                float4 blendedAlbedo = tex2D( _Albedo, float3( IN.UVsFrame117.xy, 0) );
                float alpha = blendedAlbedo.a - _AI_Clip;
                clip(alpha);
                return (blendedAlbedo + IN.viewPos17);
            }
            ENDCG
        }

    }
}

Demo场景TreeForest.scene中使用Billborad Impostor技术实现了简单的森林场景,制作了树木、石头和草的Impostor,近处展示真实的模型,远处展示Impostor模型。

88e0a25280a3b3b241361f3110d8b0e1.png

总结

Impostor是一个介于billboard和mesh模型直接的一个技术产物,用于权衡增加模型细节和减少顶点数量的矛盾,加上这里实现的简化版本Billborad Impostor,他们之间的关系如下(3d模型细节从少到多):

Billborad --- Multi Billborad --- BillBoard Impostor --- True Impostor --- Mesh(model)

参考文章

Octahedral Impostor from Ryan Brucks

True Impostors - GPU Gems 3

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 支持向量机非线性回归通用MATLAB程序解析 #### 一、概述 本文将详细介绍一个基于MATLAB的支持向量机(SVM)非线性回归的通用程序。该程序采用支持向量机方法来实现数据的非线性回归,并通过不同的核函数设置来适应不同类型的数据分布。此外,该程序还提供了数据预处理的方法,使得用户能够更加方便地应用此程序解决实际问题。 #### 二、核心功能与原理 ##### 1. 支持向量机(SVM) 支持向量机是一种监督学习模型,主要用于分类和回归分析。对于非线性回归任务,SVM通过引入核技巧(kernel trick)将原始低维空间中的非线性问题转换为高维空间中的线性问题,从而实现有效的非线性建模。 ##### 2. 核函数 核函数的选择直接影响到模型的性能。本程序内置了三种常用的核函数: - **线性核函数**:`K(x, y) = x'y` - **多项式核函数**:`K(x, y) = (x'y + 1)^d` - **径向基函数(RBF)**:`K(x, y) = exp(-γ|x - y|^2)` 其中RBF核函数被广泛应用于非线性问题中,因为它可以处理非常复杂的非线性关系。本程序默认使用的是RBF核函数,参数`D`用于控制高斯核函数的宽度。 ##### 3. 数据预处理 虽然程序本身没有直接涉及数据预处理的过程,但在实际应用中,对数据进行适当的预处理是非常重要的。常见的预处理步骤包括归一化、缺失值处理等。 ##### 4. 模型参数 - **Epsilon**: ε-insensitive loss function的ε值,控制回归带宽。 - **C**: 松弛变量的惩罚系数,控制模型复杂度与过拟合的风险之间的平衡。 #### 三、程序实现细节 ##### 1. 函数输入与输出 - **输入**: - `X`: 输入特征矩阵,维度为(n, l),其中n是特征数量,l是样本数量。 - `Y`: 目标值向量,长度为l。 - `Epsilon`: 回归带宽。 - `C`: 松弛变量的惩罚系数。 - `D`: RBF核函数的参数。 - **输出**: - `Alpha1`: 正的拉格朗日乘子向量。 - `Alpha2`: 负的拉格朗日乘子向量。 - `Alpha`: 拉格朗日乘子向量。 - `Flag`: 标记向量,表示每个样本的类型。 - `B`: 偏置项。 ##### 2. 核心代码解析 程序首先计算所有样本间的核矩阵`K`,然后构建二次规划问题并求解得到拉格朗日乘子向量。根据拉格朗日乘子的值确定支持向量,并计算偏置项`B`。 - **核矩阵计算**:采用RBF核函数,通过`exp(-(sum((xi-xj).^2)/D))`计算任意两个样本之间的相似度。 - **二次规划**:构建目标函数和约束条件,使用`quadprog`函数求解最小化问题。 - **支持向量识别**:根据拉格朗日乘子的大小判断每个样本是否为支持向量,并据此计算偏置项`B`。 #### 四、程序扩展与优化 - **多核函数支持**:可以通过增加更多的核函数选项,提高程序的灵活性。 - **自动调参**:实现参数自动选择的功能,例如通过交叉验证选择最优的`Epsilon`和`C`值。 - **并行计算**:利用MATLAB的并行计算工具箱加速计算过程,特别是当样本量很大时。 #### 五、应用场景 该程序适用于需要进行非线性回归预测的场景,如经济预测、天气预报等领域。通过调整核函数和参数,可以有效应对各种类型的非线性问题。 ### 总结 本程序提供了一个支持向量机非线性回归的完整实现框架,通过灵活的核函数设置和参数调整,能够有效地处理非线性问题。对于需要进行回归预测的应用场景,这是一个非常实用且强大的工具。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值