第3章 Unity 3D着色器系统

news2024/11/25 7:06:10

3.1 从一个外观着色器程序谈起

        新建名为basic_diffuse.shader的文件,被一个名为basic_diffuse.mat的材质文件所引用,而basic_diffuse.mat文件则被场景中名为Sphere的game object的MeshRenderer组件所使用。

basic_diffuse.shader代码文件的内容如下所示。

Shader "Custom/BasicDiffuse"

{

    Properties

    {

        _EmissiveColor ("Emissive Color", Color) = (1,1,1,1)

        _MainTex ("Main Texture", 2D) = "white"{}

    }

    SubShader

    {

        Tags { "RenderType"="Opaque" "RenderType"="Opaque" }

        LOD 200

        CGPROGRAM

        #pragma surface surf BasicDiffuse vertex:vert finalcolor:final noforwardadd

        #pragma debug

        // 指定自身的自发光颜色

        float4_EmissiveColor;

        // 指定第一层纹理的映射坐标

        sampler2D_MainTex;

        // 由顶点着色器主入口函数在调用vert函数时获取到

        // 由片元着色器主入口函数在调用surf函数时作为参数传入

        structInput

        {

            float2uv_MainTex;

        };

        // 本函数在顶点着色器主入口函数中被调用

        voidvert(

              inoutappdata_full v,// appdata_full类型的参数是Unity 3D预定义的数据类型

              outInput o) // 由本函数返回,并在顶点着色器主入口函数中用来初始

                                    //化一个v2f_surf变量

        {

            o.uv_MainTex = v.texcoord.xy;

        }

        // 本函数在片元着色器主入口函数中被调用

        voidsurf (

            Input IN, // 根据传递给片元着色器主入口函数的v2f_surf类型的参数变量初始化而成

            inoutSurfaceOutput o) // 本函数内赋值完成后返回

        {

            o.Albedo = (_EmissiveColor.rgb + tex2D(_MainTex, IN.uv_MainTex).rgb);

            o.Alpha = _EmissiveColor.a;

        }

        // 本函数在片元着色器主入口函数中被调用

        inline float4LightingBasicDiffuse (

          SurfaceOutput s, // 此变量在片元着色器入口主函数内,调用surf获取到

          fixed3lightDir, // 光源到本片元的位置连线向量,由着色器编译器展开计算生成

                                    // 计算得到此变量的代码在片元着

                                    // 色器主入口函数中

          fixedatten) // 光线的衰减值,由着色器编译器展开计算生成

                                    // 计算得到此变量的代码在片元着色器主入口函数中

        {

            floatangle_cos = max(0, dot(s.Normal, lightDir));

            float4col;

            col.rgb = s.Albedo.rgb * _LightColor0.rgb * angle_cos * atten;

            col.a = s.Alpha;

            returncol;

        }

        voidfinal(Input IN, SurfaceOutput o, inoutfixed4 color)

        {}

        ENDCG

    }

    FallBack "Diffuse"

}

观察效果。

3.1.1 BasicDiffuse着色器展开后的代码分析

        1. BasicDiffuse着色器的生成代码:原始着色器中定义的函数和变量

        在每个编译完成的着色器的Inspector面板上,都有一个Show generated code按钮,单击Show generated code按钮后,原始着色器中定义的变量和函数也会被植入生成的代码中。(代码略,可自行生成查看)

        2. BasicDiffuse着色器的生成代码:顶点着色器传递给片元着色器的数据

        引擎会针对在原始的外观着色器中定义的Input内容,依据各种编译指示符指定的光照模型和其他参数,对应生成一个v2f_surf结构体。该结构体由顶点着色器返回,传递给片元着色器。

        如下述代码所示:

// 从顶点着色器传递给片元着色器的数据结构体
#ifndef LIGHTMAP_ON
// 不使用烘焙光照贴图时的顶点着色器传递给片元着色器的数据结构体
            structv2f_surf
            {
                  UNITY_POSITION(pos);
                  float2pack0 : TEXCOORD0; // _MainTex
                  half3worldNormal : TEXCOORD1;
                  float3worldPos : TEXCOORD2;
                  fixed3vlight : TEXCOORD3; // ambient/SH/vertexlights
                  UNITY_SHADOW_COORDS(4)
    #if SHADER_TARGET >= 30
                  float4lmap : TEXCOORD5;
    #endif
                  UNITY_VERTEX_INPUT_INSTANCE_ID
                  UNITY_VERTEX_OUTPUT_STEREO
            };
