UnityShader——基础篇之UnityShader基础

UnityShader基础

UnityShader概述

材质和UnityShader

  总的来说,在Unity中需要配合使用材质(Material)和 Unity Shader 才能达到需要的效果,常见流程为:

  • 创建一个材质
  • 创建一个 Unity Shader,并把它赋给上一步中创建的材质
  • 把材质赋给需要渲染的对象
  • 在材质面板中调整 Unity Shader 的属性,以得到满意的效果

在这里插入图片描述

  Unity Shader定义了渲染所需的各种代码(如顶点着色器和片元着色器)、属性(如使用哪些纹理等)和指令(渲染和标签设置等)

  材质允许调节这些属性,将其最终赋给对应的模型

Unity 中的材质

  Unity 中的材质需要结合一个 GameObject 的 Mesh 或者 Particle systems 组件来工作,它决定了游戏对象看起来是什么样子的(也需要 Unity Shader 的配合)

  创建一个新的材质,可以在 Unity 的菜单栏中选择 Assets -> Create -> Material 来创建,也可以直接在 Project 视图中右击 -> Create -> Material 来创建

  当创建了一个材质后,就可以把它赋值给一个对象,这可以通过把材质直接拖拽到 Scene 视图中的对象来实现,或者在该对象的 Mesh Renderer 组件中直接赋值,如图:

在这里插入图片描述

  在 Unity 的 2020.x 版本中,默认情况下,一个新建的材质将使用Unity内置的 Standard Shader,这是一种基于物理渲染的着色器

  Unity的材质和许多建模软件(如 Cinema 4D、Maya 等)中提供的材质工鞥呢类似,他们都提供了一个面板来调整材质的各个参数,这种可视化的方法使得开发者不再需要自行在代码中设置和改变所需的各种参数,如图:

在这里插入图片描述

Unity 中的 Shader

  创建一个新的 Unity Shader,可以在 Unity 的菜单栏中选择 Assets -> Create -> Shader 来创建,也可以直接在 Project 视图中右击 -> Create -> Shader 来创建,Unity 一共提供了5种 Unity Shader 模板供选择 —— Shader Surface Shader, Unlit Shader, Image Effect Shader, Compute Shader 以及 Ray Tracing Shader。

  Standard Surface Shader 会产生一个包含了标准光照模型的表面着色器模板

  Unlit Shader会产生一个不包含光照(但包含雾效)的基本的顶点/片元着色器

  Image Effect Shader 为实现各种屏幕后处理效果提供了一个基本模板

  Compute Shader 会产生一种特殊的 Shader 文件,这类 Shader 旨在利用 GPU 的并行性来进行一些常规渲染流水线无关的计算

  Ray Tracing Shader是一种用于Unity中的实时光线追踪的Shader。光线追踪是一种通过追踪从相机到场景中各个像素的光线的技术,以模拟光线在真实世界中的传播和反射。Ray Tracing Shader可以帮助开发者实现更加逼真的光影效果,包括实时反射、折射、阴影和环境光照等效果。


  一个单独的 Unity Shader 是无法发挥任何作用的,它必须和材质结合起来。

  所以,可以在材质面板最上方的下拉菜单中选择需要使用的 Unity Shader,当选择完毕后,材质面板中就会出现该 Unity Shader 可用的各种属性。这些属性可以是颜色、纹理、浮点数、滑动条(限制了范围的浮点数)、向量等

  当把材质赋给场景中的一个对象,就可以看到调整属性所发生的视觉变化


  Unity Shader本质上就是一个文本文件,和 Unity 中的很多外部文件类似,Unity Shader 也有导入设置(Import Settings)面板,在 Project视图中选中某个 Unity Shader即可看到,如图:

在这里插入图片描述

  在该面板上,可以在 Default Maps 中指定该 Unity Shader 使用的默认纹理。当任何材质第一次使用该 Unity Shader 时,这些纹理就会自动被赋予到相应的属性上。在下方的面板中,Unity 会显示出和该 Unity 相关的信息,例如它是否是一个表面着色器(Surface Shader)、是否是一个固定函数着色器(Fixed Function Shader)等,还有一些信息是和在 Unity Shader 中的标签设置有关,例如是否会投射阴影、使用的渲染队列、LOD值等

  对于表面着色器来说,可以通过单击 Show generated code 按钮来打开一个新的文件,在该文件里将显示 Unity 在背后为该表面着色器生成的顶点/片元着色器。这可以方便对这些生成的代码进行修改(需要复制到一个新的 Unity Shader 中才可保存)和研究

  如果 Unity Shader 使一个固定函数着色器,在 Fixed function 的后面也会出现一个 Show generated code 按钮,来查看该固定函数着色器生成的顶点/片元着色器。Compile and show code 下拉列表可以让开发者检查该 Unity Shader 针对不同图像编程接口(例如 OpenGL、D3D9、D3D11等)最终编译成的 Shader 代码,如图所示:

在这里插入图片描述

  直接单击该按钮可以查看生成的底层的汇编指令。可以利用这些代码来分析和优化着色器

  除此之外,Unity Shader 的导入面板还可以方便地查看其使用的渲染队列(Reder queue)、是否关闭批处理(Disable batching)、属性列表(Properties)等信息

Unity Shader 的基础:ShaderLab

什么是 ShaderLab

  Unity Shader 是 Unity 为研发者提供的高层记得渲染抽象层,Unity 希望以这种方式来让开发者更加轻松的控制渲染,见下图:

在这里插入图片描述

  在 Unity 中,所有的 Unity Shader 都是使用 ShaderLab 来编写的

  ShaderLab 是 Unity 编写 Unity Shader 的一种说明性语言,它使用了一些嵌套在花括号内部的**语义&&(syntax)来描述一个 Unity Shader 文件的结构,这些结构包含了许多渲染所需的数据,例如 Properties 语句块中定义了着色器所需的各种属性,这些属性将会出现在材质面板中

  从设计上来说,ShaderLab 类似于 CgFX 和 Direct3D Effects(FX)语言,他们都定义了要显示一个材质所需的所有东西,而不仅仅是着色器代码

  一个 Unity Shader 的基础结构如下所示:

Shader "ShaderName"{
    Properties{
        //属性
    }
    SubShader{
        //显卡A使用的子着色器
    }
    SubShader{
        //显卡B使用的子着色器
    }
    Fallback "VertexLit"
}

UnityShader的结构

给Shader起名字

  每个Unity Shader文件的第一行都需要通过 Shader 语义来制定该 Unity Shader 的名字,这个名字由一个字符串来定义

  当为材质选择使用的 Unity Shader 时,这些名称就会出现在材质面板的下拉列表里,通过在字符串中添加斜杠(“/”),可以控制 Unity Shader 在材质面板中出现的位置,如下:

Shader "Unlit/001Shader"{}

  那么,这个 Unity Shader在材质面板中的位置就是:Shader -> Unlit -> 001Shader,如图:

在这里插入图片描述

材质和 Unity Shader 的桥梁:Properties

  Properties语义块中包含了一系列属性(property),这些属性将会出现在材质面板中

  Properties语义块的定义通常如下:

Properties{
    Name("display name",PropertyType) = DefaultValue
    Name("display name",PropertyType) = DefaultValue
    //更多属性
}

  开发者声明这些属性是为了在材质面板中能够方便的调整各种材质属性

  如果需要在 Shader 中访问它们,就需要使用每个属性的名字(Name),Unity中,这些属性的名字通常由一个下划线开始,显示的名称(display name)则是出现在材质面板上的名字,需要为每个属性指定它的类型(PropertyType),常见的属性如表:

属性类型默认值的定义语法例子
Intnumber_Int(“Int”,Int) = 2
Floatnumber_Float(“Float”,Float) = 1.5
Range(min,max)number_Range(“Range”,Range(0.0,5.0) = 3.0
Color(number,number,number,number)_Color(“Color”,Color) = (1,1,1,1)
Vector(number,number,number,number)_Verctor(“Vector”,Vector) = (2,3,6,1)
2D“defaulttexture”{}_2D(“2D”,2D) = “”{}
Cube“defaulttexture”{}_Cube(“Cube”,Cube) = “white”{}
3D“defaulttexture”{}_3D(“3D”,3D) = “black”

  除此之外,还需要为每个属性指定一个默认值,在第一次把该 Unity Shader 赋给某个素材时,菜盒子面板显示的就是这些默认值

    对于 Int、Float、Range ,这些数字类型的属性,其默认值就是一个单独的数;

    对于 ColorVector 这类属性,默认值是用圆括号包围的一个四维向量;

    对于 2D、Cube、3D 这3种纹理类型,默认值的类型稍微复杂,他们的默认值是通过一个字符串后跟一个花括号来指定的,其中,字符串要么是空的,要么是内置的纹理名称,如 “white”“black”“gray” 或者 “bump”。或括号的用处原本是用于制定一些纹理属性的。

  如果需要控制固定管线的纹理坐标的生成,就需要再顶点着色器中编写计算相应纹理坐标的代码

  如下方的代码:

Shader "Unlit/001Shader"
{
    Properties
    {
        _Int("Int",int) = 2
        _Float("Float",float) = 1.5
        _Range("Range",range(0.0,5.0)) = 1.0
        _Color("Color",Color) = (1,1,1,1)
        _Vector("Vector",Vector) = (2,3,6,1)
        _2D("2D",2D) = ""{}
        _Cube("Cube",Cube) = "white"{}
        _3D("3D",3D) = "black"{}
    }

	FallBack "diffuse"
}

  材质面板中的显示结果,如图:

在这里插入图片描述

  Unity 允许重载默认的材质编辑面板,以提供更多自定义的数据类型

  为了在 Unity 中可以访问到这些属性,需要在 Cg 代码片中定义和这些属性类型相匹配的变量,需要说明的是,即使不在 Properties 语义块中生命这些属性,也可以直接在 Cg 代码片中定义变量

  此时,可以通过脚本向 Shader 中换地这些属性,因此,Properties与一块的作用仅仅是为了让这些属性可以出现在材质面板中

SubShader

  每一个 Unity Shader 文件可以包含多个 SubShader 语义块,但最少要有一个。

    当 Unity 需要加载这个 Unity Shader 时,Unity 会扫描所有的 SubShader 语义块,然后选择第一个能够在目标平台运行的SubShader

    如果都不支持的话,Unity 就会使用 Fallback 语义指定的 Unity Shader

  Unity 提供这种语义的原因在于,不同的显卡具有不同的能力。例如:一些旧的显卡仅能支持一定数目的操作指令,而一些更高级的显卡可以支持更多的指令数

  那么就希望在旧的显卡上使用计算复杂度低的着色器,在高级的显卡上使用计算复杂度较高的着色器,以便提供更出色的画面

  SubShader 语义块中包含的定义通常如下

SubShader{
    //可选
    Tags

    //可选
    RenderSetup

    Pass{}

    //Other Passes
}

  SubShader 中定义了一系列 Pass 以及可选的状态(RenderSetup)和标签(Tags)设置。

  每个 Pass 定义了一个完整的渲染流程,但如果 Pass 数目过多,往往会造成渲染性能的下降

  因此,应该尽量使用最小数目的 Pass,状态和标签同样可以在 Pass 声明,不同的是,SubShader 中的一些标签是特定的

  也就是说,这些标签设置和 Pass 中使用的标签是不一样的,对于状态设置来说,使用的语法是相同的

  但是如果在 SubShader 进行了这些设置,呢么将会用于所有的 Pass

  状态设置

  ShaderLab 提供了一系列渲染状态的设置指令,这些指令可以设置显卡的各种状态,例如是否开启混合/深度测试等

  ShaderLab 中常见的渲染状态设置选项如下表:

状态名称设置指令解释
CullCull Back|Front|Off设置剔除模式:剔除背面/正面/关闭剔除
ZTestZTest Less Greater|LEqqual|GEqual|Equal|NotEqual|Always设置深度测试时使用的函数
ZWriteZWrite On|Off开启/关闭深度写入
BlendBlend SrcFactor DstFactor开启并设置混合模式

  当在 SubShader 块中设置了上述渲染状态时,将会应用到所有的 Pass,如果不想这样(如在双面渲染中,希望第一个 Pass 中剔除正面来对背面进行渲染,在第二个 Pass 中剔除背面来对正面进行渲染),可以在 Pass 语义块中单独进行上面的设置

  SubShader的标签

  SubShader 的标签(Tags)是一个键值对(Key/Value Pair),它的键和值都是字符串类型,这些键值对是 SubShader 和渲染引擎之间的沟通桥梁。它们用来告诉 Unity 的渲染引擎希望怎样以及何时渲染这个对象:

  标签的结构如下:

Tags{"TagName1" = "Value1" "TagName2" = "Value2"}

  SubShader 的标签快支持的标签类型如表:

标签类型说明例子
Queue控制渲染顺序,指定该物体属于哪一个渲染队列,通过这种方式可以保证所有的透明物体可以在所有不透明物体后面被渲染,也可以自定义使用的渲染队列来控制物体的渲染顺序Tags{“Queue” = “Transparent”}
RenderTyep对着色器进行分类,例如这是一个不同明德着色器,或是一个透明的着色器等,这可以被用于着色器替换(Shader Replacement)功能Tags{“renderType”=“Opaque”}
DisableBatching一些 SubShader 在使用 Unity 的批处理功能时会出现问题,例如使用了模型空间下的坐标进行顶点动画,这是可以通过该标签来直接指明是否对该 SubShader 使用批处理Tags{“DisableBatching” = “True”
ForceNoShadowCasting控制使用该 SubShader 的物体是否会投射阴影Tags{“ForceNoShadowCasting”=“True”
IgnoreProjector如果该标签为"True",那么使用该 SubShader 的物体将不会受 Projector 的影响,通常用于半透明物体Tags{“IgnoreProjector”=“True”}
CanUseSpriteAtlas当该 SubShader 是用于精灵(sprite)时,将该标签设置为"False"Tags{“CanUseSpriteAtlas”=“False”
PreviewType知名材质面板将如何预览改材质。默认情况下,材质将显示为一个球形,可以通过把这些标签的值设为"Plane""SkyBox"来改变预览类型Tags{“PreviewType”=“Plane”}

  需要注意的是,上述标签仅可以在 SubShader 中声明,而不可以在 Pass 块中声明。Pass 块虽然也可以定义标签,但这些标签是不同于 SubShader 的标签类型

  Pass 语义块

  Pass 语义块包含的语义如下:

Pass{
    Name
    Tags
    RenderSetup
}

  首先,可以在 Pass 中定义该 Pass 的名称,例如:

Name "MyPassName"

  通过这个名称,可以使用 ShaderLab 的 UsePass 命令来直接使用其他 UnityShader 中的 Pass,例如:

UsePass "MyShader/MYPASSNAME"

  这样可以提高代码的复用性,需要注意的是,由于 Unity 内部会把所有 Pass 的名称转换成大写字母的表示,因此,在使用 UsePass 命令时必须使用大写形式的名字

  其次,可以对 Pass 设置渲染状态。SubShader 的状态设置同样适用于 Pass。除了上面提到的状态设置外,在 Pass 中我们还可以使用固定管线的着色器命令

  Pass 同样可以设置标签,但它的标签不同于 SubShader 的标签,这些标签也是用于告诉渲染引擎希望怎样来渲染该物体

  Pass 中使用的标签类型如下表:

标签类型说明例子
LightMode定义该 Pass 在 Unity 的渲染流水线中的角色Tags{“LightMode”=“ForwardBase”}
RequireOptions用于指定当满足某些条件时才渲染该 Pass,它的值是一个由空格分割的字符串。Tags{“RequireOptions”=“SoftVegetation”}

  除了上面普通的 Pass 定义外,UnityShader 还支持一些特殊的 Pass,以便进行代码复用或实现更复杂的效果:

    UsePass:如之前提到的一样,可以使用该命令来复用其他 UnityShader 中的 Pass

    GrabPass:该 Pass 负责抓取屏幕并将结果存储在一张纹理中,以用于后续的 Pass 处理

Fallback

  紧跟在各个 SubShader 语义块后面的,可以是一个 Fallback 指令,它用于告诉 Unity,“如果上面所有的 SubShader 在这块显卡上都不能运行,那么就是用这个最低级的 Shader”

  它的语义如下:

Fallback "name"
//或者
Fallback Off

  如上所述,可以通过一个字符串来告诉 Unity 这个“最低级的 UnityShader”是谁,也可以关闭 Fallback 功能,但一旦这么做,那么意思就是“如果一块显卡跑不了上面所有的 Shader,那就不管他了”

  例子如下:

Fallback "VertexLit"

  事实上,Fallback还会影响阴影的投射

  在渲染阴影纹理时,Unity 会在每个 UnityShader 中寻找一个阴影投射的 Pass,通常情况下,不需要自己专门实现一个 Pass,这是因为 Fallback 使用的内置 Shader 中包含了这样一个通用的 Pass,因此,为每个 UnityShader 正确设置 Fallback 是非常重要的

ShaderLab还有其他的语义吗

  除了上述的语义,还有一些不常用到的寓意。

  例如:如果不满足于 Unity 内置的属性类型,想要自定义材质面板的编辑界面,就可以使用 CustomEditor 语义来拓展编辑界面。还可以使用 Category 语义来对 UnityShader 中的命令进行分组。

UnitySahder 的形式

  尽管 UnityShader 可以做的事情很多(例如设置渲染状态等),但其最重要的任务还是指定各种着色器所需的代码。这些着色器代码可以写在 SubShader 语义块中(表面着色器的做法),也可以写在 Pass 语义块中(顶点/片元着色器和固定函数着色器的做法)

  在 Unity 中,可以使用下面 3 种形式来编写 UnityShader,而不管使用哪种形式,真正意义上的 Shader 代码都需要包含在 ShaderLab 语义块中,如下所示:

Shader "MyShader"{
    Properties{
        //所需的各种属性
    }

    SubShader{
        //真正意义上的 Shader 代码会出现在这里
        //表面着色器{Surface Shader}或者
        //顶点/片元着色器{Vertex/Fragment Shader}或者
        //固定函数着色器{Fixed Function Shader}
    }

    SubShader{
        //和上一个 SubShader 类似
    }
}

表面着色器

  表面着色器(Surface Shader) 是 Unity 自己创造的一种着色器代码类型,他需要的代码量很少,Unity 在背后做了很多工作,渲染的代价比较大。

  本质上和顶点/片元着色器是一样的,当给Unity提供一个便面着色器的时候,在背后仍旧需要把它装换成对应的顶点/片元着色器

  它的价值在于:Unity 处理了很多光照细节,不需要去操心这些“烦人的事情”

  示例代码如下:

Shader "Custom/Simple Surface Shader"{
    SubSHader{
        Tags{"RenderType"="Opaque"}
        CGPROGAM
        #pragma surface surf Lambert
        struct Input{
            float4 color : COLOR;
        };
        void surf(Input IN,inout SurfaceOutput o){
            o.Albedo = 1;
        }
        ENDCG
    }
    Fallback "Diffuse"
}

  表面着色器被定义在 SubShader 语义块(不是 Pass 语义块)中的 CGPROGRAM 和 ENDCG 之间

  原因是表面着色器不需要开发者关心使用多少个 Pass,每个 Pass 如何渲染等问题,Unity 会在背后做好这些事情

  CGPROGRAM 和 ENDCG 之间的代码是使用 Cg/HLSL 编写的,也就是说,需要把 Cg/HLSL 语言嵌套在 ShaderLab 语言中

  这里的 Cg/HLSL 是 Unity 经封装后提供的,它的语法和标准的 Cg/HLSL 语法几乎一样,但还是有细微的不同

顶点/片元着色器

  Unity 中可以使用 Cg/HLSL 语言来编写顶点/片元着色器(Vertex/Fragment Shader)。它们更加复杂,但灵活性更高

  示例代码如下:

Shader "Custom/Simple VertexFragment Shader"{
    SubShader{
        Pass{
            CGPROGRAM

            #progama vetex vert
            #pragma fragment frag

            float4 vert(float4 v : POSITION) : SV_POSITON{
                return mul(UNITY_MATRIX_MVP,v);
            }

            fixed4 frag() : SV_Target{
                return fixed4(1.0,0.0,0.0,1.0);
            }

            ENDCG
        }
    }
}

  顶点/片元着色器的代码需要定义在 CGPROGRAM 和 ENDCG 之间,不同的是,顶点/片元着色器是写在 Pass 语义块内,而非 SubShader内的

  这样的原因是,需要自己定义每个 Pass 需要使用的 Shader 代码,虽然可能需要编写更多的代码,但好处是灵活性很高,更重要的是,可以空时渲染的实现细节

固定函数着色器

  对于一些较旧的设备(其GPU仅支持 DirectX 7.0、OpenGL 1.5 或 OpenGL ES 1.1),它们不支持可编程管线着色器,因此,这时候就需要使用固定函数着色器(Fixed Function Shader) 来完成渲染。这些着色器网网址可以完成一个非常简单的效果

  示例代码如下:

Shader "Tutorial/Basic"{
    Properties{
        _Color("Main Color",Color} = {1,0.5,0.5,1}
    }
    SubShader{
        Pass{
            Material{
                Diffuse [_Color]
            }
        }
    }
}

  固定着色器的代码被定义在 Pass 语义块中,这些代码相当于 Pass 中的一些渲染设置

  对于固定函数着色器来说,需要完全使用 ShaderLab 的语法(使用 ShaderLab 的渲染设置命令)来编写,而不是使用 Cg/HLSL

  由于现在绝大多数 GPU 都支持可编程的渲染管线,这种编程方式已经被逐渐抛弃,在新版的 Unity 中,所有固定函数着色器都会在背后被 Unity 编译成对应的顶点/片元着色器

选择哪种 UnityShader 形式

  如果有非常明确的需求必须要使用固定函数着色器,否则使用可编程管线的着色器,即表面着色器或顶点/片元着色器

  如果要和各种光源打交道,可能更需要使用表面着色器,但需要小新在移动平台中的性能表现

  如果需要使用的光照数目非常少,那么使用顶点/片元着色器使一个更好的选择

  如果有很多自定义的渲染效果,使用顶点/片元着色器

其他的一些问题

UnityShader != 真正的Shader

  尽管UnityShader翻译过来就是Unity着色器,在Unity里,UnityShader实际上指的就是一个ShaderLab文件——硬盘上以.shader作为文件后缀的一种文件

  在UnityShader里,可以做的事情远多于一个传统意义上的Shader

    传统的Shader中,仅可以编写特定类型的Shader,例如顶点着色器、片元着色器等,在UnityShader中,可以在同一个文件里同时包含需要的顶点着色器和片元着色器代码

    在传统的Shader中,无法设置一些渲染设置,例如是否开启混合、深度测试等,这些是开发者在另外的代码中自行设置的。而在UnityShader中,通过一行特定的指令就可以完成这些设置

    在传统的Shader中,我们需要编写荣昌的代码来设置着色器的输入和输出,要小心的处理这些输入输出的位置对应关系等。而在UnityShader中,只需要在特定的语句块中生命一些属性,就可以依靠材质来方便的改变这些属性。而且对于模型自带的数据(如定点位置、纹理坐标、法线等),UnityShader也提供了直接访问的方法,不需要开发者自行编码来传给着色器

  UnityShader出了上述这些优点外,也有一些缺点。

    由于UnityShader的高度封装性,可以编写的Shader类型和语法都被限制了。对于一些类型的Shader,例如曲面细分着色器(Tessellation Shader)、几何着色器(Geometry Shader)等,Unity的支持就相对差一些。例如:Unity 4.x 仅在 DirectX 11平台下提供曲面细分着色器、几何着色器的相关功能,而对于OpenGL平台则没有这些支持。除此之外,一些高级的Shader语法UnityShader也不支持

  可以说,UnityShader提供了一种让开发者同时控制渲染流水线中多个阶段的一种方式,不仅仅是提供Shader代码。作为开发者而言,绝大部分时候只需要和UnityShader打交道,而不需要关心渲染引擎底层的实现细节

UnityShader和Cg/HLSL之间的关系

  UnityShader是用ShaderLab语言编写的,但对于表面着色器和顶点/片元着色器,可以在ShaderLab内部嵌套Cg/HLSL语言来编写这些着色器代码。这些Cg/HLSL代码是嵌套在 CGPROGRAM 和 ENDCG 之间的。由于Cg和DX9风格的HLSL从写法上来说几乎是同一种语言,因此在Unity里Cg和HLSL是等价的。可以说,Cg/HLSL代码是区别于ShaderLab的另一个世界

  通常,Cg的代码片段是位于Pass语义块内部的,如下所示

Pass{
    //Pass的标签和状态设置

    CGPROGRAM
    //编译指令,例如:
    #pragma vertex vert
    #pragma fragment frag

    //Cg代码

    ENDCG
    //其他一些设置
}

  在提供给编程人员这些便利的背后,Unity编辑器会把这些Cg片段编译成低级语言,如汇编语言等。同城,Unity会自动把这些诶Cg片段编译到所有相关平台(这里的平台是指不同的渲染平台,如Dirext3D 9、OpenGL、Direct3D 11、OpenGL ES等)上。这些编译过程比较复杂,Unity会使用不同的编译器来吧Cg转换成对应平台的代码。这样就不会在切换平台时再重新编译,而且如果代码在某些平台上发生错误就可以立刻得到错误信息。

  可以在UnityShader的导入设置面板上查看这些编译后的代码,查看这些代码有助于进行Debug或优化等

  如图:

在UnityShader的导入设置面板中可以通过Compile and show code按钮来查看Unity对Cg片段编译后的代码,通过单击Compile and show按钮右侧的倒三角可以打开下拉菜单,在这个下拉菜单中可以选择变异的平台种类,如职位当前的显卡设备编译特定的汇编代码,或为所有的平台编译汇编代码,也可以自定义选择编译到哪些平台上
  但当发布游戏的时候,游戏数据文件中质保函目标平台需要的编译代码,而那些在目标平台上不需要的代码部分就会被移除。例如,当发布到Mac OS X平台上时,DirectX对应的代码部分就会被移除。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

qt基本窗口类(QWidget,QDialog,QMainWindow)

1.三个基本窗口类 1.1QWidget 所有窗口的基类 可以内嵌到其他窗口的内部,无边框 也可以作为独立窗口显示,有边框 1.2QDialog 继承于QWidget 对话框窗口类 不可以内嵌到其他窗口 有模态和非模态两种显示方式 1.3QMainWind 继承于QWidget 主窗口类 不可以…

SD卡无法读取?原因分析与数据恢复策略

一、SD卡无法读取的困境 SD卡作为便携式的存储介质,广泛应用于手机、相机、平板等多种电子设备中。然而,在使用过程中,我们可能会遭遇SD卡无法读取的困扰。当我们将SD卡插入设备时,设备无法识别SD卡,或者虽然识别了SD…

【调试笔记-20240619-Windows-Typescripts中类型不匹配的解决方法】

调试笔记-系列文章目录 调试笔记-20240619-Windows-Typescripts中类型不匹配的解决方法 文章目录 调试笔记-系列文章目录调试笔记-20240619-Windows-Typescripts中类型不匹配的解决方法 前言一、调试环境操作系统:Windows 10 专业版调试环境调试目标 二、调试步骤搜…

数据结构:小白专场:树的同构2程序框架、建树及同构判别

T1T2是之前提到的结构数组,是全局变量,作为参数传进去 调用Ismorphic这个函数判断R1R2是不是同构的 首先考虑如何构建BuidTree函数 输入数据的结构如左图 知道n之后,n不为0,循环,循环一次读入一行数据 为了处理方便…

FlowUs知识库践行“持续交付”“持续推广” 让内容创作者专注内容

FlowUs在推出订阅付费功能时,充分体现了软件工程领域中的持续交付(Continuous Delivery, CD)与持续部署(Continuous Deployment, CD)理念,将这一高效、敏捷的软件开发策略融入知识管理与变现平台之中&#…

如何实现外部编码器轴和虚轴电子齿轮比例随动(汇川AM400PLC)

1、如何添加虚轴可以参考下面文章链接: 如何添加虚轴(AM400PLC)-CSDN博客文章浏览阅读2次。EtherCAT运动控制总线启用的时候,选择EtherCAT总线任务周期。选择好后,选择点击添加。https://blog.csdn.net/m0_46143730/article/details/139898985?csdn_share_tail=%7B%22type…

嵌入式实验---实验五 串口数据接收实验

一、实验目的 1、掌握STM32F103串口数据接收程序设计流程; 2、熟悉STM32固件库的基本使用。 二、实验原理 1、STM32F103R6能通过查询中断方式接收数据,每接收到一个字节,立即向对方发送一个相同内容的字节,并把该字节的十六进…

艺体培训机构管理系统的设计

管理员账户功能包括:系统首页,个人中心,管理员管理,教师管理,学员管理,活动管理,课程管理,选课信息管理 前台账户功能包括:系统首页,个人中心,论…

stm32学习笔记---GPIO输出(理论部分)

目录 GPIO通用输入输出口 GPIO的基本结构 I/O端口位的基本结构 输入部分 输出部分 推挽模式 开漏模式 GPIO的8种工作模式 STM32手册GPIO和AFIO大致介绍 STM32外部的设备和电路 声明:本专栏是本人跟着B站江科大的视频的学习过程中记录下来的笔记&#xff0…

在LangChain中,LLM(大型语言模型)和LLM Chain的区别是什么?

简单来说,LLM是一个大型语言模型,而LLM Chain是由多个LLM或其他组件组成的链式结构,用于在LangChain中构建复杂的自然语言处理流程。 Direct LLM Interface: 直接大型语言模型(LLM)接口: llm Open…

已解决java.util.concurrent.BrokenBarrierException异常的正确解决方法,亲测有效!!!

已解决java.util.concurrent.BrokenBarrierException异常的正确解决方法,亲测有效!!! 目录 问题分析 出现问题的场景 报错原因 解决思路 解决方法 分析错误日志 检查线程中断 设置合理的等待时间 优化代码逻辑 使用同步…

http1.x和http2.0的一些区别

1、http2.0采用多路复用技术,可以同时发送多个请求或回应 2、http2.0可以由服务器主动向客户端推送数据 3、http2.0对头信息进行压缩,并维护一张信息表,生成头信息索引号,发送时只发送索引号

L57---112.路径总和(广搜)---Java版

1.题目描述 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。叶子节点 是指…

【C++】——二叉搜索树(详解)

一 二叉搜索树概念 二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树: ✨若它的左子树不为空,则左子树上所有节点的值都小于根节点的值 ✨若它的右子树不为空,则右子树上所有节点的值都大于根节点的值 …

使用python下载图片且批量将图片插入word文档

最近有一个小的功能实现,从小某书上下载指定帖子的图片们,然后批量插入到word文档中,便于打印。于是有了以上需求。 一、下载图片 1、首先获取图片们的链接img_urls 首先,获取到的指定帖子的所有信息可以存入一个json文件中&am…

Ubuntu/Linux SSH 端口转发

文章目录 Ubuntu/Linux SSH 端口转发概述本地端口转发场景一场景二 参考资料 Ubuntu/Linux SSH 端口转发 概述 SSH, Secure Shell 是一种在网络上用于安全远程登录到另一台机器的工具。除了远程登录以外,ssh 的端口转发是它的另一项强大功能。通过 ssh 端口转发功…

【会议征稿,ACM出版】2024年图像处理、智能控制与计算机工程国际学术会议(IPICE 2024,8月9-11)

2024年图像处理、智能控制与计算机工程国际学术会议(IPICE 2024)将于2024年8月9-11日在中国福州举行。本届会议由阳光学院、福建省空间信息感知与智能处理重点实验室、空间数据挖掘与应用福建省高校工程研究中心联合主办。 会议主要围绕图像处理、智能控…

颠覆传统编程:用ChatGPT十倍提升生产力

我们即将见证一个新的时代!这是最好的时代,也是最坏的时代! 需求背景 背景: 平时会编写博客,并且会把这个博客上传到github上,然后自己买一个域名挂到github上。 我平时编写的博客会有一些图片来辅助说明的…

已解决javax.management.BadStringOperationException异常的正确解决方法,亲测有效!!!

已解决javax.management.BadStringOperationException异常的正确解决方法,亲测有效!!! 目录 问题分析 出现问题的场景 报错原因 解决思路 解决方法 分析错误日志 检查字符串值合法性 确认字符串格式 优化代码逻辑 增加…

物联网技术-第6章-物联网应用案例

目录 1.共享单车 2.自动驾驶汽车 (1)概念 (2)关键技术 (3)典型代表 3.智能电网 4.智能交通 (1)车联网 (2)无人驾驶 5.智能物流 6.致谢 1.共享单车…