#endif
#ifdef LIGHTMAP_ON
// 使用烘焙光照贴图时的顶点着色器传递给片元着色器的数据结构体
            structv2f_surf
            {
                  UNITY_POSITION(pos);
                  float2pack0 : TEXCOORD0; // _MainTex
                  half3worldNormal : TEXCOORD1;
                  float3worldPos : TEXCOORD2;
                  float4lmap : TEXCOORD3;
                  UNITY_SHADOW_COORDS(4)
    #ifdef DIRLIGHTMAP_COMBINED
                  fixed3tSpace0 : TEXCOORD5;
                  fixed3tSpace1 : TEXCOORD6;
                  fixed3tSpace2 : TEXCOORD7;
    #endif
                  UNITY_VERTEX_INPUT_INSTANCE_ID
                  UNITY_VERTEX_OUTPUT_STEREO
            };
#endif


        3. BasicDiffuse着色器的生成代码:顶点着色器入口函数

        引擎会对应生成一个顶点着色器入口主函数vert_surf,该函数使用一个内置的顶点格式描述结构体appdata_full作为传入参数。在把顶点变换到裁剪空间之前,会用用户定义的vert函数对顶点进行操作。顶点着色器根据原始的外观着色器中各种编译指示符指定的光照模型和其他的参数,在里面进行顶点变换和光照处理,然后生成一个v2f_surf结构体返回。

        4. BasicDiffuse着色器的生成代码:片元着色器入口函数

        引擎会对应生成一个片元着色器入口主函数frag_surf,该函数使用顶点着色器返回的v2f_surf结构体作为传入参数。根据原始的外观着色器中各种编译指示符指定的光照模型和其他参数,在里面依次调用用户定义的surf函数、LightingBasicDiffuse函数和final函数进行光照和阴影处理。

        Unity 3D着色器代码的编译指示符(compile directive)。编译指示符的一般格式如下。

#pragma surface surfaceFunction lightModel [optionalparams]

        5. SurfaceOutput结构体

        SurfaceOutput结构体的定义如以下代码所示。

// 所在文件:lighting.cginc代码

// 所在目录:CGIncludes

// 从原文件第10行开始,至第17行结束

structSurfaceOutput

{

      fixed3Albedo; // 物体表面的漫反射颜色

      fixed3Normal; // 物体表面的法线

      fixed3Emission;// 物体的自发光颜色

      halfSpecular; // 物体的镜面反射系数,在[0,1]范围

      fixedGloss; // 物体的镜面高光亮度值

      fixedAlpha; // 物体的透明值

};

        lightModel即光照模型,此模型指明了经过surfaceFunction处理过的表面片元信息之后,如何利用这些片元信息进行光照计算。光照模型可以使用Unity 3D自带的光照函数,如Lambert模型,也可以使用自定义的光照函数。这些光照函数的命名规则是Lighting××××,即如果在编译指令中指定的光照模型名为××××,则定义此光照模型的光照函数名为Lighting××××

        以引擎自带的Lambert模型为例,此光照模型函数则是在Lighting.cginc文件中定义的Lighting Lambert,如下所示。

inline fixed4LightingLambert (SurfaceOutput s, UnityGI gi)

{

      fixed4c;

      c = UnityLambertLight(s, gi.light);

#ifdef UNITY_LIGHT_FUNCTION_APPLY_INDIRECT

    c.rgb += s.Albedo * gi.indirect.diffuse;

#endif

    returnc;

}

        6. UnityLambertLight函数

        UnityLambertLight函数的定义如下。

// 所在文件:lighting.cginc代码

// 所在目录:CGIncludes

// 从原文件第29行开始,至第37行结束

inline fixed4UnityLambertLight (SurfaceOutput s, UnityLight light)

{

      fixeddiff = max(0, dot(s.Normal, light.dir));

      fixed4c;

      c.rgb = s.Albedo * light.color * diff;

      c.a = s.Alpha;

      returnc;

}

        7. UnityLight、UnityIndirect和UnityGI结构体的定义

        UnityLight、UnityIndirect和UnityGI结构体的定义如下。

// 所在文件:UnityLightingCommon.cginc代码

// 所在目录:CGIncludes

// 从原文件第9行开始,至第26行结束

structUnityLight

{

      half3color; // 直接光照光的颜色

      half3dir; // 直接光照的方向

      half ndotl; // 法线方向向量和光照方向向量的点积值,此值已经不使用了,故在开发中不要再用

                    // 在开发中不要再用

};

structUnityIndirect

{

      half3diffuse; // 间接光照的漫反射贡献量

      half3specular;// 间接光照的镜面反射贡献量

};

structUnityGI

{

      UnityLight light;

      UnityIndirect indirect;

};

        除了上述的surfaceFuntion和lightModel,还可以自定义两种函数:vertex:VertexFunction和finalcolor:ColorFunction。

一个外观着色器在整个渲染流水线中的执行流程如图3-3所示。

ca6c6162106a48fbbf49e835762b0a38.jpg

         ▲图3-3 外观着色器在整个渲染流水线中的执行流程

3.1.2 外观着色器的编译指示符

        外观着色器的功能性代码需要放在CGPROGRAM和ENDCG两个指令之间。

        着色器代码段必须放在SubShader块中间,而不能放在Pass块内,编译器会将它编译到多个渲染通路(render pass)内。

        必须使用#pragma surface指示符去指明本着色器是一个外观着色器。格式如下:

#pragma surface surfaceFunction lightModel [optionalparams]

ac9a9ebf79ae4d639091e07ce06ec1df.jpg

 ▲表3-1 外观着色器必需的编译指示符与参数

6eeae98f2a0643ec9a5b3f7dbaf013a0.jpg

▲ 表3-2 和Alpha混合及Alpha测试相关的编译指示符与参数

76e16a7480e5423ba1570acefa33eee1.jpg

▲ 表3-3 和自定义操作顶点与片元着色器相关的指示符与参数

035ea3abe6a24dc99444b235d74e08bb.jpg

 ▲表3-4 和阴影操作和顶点镶嵌操作相关的指示符与参数

720f3347986d4e0b9c9e4f86016b0c51.jpg

 ▲表3-5 精简着色器代码的指示符与参数

f46ca246c8ba4c4881da0c8b52333850.jpg

 ▲表3-6 其他杂项相关的指示符与参数

3.1.3 传给外观着色器函数的参数

        传给外观着色器的输入结构通常可以包含任意着色器所需的纹理映射坐标。定义一个结构体分量去描述纹理映射坐标时,该分量务必以uv开头,然后后面加上分量名字,如uv_diffuse、uv_bumpmap等;如果是第二层纹理映射坐标,则使用uv2开头,其他层依此类推。

18c7b6dbace2467cada72938d5d3285c.jpg

 ▲表3-7 引擎内建支持的外观着色器光照函数的输入结构体的各属性分量

3.2 直接编写顶点着色器和片元着色器

通过使用Cg语言,在ShaderLab的Pass代码段中,在CGPROGRAM语句之后,ENDCG语句之前,嵌入一些着色器代码片段(snippets),便可以采用直接编写顶点着色器和片元着色器的实现代码这种传统的着色器编写方式,去实现着色器。这种编写方式的代码结构通常如下所示。

Pass

{

    // 此处的代码和普通的Pass的设置一样

    CGPROGRAM

    // 本代码片段所需要的编译指示符

    #pragma vertex vert // 指定顶点着色器的主入口函数,主入口函数名为vert

    #pragma fragment frag // 指定片元着色器的主入口函数,主入口函数名为frag

    // 具体的着色器实现代码在这里

    ENDCG

    // 此处的代码和普通的Pass的设置一样

}

3.2.1 用Cg语言编写的包含着色器功能的代码片段

如果使用CGPROGRAM和ENDCG关键字,编译器在编译代码时会自动包含HLSLSupport文件和UnityShaderVariables文件;如果启用了HLSLPROGRAM和ENDHLSL语句,就不会自动包含。

4c00f7ce220b4e49ba419409758817c2.jpg

 ▲表3-8 控制Cg代码片段的编译和优化指示符

3.2.2 声明目标渲染器

默认情况下所有着色器源代码都被编译到所有引擎能支持的渲染器中。使用#pragma only_renderers或#pragma exclude_renderers编译指示符,可以明确地告诉引擎把着色器源代码编译成只能给指定渲染器执行的版本。其他没有指定的渲染器,即使特性都支持,也都不编译。

6898702846e6450f82fff64173696e8f.jpg

  ▲Unity 3D支持的渲染器名称和对应的渲染接口如表3-9所示。

3.2.3 着色器的语义

着色器语言中的语义(semantics)用来说明在输入顶点的结构体中,以及顶点着色器传递给片元着色器中的数据(称为varying数据)结构体中,各数据成员的预期用途,如说明某数据是位置信息还是法向量信息,是纹理映射坐标还是雾化因子等。语义也表明这些图元数据存放的硬件资源是什么,如是寄存器还是纹理缓冲区等。如代码中的POSITION、NORMAL、TAGENT、TEXCOORD0便是语义词。

1. 顶点着色器的输入语义

67132beb56804762818f1ce6020c647e.jpg

 ▲表3-10是常见的顶点着色器中支持的输入语义,其中,n是一个从0到系统所支持的最大个数

语义词的使用示例说明如下所示:

Pass

{

    // 此处的代码和普通的Pass的设置一样


    CGPROGRAM

    // 本代码片段所需要的编译指示符

    #pragma vertex vert // 指定顶点着色器的主入口函数,主入口函数名为vert

    #pragma fragment frag // 指定片元着色器的主入口函数,主入口函数名为frag

    // 具体的着色器实现代码在这里


    ENDCG

    // 此处的代码和普通的Pass的设置一样

}

in float4modelPos: POSITION

2. 顶点着色器的输出语义

5bd3c8e7d30e4154986314626362237a.jpg

 ▲表3-11 常用的顶点着色器中支持的输出语义

顶点着色器的输出数据被传入片元着色器中,所以顶点着色器的输出语义通常也是片元着色器的输入语义,但是语义POSITION除外。顶点着色器必须声明一个输出变量,并绑定POSITION语义。

3. 片元着色器的输入语义

0856828829c148c9bf7c0efb5c255752.jpg

 ▲常用的片元着色器的输入语义

POSITION语义用于顶点着色器,用来指定这些位置坐标值,是变换前的顶点在模型空间中的坐标。SV_Position语义则用于片元着色器,用来标识经过顶点着色器变换之后的顶点坐标。

在SV_Position的情况下,如果它绑定在一个从顶点着色器输出的结构体上,意味着该输出的结构体包含了最终转换过的并将用于光栅器的顶点坐标。或者,如果将这个标志绑定到一个输入给片元着色器的结构体,它会包含一个基于屏幕空间的像素坐标。

4. 在着色器中同时使用SV Position语义和VPOS语义

下面的代码展示了如何同时使用SV_Position语义和VPOS语义。

Shader "Unlit/Screen Position"

{

    Properties

    {

        _MainTex ("Texture", 2D) = "white" {}

    }

    SubShader

    {

        Pass

        {

            CGPROGRAM

            #pragma vertex vert

            #pragma fragment frag

            #pragma target 3.0

            // 只定义了使用第0层纹理坐标的语义,没有定义SV_Position语义到分量中

            structv2f {

                  float2uv : TEXCOORD0;

            };

            v2f vert (// 输入给顶点着色器的顶点描述结构体

                  float4vertex : POSITION, // 顶点坐标

                  float2uv : TEXCOORD0, // 顶点使用的第0层纹理映射坐标

                  // 不能在顶点到片元的结构体v2f中描述,只能在顶点着色器的

                  // 主入口函数中声明为out返回

                  out float4outpos : SV_Position// 顶点在裁剪空间的位置坐标

                  )

            {

                  v2f o;

                  o.uv = uv;

                  // 调用Unity提供的工具函数,把顶点从模型空间变换到裁剪空间

                  outpos = UnityObjectToClipPos(vertex);

                  returno;

            }

            sampler2D_MainTex;

            fixed4frag (v2f i, UNITY_VPOS_TYPE screenPos : VPOS) : SV_Target

            {

                  //SV_Position语义所指明的裁剪空间坐标的范围是[-1,1],而VPOS

                  //语义所指明的坐标值就是像素坐标值,假如视口的高宽分别是

                  //1024像素和768像素,则VPOS坐标的取值范围就是[0,1024]、[0,768],并且是整数值

                  screenPos.xy = floor(screenPos.xy * 0.25) * 0.5;

                  floatchecker = -frac(screenPos.r + screenPos.g);

                  //若不能通过检测,就直接丢弃

                  clip(checker);

                  fixed4c = tex2D(_MainTex, i.uv);

                  returnc;

            }

            ENDCG

        }

    }

}

5. 在着色器中使用VFACE语义

        当多边形正向摄像机时,VFACE绑定的变量是一个正值;背向摄像机时,该变量是一个负值。因为VFACE语义是在shader model 3.0时引入的,所以要使用VFACE语义,必须使用#pragma target 3.0编译指示符。

下面的代码展示了如何在着色器中使用VFACE语义。

Shader "Unlit/Face Orientation"

{

    Properties

    {

        _ColorFront ("Front Color", Color) = (1,0.7,0.7,1)

        _ColorBack ("Back Color", Color) = (0.7,1,0.7,1)

    }

    SubShader

    {

        Pass

        {

            Cull Off // turn off backface culling

            CGPROGRAM

            #pragma vertex vert

            #pragma fragment frag

            #pragma target 3.0

            float4vert (float4vertex : POSITION) : SV_Position

            {

                  returnUnityObjectToClipPos(vertex);

            }

            fixed4_ColorFront;

            fixed4_ColorBack;

            fixed4frag (fixedfacing : VFACE) : SV_Target

            {

                  // 依据VFACE语义变量facing的取值,得到当前是正向还是背向摄像机,显示不同颜色

                  returnfacing > 0 ? _ColorFront : _ColorBack;

            }

            ENDCG

        }

    }

}

6. 片元着色器的输出语义

cb19986d359f492ea5f771be89d9f746.jpg

 ▲表3-13是常见的片元着色器的输出语义。

在大多数情况下,片元着色器将会输出一个颜色值,这个颜色值通常指定为SV_Target语义。

除了以单个数值的形式返回之外,片元着色器还支持以结构体的形式返回数据,如下代码所示。

structfragOutput
{
      fixed4color : SV_Target;
};
fragOutput frag (v2f i)
{
      fragOutput o;
      o.color = fixed4(i.uv, 0, 0);
      returno;
}


        其他的语义项有SV_Target1、SV_Target2。SV_Target等同于SV_Target0,大多数情况下对应于默认的帧缓冲区。

        当使用多渲染目标(multiple render targets,MRT)技术一次性地向不止一个渲染目标(render target)中写入颜色数据时,就需要利用SV_Target1、SV_Target2等一一对应去注明往哪个渲染目标去写入。多渲染目标在延迟渲染技术中会被普遍使用到。

3.3 在Cg代码中访问着色器属性块

        Unity 3D着色器代码通过使用属性块(properties block)的方式声明着色器中要用到的材质属性。声明完材质属性后,还需要在着色器的Cg/HLSL代码体内一一对应声明一次材质属性对应的着色器变量。

3.4 使用着色器多样体处理多种情况

        每一段由编译条件控制编译与否的代码段称为着色器多样体(shader variants),#pragma multi_compile或者#pragma shader_feature指示符后面跟着的名字字符串称为多样体关键字(variants keyword)。

3.4.1 编译指示符multi_compile和shader_feature的使用方式与区别

        如下所示是一个使用编译指示符定义多样体的语句。

#pragma multi_compile FANCY_STUFF_OFF FANCY_STUFF_ON

        该语句将会生成两个着色器多样体,一种就是启用了FANCY_STUFF_OFF,另一种就是启用了FANCY_STUFF_ON。在运行时,在C#语言层面,调用Material类的成员函数EnableKeyword可以显式地激活其中一个着色器多样体,如果没有一个多样体关键字被显式地启用,那么将默认启用第一个多样体。#pragma multi_Compile指示符还可以定义多于两个的多样体关键字。

        当使用完全由下画线“_”组成的多样体关键字时,对应的多样体依然被编译,但没有与之对应的预处理宏被定义,比如以下代码。

#pragma multi_compile __ FOO_ON


        使用这种匿名的方式定义多样体关键字,其好处是可以节省多样体关键字的个数,因为着色器编译器对多样体关键字的定义个数是有限制的。

        #pragma shader_feature类似于#pragma multi_compile,唯一的区别就是shader_feature指示符中声明的着色器多样体如果未被使用,在构建游戏运行包(game build)时将不会被打包进去。

        如果要在物体的材质中设置,即调用Material类成员函数EnableKeyword去设置的多样体关键字,用shader_feature指示符声明最好。如果要在全局范围中设置,即调用Shader类静态成员函数EnableKeyword去设置的多样体关键字,用multi_compile指示符声明最好。

3.4.2 多样体关键字的使用限制

        当使用着色器多样体时,要时刻切记Unity 3D有着只能使用256个多样体关键字的限制,并且大约有60个关键字已经被内置的代码所使用了。所以在编写自定义的着色器代码时,不能超出个数的限制。

3.4.3 内置的multi_compile指示符快捷使用方式

        Unity 3D提供了若干快捷(shortcut)编译指示符,可以用一个语句的方式代替需要多个编译指示符的声明,引擎后台在编译着色器代码时会将其自动展开。以下是若干快捷编译指示符的作用。

·        multi_compile_fwdbase编译指示符一次性开启所有在ForwardBase类型的渲染通路中所需的多样体,这些多样体定义了不同的烘焙光照图的类型;以及主要的有向平行光参与的光照计算中是否开启阴影计算。

·         multi_compile_fwdadd编译指示符一次性开启所有在ForwardAdd类型的渲染通路中所需的多样体,这些多样体将在渲染时控制操作场景中的有向光源、点光源和聚光灯光源的光照计算。

·         multi_compile_fwdadd_fullshadows编译指示符除了处理控制操作场景中的有向光源、点光源和聚光灯光源的光照计算之外,还会控制光源生成对应的实时阴影。

·         multi_compile_fwdadd编译指示符将会根据当前选定的雾化因子对应去展开成各个不同的多样体定义。

        在使用快捷编译指示符时,如果想在快捷定义中同时生成的多样体中去除若干多样体,可以使用skip_variants编译指示符,如以下代码所示。

#pragma multi_compile_fwdadd

// multi_compile_fwdadd指示符把"POINT"和"POINT_COOKIE"都开启了

// 现在不想启用这两个

#pragma skip_variants POINT POINT_COOKIE

3.5 多平台着色器代码的支持

b568f9a069994edcabfd0d9c3f401dc1.jpg

 ▲表3-14 编译指示符指定的目标渲染器和对应的宏

        使用#pragma only_renderers或#pragma exclude_renderers编译指示符,定义了如表3-9所示的目标渲染器后,着色器编译器会根据所启用的目标渲染器,定义上对应的宏。

3.6 确定着色器编译器的版本

      3.6.1 和着色器编译器版本相关的宏

        确定着色器使用版本的代码段如下:

// 所在文件:HLSLSupport.cginc代码

// 所在目录:CGIncludes

// 从原文件第3行开始,至第23行结束

#ifndef HLSL_SUPPORT_INCLUDED

#define HLSL_SUPPORT_INCLUDED

// 根据各个宏的预定义情况,确定底层使用哪个着色器编译器

#if !defined(UNITY_COMPILER_CG) && // 如果Cg编译器没有被指定启用

      !defined(UNITY_COMPILER_HLSL) && // 如果HLSL编译器没有被指定启用

      !defined(UNITY_COMPILER_HLSL2GLSL)&& //HLSL转GLSL编译器没使用

      !defined(UNITY_COMPILER_HLSLCC)//如果HLSLCC编译器没被启用

#if defined(SHADER_TARGET_SURFACE_ANALYSIS)

//

#define UNITY_COMPILER_CG

      #elif defined(SHADER_API_GLCORE) || defined(SHADER_API_GLES3)

              || defined(SHADER_API_VULKAN)

#define UNITY_COMPILER_HLSL

#define UNITY_COMPILER_HLSLCC

#elif defined(SHADER_API_D3D11) || defined(SHADER_API_D3D11_9X)

      || defined(SHADER_API_D3D9) || defined(SHADER_API_XBOXONE)

      #define UNITY_COMPILER_HLSL

#elif defined(SHADER_TARGET_GLSL) || defined(SHADER_API_WIIU)

      #define UNITY_COMPILER_HLSL2GLSL

#else

      #define UNITY_COMPILER_CG

#endif

#endif

      3.6.2 消除着色器代码中各平台的语义差异性

        1.利用宏消除各平台的语义差异性

// 所在文件:HLSLSupport.cginc代码

// 所在目录:CGIncludes

// 从原文件第43行开始,至第73行结束

#if defined(UNITY_FRAMEBUFFER_FETCH_AVAILABLE) &&

      defined(UNITY_FRAMEBUFFER_FETCH_ENABLED) &&

      defined(UNITY_COMPILER_HLSLCC)

#define SV_Target CoLoR

#define SV_Target0 CoLoR0

#define SV_Target1 CoLoR1

#define SV_Target2 CoLoR2

#define SV_Target3 CoLoR3



#define COLOR VCOLOR

#define COLOR0 VCOLOR0

#define COLOR1 VCOLOR1

#define COLOR2 VCOLOR2

#define COLOR3 VCOLOR3

#endif

        UNITY_FRAMEBUFFER_FETCH_AVAILABLE宏用来表征目标硬件平台是否实现了“帧缓冲区撷取”(frame buffer fetch)这一功能;UNITY_FRAMEBUFFER_FETCH_ENABLED宏则表示假如功能实现,是否启用它。

        2. 消除SV Target和SV Depth语义在各平台的差异性


// 所在文件:HLSLSupport.cginc代码

// 所在目录:CGIncludes

// 从原文件第76行开始,至第125行结束

#if !defined(SV_Target)

# if !defined(SHADER_API_XBOXONE)

# define SV_Target COLOR

# endif

#endif

      3.6.3 关闭可忽视的编译警告

        可以使用#pragma warning(disable警告编号)语句关闭一些特定的编译警告,使在编译时不提示这些警告。

        //屏蔽把数据类型从大范围值转为小值时发生的警告

#pragma warning (disable : 3205)

3.6.4 Unity 3D Shader的基本数据类型

        1. 浮点数类型

        Cg/HLSL着色器语言中有几种浮点数的实现类型:float、half和fixed,以及基于它们所实现的向量和矩阵类型,如half3和float4x4。

80dfee7f45e8453f95e7ca72ae76e65c.jpg

 ▲表3-15 Cg语言中的浮点数类型

        2.整数类型
        着色器语言中的整数类型通常用于循环次数计数,或者用于数组索引,所以在大多数平台上同一种整数类型大都能工作良好。但在不同平台中,对整数类型的实现有所不同,如在Direct3D 9和OpenGL ES 2.0平台下,整数类型在GPU内部是用浮点数去模拟的。因此,一些在C语言中常见的对整数进行移位、按位与、按位或、按位异或等位操作是不能在着色器代码中使用的。而在Direct3D 11、OpenGL ES3.0、Metal等平台上,则真正地拥有整数类型,可以对整型变量进行位操作。

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

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

相关文章

51.Python-web框架-Django开始第一个应用的增删改查

目录 1.概述 2.创建应用 创建app01 在settings.py里引用app01 3.定义模型 在app01\models.py里创建模型 数据库迁移 4.创建视图 引用头 部门列表视图 部门添加视图 部门编辑视图 部门删除视图 5.创建Template 在app01下创建目录templates 部门列表模板depart.ht…

java+vue3+el-tree实现树形结构操作

基于springboot vue3 elementPlus实现树形结构数据的添加、删除和页面展示 效果如下 代码如下,业务部分可以自行修改 java后台代码 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.daztk.mes.common.annotation.LogOperation…

高通Android 12 右边导航栏改成底部显示

最近同事说需要修改右边导航栏到底部,问怎么搞?然后看下源码尝试下。 1、Android 12修改代码路径 frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java a/frameworks/base/services/core/java/com/android/server/wm/Display…

树莓派4B_OpenCv学习笔记6:OpenCv识别已知颜色_运用掩膜

今日继续学习树莓派4B 4G:(Raspberry Pi,简称RPi或RasPi) 本人所用树莓派4B 装载的系统与版本如下: 版本可用命令 (lsb_release -a) 查询: Opencv 版本是4.5.1: 学了这些OpenCv的理论性知识,不进行实践实在…

Roboflow 图片分类打标

今天准备找个图片标注工具,在网上搜了一下,看 Yolo 的视频中都是用 Roboflow 工具去尝试了一下,标注确实挺好用的,可以先用一些图片训练一个模型,随后用模型进行智能标注。我主要是做标注然后到处到本地进行模型的训练…

springboot的WebFlux 和Servlet

Spring Boot 中的 Servlet 定义: 在 Spring Boot 中,Servlet 应用程序通常基于 Spring MVC,它是一个基于 Servlet API 的 Web 框架。Spring MVC 提供了模型-视图-控制器(MVC)架构,用于构建 Web 应用程序。…

【Mac】增加 safari 体验的插件笔记

Safari 本身的功能不全面,探索积累了一点插件笔记,提升使用体验;但后面因为插件或会影响运行速度,就全部都禁止了。做个笔记记录一下。 Cascadea 相当于 stylus,可以自定义页面。测试过几个,只有几个可行。…

Java:爬虫htmlunit抓取a标签

如果对htmlunit还不了解的话可以参考Java:爬虫htmlunit-CSDN博客 了解了htmlunit之后,我们再来学习如何在页面中抓取我们想要的数据,我们在学习初期可以找一些结构比较清晰的网站来做测试爬取,首先我们随意找个网站如下&#xff…

【StableDiffusion】Embedding 底层原理,Prompt Embedding,嵌入向量

Embedding 是什么? Embedding 是将自然语言词汇,映射为 固定长度 的词向量 的技术 说到这里,需要介绍一下 One-Hot 编码 是什么。 One-Hot 编码 使用了众多 5000 长度的1维矩阵,每个矩阵代表一个词语。 这有坏处&#xff0c…

美国空军发布类ChatGPT产品—NIPRGPT

6月11日,美国空军研究实验室(AFRL)官网消息,空军部已经发布了一款生成式AI产品NIPRGPT。 据悉,NIPRGPT是一款类ChatGPT产品,可生成文本、代码、摘要等内容,主要为为飞行员、文职人员和承包商提…

Python 中浅拷贝(copy)和深拷贝(deepcopy)

1. 浅拷贝: 它创建一个新的对象,但对于原始对象内的子对象(如列表中的嵌套列表),只是复制了引用。例如: import copy original_list [1, 2, 3] shallow_copied_list copy.copy(original_list) original_…

【PIXEL】2024年 Pixel 解除 4G限制

首先在谷歌商店下载 Shizuku 和 pixel IMS 两个app 然后打开shizuku ,按照它的方法启动 推荐用adb 启动( 电脑连手机 ,使用Qtscrcpy最简洁) 一条指令解决 shell sh /storage/emulated/0/Android/data/moe.shizuku.privileged.ap…

Chrome/Edge浏览器视频画中画可拉动进度条插件

目录 前言 一、Separate Window 忽略插件安装,直接使用 注意事项 插件缺点 1 .无置顶功能 2.保留原网页,但会刷新原网页 3.窗口不够美观 二、弹幕画中画播放器 三、失败的尝试 三、Potplayer播放器 总结 前言 平时看一些视频的时候&#xff…

ListView的使用

📖ListView的使用 ✅1. 创建ListView✅2. 创建适配器Adapter✅3. 开始渲染数据 主要3步骤: 创建ListView 创建适配器Adapter,和Adapter对应的视图 开始渲染数据 效果图: ✅1. 创建ListView 例如现有DemoActivity页面&#xf…

C# WinForm —— 33 ContextMenuStrip介绍

1. 简介 右键某个控件/窗体时,弹出来的菜单,比如VS中右键窗体,弹出来的这个菜单: 和MenuStrip类似,ContextMenuStrip主菜单下面可以有子菜单,子菜单下面可以有下一级子菜单 2. 属性 和MenuStrip一样 …

国内服务器安装 Docker 服务和拉取 dockerhub 镜像

前提: 有一台海外的VPS,目的是安装dockerhub镜像.适用debian系统 1: 安装 docker-ce (国内服务器) sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/…

如何免费用 Qwen2 辅助你翻译与数据分析?

对于学生用户来说,这可是个好消息。 开源 从前人们有一种刻板印象——大语言模型里好用的,基本上都是闭源模型。而前些日子,Meta推出了Llama3后,你可能已经从中感受到现在开源模型日益增长的威力。当时我也写了几篇文章来介绍这个…

【DevOps】Ubuntu基本使用教程

目录 引言 Ubuntu简介 安装Ubuntu 准备工作 创建启动盘 安装过程 桌面环境 基本操作 定制桌面 文件管理 文件操作 文件权限 软件管理 安装软件 更新软件 系统设置 用户账户 网络设置 电源管理 命令行操作 常用命令 管理权限 安全与维护 系统更新 备份…

pdf添加书签的软件,分享3个实用的软件!

在数字化阅读日益盛行的今天,PDF文件已成为我们工作、学习和生活中不可或缺的一部分。然而,面对海量的PDF文件,如何高效地进行管理和阅读,成为了许多人关注的焦点。其中,添加书签功能作为提高PDF文件阅读体验的重要工具…

JetLinks开源物联网平台社区版部署教程

1.上github搜素jetlinks 2.找到源代码,并且下载到本地。 3.项目下载完成之后,还需要另外下载三个核心依赖模块。在github找到jetlinks。 4.点击进去下载,下载完成之后,你会发现里面有三个文件夹是空白的,先不用理会&am…