当前位置: 首页 > news >正文

《Shader入门精要》透明效果

代码以及实例图可以看github :zaizai77/Shader-Learn: 实现一些书里讲到的shader

在实时渲染中要实现透明效果,通常会在渲染模型时控制它的透明通道(Alpha Channel)​。当开启透明混合后,当一个物体被渲染到屏幕上时,每个片元除了颜色值和深度值之外,它还有另一个属性——透明度。当透明度为1时,表示该像素是完全不透明的,而当其为0时,则表示该像素完全不会显示。

我们通常使用两种方法来实现透明效果:第一种是使用透明度测试(Alpha Test)​,这种方法其实无法得到真正的半透明效果;另一种是透明度混合(Alpha Blending)​。

在实时渲染中,深度缓冲是用于解决可见性(visibility)问题的,它可以决定哪个物体的哪些部分会被渲染在前面,而哪些部分会被其他物体遮挡。它的基本思想是:根据深度缓存中的值来判断该片元距离摄像机的距离,当渲染一个片元时,需要把它的深度值和已经存在于深度缓冲中的值进行比较(如果开启了深度测试)​,如果它的值距离摄像机更远,那么说明这个片元不应该被渲染到屏幕上(有物体挡住了它)​;否则,这个片元应该覆盖掉此时颜色缓冲中的像素值,并把它的深度值更新到深度缓冲中(如果开启了深度写入)​。

简单来说,透明度测试和透明度混合的基本原理如下。

透明度测试:它采用一种“霸道极端”的机制,只要一个片元的透明度不满足条件(通常是小于某个阈值)​,那么它对应的片元就会被舍弃。被舍弃的片元将不会再进行任何处理,也不会对颜色缓冲产生任何影响;否则,就会按照普通的不透明物体的处理方式来处理它,即进行深度测试、深度写入等。也就是说,透明度测试是不需要关闭深度写入的,它和其他不透明物体最大的不同就是它会根据透明度来舍弃一些片元。虽然简单,但是它产生的效果也很极端,要么完全透明,即看不到,要么完全不透明,就像不透明物体那样。

透明度混合:这种方法可以得到真正的半透明效果。它会使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新的颜色。但是,透明度混合需要关闭深度写入(我们下面会讲为什么需要关闭)​,这使得我们要非常小心物体的渲染顺序。需要注意的是,透明度混合只关闭了深度写入,但没有关闭深度测试。这意味着,当使用透明度混合渲染一个片元时,还是会比较它的深度值与当前深度缓冲中的深度值,如果它的深度值距离摄像机更远,那么就不会再进行混合操作。这一点决定了,当一个不透明物体出现在一个透明物体的前面,而我们先渲染了不透明物体,它仍然可以正常地遮挡住透明物体。也就是说,对于透明度混合来说,深度缓冲是只读的。

为什么渲染顺序重要

如果不关闭深度写入,一个半透明表面背后的表面本来是可以透过它被我们看到的,但由于深度测试时判断结果是该半透明表面距离摄像机更近,导致后面的表面将会被剔除,我们也就无法透过半透明表面看到后面的物体了

渲染引擎一般都会先对物体进行排序,再渲染。常用的方法是。

  1. 先渲染所有不透明物体,并开启它们的深度测试和深度写入。
  2. 把半透明物体按它们距离摄像机的远近进行排序,然后按照从后往前的顺序渲染这些半透明物体,并开启它们的深度测试,但关闭深度写入。

在一些情况下,半透明物体还是会出现“穿帮镜头”​。深度缓冲中的值其实是像素级别的,即每个像素有一个深度值,但是现在我们对单个物体级别进行排序,这意味着排序结果是,要么物体A全部在B前面渲染,要么A全部在B后面渲染。但如果存在循环重叠的情况,那么使用这种方法就永远无法得到正确的结果

为了减少错误排序的情况,我们可以尽可能让模型是凸面体,并且考虑将复杂的模型拆分成可以独立排序的多个子模型等。其实就算排序错误结果有时也不会非常糟糕,如果我们不想分割网格,可以试着让透明通道更加柔和,使穿插看起来并不是那么明显。我们也可以使用开启了深度写入的半透明效果来近似模拟物体的半透明

Unity Shader 的渲染顺序

Unity为了解决渲染顺序的问题提供了渲染队列(render queue)这一解决方案。我们可以使用SubShader的Queue标签来决定我们的模型将归于哪个渲染队列。Unity在内部使用一系列整数索引来表示每个渲染队列,且索引号越小表示越早被渲染

因此,如果我们想要通过透明度测试实现透明效果,代码中应该包含类似下面的代码:

        SubShader  {Tags  {  "Queue"="AlphaTest"  }Pass  {...}}

如果我们想要通过透明度混合来实现透明效果,代码中应该包含类似下面的代码:

        SubShader  {Tags  {  "Queue"="Transparent"  }Pass  {ZWrite  Off...}}

ZWrite Off用于关闭深度写入,在这里我们选择把它写在Pass中。我们也可以把它写在SubShader中,这意味着该SubShader下的所有Pass都会关闭深度写入。

透明度测试

透明度测试:只要一个片元的透明度不满足条件(通常是小于某个阈值)​,那么它对应的片元就会被舍弃。被舍弃的片元将不会再进行任何处理,也不会对颜色缓冲产生任何影响;否则,就会按照普通的不透明物体的处理方式来处理它。

通常,我们会在片元着色器中使用clip函数来进行透明度测试。clip是CG中的一个函数,它的定义如下。

函数:void clip(float4 x); void clip(float3 x); void clip(float2 x); void clip(float1 x); void clip(float x);参数:裁剪时使用的标量或矢量条件。

描述:如果给定参数的任何一个分量是负数,就会舍弃当前像素的输出颜色。它等同于下面的代码:

void clip(float4 x) {

if (any(x < 0))

        discard;

}

这一节使用的纹理:

        SubShader {Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"}Pass  {Tags  {  "LightMode"="ForwardBase"  }

在Unity中透明度测试使用的渲染队列是名为AlphaTest的队列,因此我们需要把Queue标签设置为AlphaTest。而RenderType标签可以让Unity把这个Shader归入到提前定义的组(这里就是TransparentCutout组)中,以指明该Shader是一个使用了透明度测试的Shader。RenderType标签通常被用于着色器替换功能。我们还把IgnoreProjector设置为True,这意味着这个Shader不会受到投影器(Projectors)的影响。通常,使用了透明度测试的Shader都应该在SubShader中设置这三个标签。

        fixed4  frag(v2f  i)  :  SV_Target  {fixed3  worldNormal  =  normalize(i.worldNormal);fixed3  worldLightDir  =  normalize(UnityWorldSpaceLightDir(i.worldPos));fixed4  texColor  =  tex2D(_MainTex,  i.uv);//  Alpha  testclip  (texColor.a  -  _Cutoff);//  Equal  to//   if  ((texColor.a  -  _Cutoff)  <  0.0)  {//        discard;//   }fixed3  albedo  =  texColor.rgb  *  _Color.rgb;fixed3  ambient  =  UNITY_LIGHTMODEL_AMBIENT.xyz  *  albedo;fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));return  fixed4(ambient  +  diffuse,  1.0);}

前面我们已经提到过clip函数的定义,它会判断它的参数,即texColor.a - _Cutoff是否为负数,如果是就会舍弃该片元的输出。也就是说,当texColor.a小于材质参数_Cutoff时,该片元就会产生完全透明的效果。使用clip函数等同于先判断参数是否小于零,如果是就使用discard指令来显式剔除该片元。后面的代码和之前使用过的完全一样,我们计算得到环境光照和漫反射光照,把它们相加后再进行输出。

Shader "Custom/Chapter8/AlphaTest"
{Properties{_Color("Color Tint", Color) = (1, 1, 1, 1)_MainTex("Main Tex", 2D) = "white" {}_Cutoff("Alpha Cutoff", Range(0, 1)) = 0.5}SubShader{Tags {"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout"}Pass {Tags { "LightMode" = "ForwardBase" }CGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"fixed4 _Color;sampler2D _MainTex;float4 _MainTex_ST;fixed _Cutoff;struct a2v {float4 vertex : POSITION;float3 normal : NORMAL;float4 texcoord : TEXCOORD0;};struct v2f {float4 pos : SV_POSITION;float3 worldNormal : TEXCOORD0;float3 worldPos : TEXCOORD1;float2 uv : TEXCOORD2;};v2f vert(a2v v) {v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);return o;}fixed4 frag(v2f i) : SV_Target {fixed3 worldNormal = normalize(i.worldNormal);fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));fixed4 texColor = tex2D(_MainTex, i.uv);// Alpha testclip(texColor.a - _Cutoff);// Equal to //				if ((texColor.a - _Cutoff) < 0.0) {//					discard;//				}fixed3 albedo = texColor.rgb * _Color.rgb;fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));return fixed4(ambient + diffuse, 1.0);}ENDCG}}
}

透明度混合

透明度混合:这种方法可以得到真正的半透明效果。它会使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新的颜色。但是,透明度混合需要关闭深度写入,这使得我们要非常小心物体的渲染顺序

为了进行混合,我们需要使用Unity提供的混合命令——Blend。Blend是Unity提供的设置混合模式的命令。想要实现半透明的效果就需要把当前自身的颜色和已经存在于颜色缓冲中的颜色值进行混合,混合时使用的函数就是由该指令决定的

在这里我们使用第二个语义,我们会把源颜色的混合因子SrcFactor设为SrcAlpha,而目标颜色的混合因子DstFactor设为OneMinusSrcAlpha。这意味着,经过混合后新的颜色是:

        SubShader  {Tags  {"Queue"="Transparent"  "IgnoreProjector"="True"  "RenderType"="Transparent"}

在Unity中透明度混合使用的渲染队列是名为Transparent的队列,因此我们需要把Queue标签设置为Transparent。RenderType标签可以让Unity把这个Shader归入到提前定义的组(这里就是Transparent组)中,用来指明该Shader是一个使用了透明度混合的Shader。RenderType标签通常被用于着色器替换功能

        Pass  {Tags  {  "LightMode"="ForwardBase"  }ZWrite  OffBlend  SrcAlpha  OneMinusSrcAlpha

与透明度测试不同的是,我们还需要在Pass中为透明度混合进行合适的混合状态设置:

把该Pass的深度写入(ZWrite)设置为关闭状态(Off)​

我们将源颜色(该片元着色器产生的颜色)的混合因子设为SrcAlpha,把目标颜色(已经存在于颜色缓冲中的颜色)的混合因子设为OneMinusSrcAlpha,以得到合适的半透明效果。

        fixed4  frag(v2f  i)  :  SV_Target  {fixed3  worldNormal  =  normalize(i.worldNormal);fixed3  worldLightDir  =  normalize(UnityWorldSpaceLightDir(i.worldPos));fixed4  texColor  =  tex2D(_MainTex,  i.uv);fixed3  albedo  =  texColor.rgb  *  _Color.rgb;fixed3  ambient  =  UNITY_LIGHTMODEL_AMBIENT.xyz  *  albedo;fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));return  fixed4(ambient  +  diffuse,  texColor.a  *  _AlphaScale);}
Shader "Custom/Chapter8/AlphaBlend"
{Properties{_Color("Color Tint", Color) = (1, 1, 1, 1)_MainTex("Main Tex", 2D) = "white" {}_AlphaScale("Alpha Scale", Range(0, 1)) = 1}SubShader{Tags {"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}Pass {Tags { "LightMode" = "ForwardBase" }ZWrite OffBlend SrcAlpha OneMinusSrcAlphaCGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"fixed4 _Color;sampler2D _MainTex;float4 _MainTex_ST;fixed _AlphaScale;struct a2v {float4 vertex : POSITION;float3 normal : NORMAL;float4 texcoord : TEXCOORD0;};struct v2f {float4 pos : SV_POSITION;float3 worldNormal : TEXCOORD0;float3 worldPos : TEXCOORD1;float2 uv : TEXCOORD2;};v2f vert(a2v v) {v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);return o;}fixed4 frag(v2f i) : SV_Target {fixed3 worldNormal = normalize(i.worldNormal);fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));fixed4 texColor = tex2D(_MainTex, i.uv);fixed3 albedo = texColor.rgb * _Color.rgb;fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));return fixed4(ambient + diffuse, texColor.a * _AlphaScale);}ENDCG}}
}

当模型本身有复杂的遮挡关系或是包含了复杂的非凸网格的时候,就会有各种各样因为排序错误而产生的错误的透明效果(关闭深度写入带来的问题),因为这样我们就无法对模型进行像素级别的深度排序

开启深度写入的半透明效果

上一节中我们给出了一种由于关闭深度写入而造成的错误排序的情况。一种解决方法是使用两个Pass来渲染模型:第一个Pass开启深度写入,但不输出颜色,它的目的仅仅是为了把该模型的深度值写入深度缓冲中;第二个Pass进行正常的透明度混合,由于上一个Pass已经得到了逐像素的正确的深度信息,该Pass就可以按照像素级别的深度排序结果进行透明渲染。但这种方法的缺点在于,多使用一个Pass会对性能造成一定的影响。在本节最后,我们可以得到类似下图中的效果。可以看出,使用这种方法,我们仍然可以实现模型与它后面的背景混合的效果,但模型内部之间不会有任何真正的半透明效果。

Shader "Custom/Chapter8/AlphaBlendZWrite"
{Properties{_Color("Color Tint", Color) = (1, 1, 1, 1)_MainTex("Main Tex", 2D) = "white" {}_AlphaScale("Alpha Scale", Range(0, 1)) = 1}SubShader{Tags {"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}// Extra pass that renders to depth buffer onlyPass {ZWrite OnColorMask 0}Pass {Tags { "LightMode" = "ForwardBase" }ZWrite OffBlend SrcAlpha OneMinusSrcAlphaCGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"fixed4 _Color;sampler2D _MainTex;float4 _MainTex_ST;fixed _AlphaScale;struct a2v {float4 vertex : POSITION;float3 normal : NORMAL;float4 texcoord : TEXCOORD0;};struct v2f {float4 pos : SV_POSITION;float3 worldNormal : TEXCOORD0;float3 worldPos : TEXCOORD1;float2 uv : TEXCOORD2;};v2f vert(a2v v) {v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);return o;}fixed4 frag(v2f i) : SV_Target {fixed3 worldNormal = normalize(i.worldNormal);fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));fixed4 texColor = tex2D(_MainTex, i.uv);fixed3 albedo = texColor.rgb * _Color.rgb;fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));return fixed4(ambient + diffuse, texColor.a * _AlphaScale);}ENDCG}}
}

新增加了一个Pass,新添加的Pass的目的仅仅是为了把模型的深度信息写入深度缓冲中,从而剔除模型中被自身遮挡的片元。因此,Pass的第一行开启了深度写入。在第二行,我们使用了一个新的渲染命令——ColorMask。在ShaderLab中,ColorMask用于设置颜色通道的写掩码(write mask)​。它的语义如下:

ColorMask RGB | A | 0 | 其他任何R、G、B、A的组合

当ColorMask设为0时,意味着该Pass不写入任何颜色通道,即不会输出任何颜色。这正是我们需要的——该Pass只需写入深度缓存即可。

ShaderLab 的混合命令

首先来看一下混合是如何实现的。当片元着色器产生一个颜色的时候,可以选择与颜色缓存中的颜色进行混合。这样一来,混合就和两个操作数有关:源颜色(source color)目标颜色(destination color)​。源颜色,我们用S表示,指的是由片元着色器产生的颜色值;目标颜色,我们用D表示,指的是从颜色缓冲中读取到的颜色值。对它们进行混合后得到的输出颜色,我们用O表示,它会重新写入到颜色缓冲中。需要注意的是,当我们谈及混合中的源颜色、目标颜色和输出颜色时,它们都包含了RGBA四个通道的值,而并非仅仅是RGB通道。

想要使用混合,我们必须首先开启它。在Unity中,当我们使用Blend(Blend Off命令除外)命令时,除了设置混合状态外也开启了混合

混合等式和参数

现在,我们已知两个操作数:源颜色S和目标颜色D,想要得到输出颜色O就必须使用一个等式来计算。我们把这个等式称为混合等式(blend equation)​。当进行混合时,我们需要使用两个混合等式:一个用于混合RGB通道,一个用于混合A通道。当设置混合状态时,我们实际上设置的就是混合等式中的操作因子。在默认情况下,混合等式使用的操作都是加操作(我们也可以使用其他操作)​,我们只需要再设置一下混合因子即可。由于需要两个等式(分别用于混合RGB通道和A通道)​,每个等式有两个因子(一个用于和源颜色相乘,一个用于和目标颜色相乘)​,因此一共需要4个因子。表8.3给出了ShaderLab中设置混合因子的命令。

混合操作

我们可以使用ShaderLab的 BlendOp BlendOperation 命令,即混合操作命令

        // 正常(Normal),即透明度混合Blend  SrcAlpha  OneMinusSrcAlpha// 柔和相加(Soft Additive)Blend  OneMinusDstColor  One// 正片叠底(Multiply),即相乘Blend  DstColor  Zero// 两倍相乘(2x Multiply)Blend  DstColor  SrcColor// 变暗(Darken)BlendOp  MinBlend  One  One// 变亮(Lighten)BlendOp  MaxBlend  One  One// 滤色(Screen)Blend  OneMinusDstColor  One// 等同于Blend  One  OneMinusSrcColor// 线性减淡(Linear Dodge)Blend  One  One

不同混合状态设置得到的效果

双面渲染的透明效果

如果一个物体是透明的,意味着我们不仅可以透过它看到其他物体的样子,也可以看到它内部的结构。

但在前面实现的透明效果中,无论是透明度测试还是透明度混合,我们都无法观察到正方体内部及其背面的形状,导致物体看起来就好像只有半个一样。这是因为,默认情况下渲染引擎剔除了物体背面(相对于摄像机的方向)的渲染图元,而只渲染了物体的正面。如果我们想要得到双面渲染的效果,可以使用Cull指令来控制需要剔除哪个面的渲染图元。在Unity中,Cull指令的语法如下:

Cull Back | Front | Off

如果设置为Back,那么那些背对着摄像机的渲染图元就不会被渲染,这也是默认情况下的剔除状态;如果设置为Front,那么那些朝向摄像机的渲染图元就不会被渲染;如果设置为Off,就会关闭剔除功能,那么所有的渲染图元都会被渲染,但由于这时需要渲染的图元数目会成倍增加,因此除非是用于特殊效果,例如这里的双面渲染的透明效果,通常情况是不会关闭剔除功能的。

透明度测试的双面渲染

        Pass  {Tags  {  "LightMode"="ForwardBase"  }//  Turn  off  cullingCull  Off

 此时,我们可以透过正方体的镂空区域看到内部的渲染结果。

透明度混合的双面渲染

和透明度测试相比,想要让透明度混合实现双面渲染会更复杂一些,这是因为透明度混合需要关闭深度写入

我们选择把双面渲染的工作分成两个Pass——第一个Pass只渲染背面,第二个Pass只渲染正面,由于Unity会顺序执行SubShader中的各个Pass,因此我们可以保证背面总是在正面被渲染之前渲染,从而可以保证正确的深度渲染关系。

        Shader  "Unity  Shaders  Book/Chapter  8/Alpha  Blend  With  Both  Side"  {Properties  {_Color  ("Main  Tint",  Color)  =  (1,1,1,1)_MainTex  ("Main  Tex",  2D)  =  "white"  {}_AlphaScale  ("Alpha  Scale",  Range(0,  1))  =  1}SubShader  {Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}Pass  {Tags  {  "LightMode"="ForwardBase"  }//  First  pass  renders  only  back  facesCull  Front// 和之前一样的代码}Pass  {Tags  {  "LightMode"="ForwardBase"  }//  Second  pass  renders  only  front  facesCull  Back// 和之前一样的代码}}Fallback  "Transparent/VertexLit"}
Shader "Custom/Chapter8/AlphaBlendBothSided"
{Properties{_Color("Color Tint", Color) = (1, 1, 1, 1)_MainTex("Main Tex", 2D) = "white" {}_AlphaScale("Alpha Scale", Range(0, 1)) = 1}SubShader{Tags {"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}Pass {Tags { "LightMode" = "ForwardBase" }// First pass renders only back faces Cull FrontZWrite OffBlend SrcAlpha OneMinusSrcAlphaCGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"fixed4 _Color;sampler2D _MainTex;float4 _MainTex_ST;fixed _AlphaScale;struct a2v {float4 vertex : POSITION;float3 normal : NORMAL;float4 texcoord : TEXCOORD0;};struct v2f {float4 pos : SV_POSITION;float3 worldNormal : TEXCOORD0;float3 worldPos : TEXCOORD1;float2 uv : TEXCOORD2;};v2f vert(a2v v) {v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);return o;}fixed4 frag(v2f i) : SV_Target {fixed3 worldNormal = normalize(i.worldNormal);fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));fixed4 texColor = tex2D(_MainTex, i.uv);fixed3 albedo = texColor.rgb * _Color.rgb;fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));return fixed4(ambient + diffuse, texColor.a * _AlphaScale);}ENDCG}Pass {Tags { "LightMode" = "ForwardBase" }// Second pass renders only front faces Cull BackZWrite OffBlend SrcAlpha OneMinusSrcAlphaCGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"fixed4 _Color;sampler2D _MainTex;float4 _MainTex_ST;fixed _AlphaScale;struct a2v {float4 vertex : POSITION;float3 normal : NORMAL;float4 texcoord : TEXCOORD0;};struct v2f {float4 pos : SV_POSITION;float3 worldNormal : TEXCOORD0;float3 worldPos : TEXCOORD1;float2 uv : TEXCOORD2;};v2f vert(a2v v) {v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);return o;}fixed4 frag(v2f i) : SV_Target {fixed3 worldNormal = normalize(i.worldNormal);fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));fixed4 texColor = tex2D(_MainTex, i.uv);fixed3 albedo = texColor.rgb * _Color.rgb;fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));return fixed4(ambient + diffuse, texColor.a * _AlphaScale);}ENDCG}}
}

相关文章:

《Shader入门精要》透明效果

代码以及实例图可以看github &#xff1a;zaizai77/Shader-Learn: 实现一些书里讲到的shader 在实时渲染中要实现透明效果&#xff0c;通常会在渲染模型时控制它的透明通道&#xff08;Alpha Channel&#xff09;​。当开启透明混合后&#xff0c;当一个物体被渲染到屏幕上时&…...

一篇文章了解Linux

目录 一&#xff1a;命令 1 ls命令作用 2 目录切换命令&#xff08;cd/pwd&#xff09; &#xff08;1)cd切换工作目录命令 3 相对路径、绝对路径和特殊路径 (1)相对路径和绝对路径的概念和写法 (2)几种特殊路径的表示符 (3)练习题&#xff1a; 4 创建目录命令&#x…...

创建字典时,键名不能是数字

4. 请问下面创建字典的 8 种方法中&#xff0c;哪几种是正确的。 >>> a {99:"吕布", 90:"关羽", 60:"刘备"}>>> b dict(99:"吕布", 90:"关羽", 60:"刘备")>>> c dict(99"吕布…...

架构-微服务-环境搭建

文章目录 前言一、案例准备1. 技术选型2. 模块设计3. 微服务调用 二、创建父工程三、创建基础模块四、创建用户微服务五、创建商品微服务六、创建订单微服务 前言 ‌微服务环境搭建‌ 使用的电商项目中的商品、订单、用户为案例进行讲解。 一、案例准备 1. 技术选型 maven&a…...

mysql根据日期查询没有的日期也要显示数据

先查询出日期数据(当前日期往前推12个月) select bb.datefrom (select num : num 1,date_format(adddate(date_sub(date_sub(curdate(),interval 12 month),interval 1 month),interval num month), %Y-%m) as datefrom mysql.help_topic,(select num : 0) as twhere addd…...

ArcGIS定义投影与投影的区别(数据和底图不套合的原因和解决办法)

今天介绍一下ArcGIS中定义投影与投影的区别。 给大家解惑一下为什么经常出现自己的数据无法和底图套合的情况。 一 目录 1、ArcGIS定义投影与投影的概念区别 2、ArcGIS定义正确的坐标系 3、ArcGIS动态投影实现套合 4、ArcGIS地理坐标系转投影坐标系&#xff08;错误做法&am…...

SQL Server数据库日志(ldf文件)清理

随着系统运行时间的推移&#xff0c;数据库日志文件会变得越来越大&#xff0c;这时我们需要对日志文件进行备份或清理。 下面是日常运维中比较常用的日志清理SQL语句 --- 查询数据库log名称 USE testdb SELECT name, physical_name FROM sys.master_files WHERE database_id …...

ubuntu 安装proxychains

在Ubuntu上安装Proxychains&#xff0c;你可以按照以下步骤操作&#xff1a; 1、更新列表 sudo apt-update 2、安装Proxychains sudo apt-get install proxychains 3、安装完成后&#xff0c;你可以通过编辑/etc/proxychains.conf文件来配置代理规则 以下是一个简单的配置示例&…...

Maven学习笔记

Maven功能介绍 提供了一套标准化的项目结构提供了一套标准化的构建流程&#xff08;编译、测试、打包、发布.....&#xff09;提供了一套依赖管理机制 依赖管理其实就是管理你项目所依赖的第三方资源&#xff08;jar包、插件...&#xff09; ①Maven使用标准的坐标配置来管理…...

.net 支持跨平台(桌面)系列技术汇总

1. 首先微软老大哥的.net core 。 .NET Core 是微软开发的一个跨平台、高性能的开源框架&#xff0c;用于构建云和互联网连接的新型应用。 它允许开发者在 Windows、macOS 和 Linux 上使用喜爱的开发工具进行开发&#xff0c;并支持部署到云或本地环境。 .NET Core 是对 .NET …...

[Golang]传递一个切片(slice)和使用变参(...)语法传递多个参数之间的区别

在 Go 中&#xff0c;传递一个切片&#xff08;slice&#xff09;和使用变参&#xff08;…&#xff09;语法传递多个参数之间有一些关键区别。让我们详细讨论这两种方式之间的区别&#xff1a; 传递切片&#xff08;Slice&#xff09; 传递方式&#xff1a; 传递切片时&…...

【PGCCC】Postgresql BRIN 索引原理

前言 postgresql 提供了块级索引&#xff08;简称 BRIN&#xff09;&#xff0c;主要适用于类似时序数据之类的&#xff0c;有着天然的顺序&#xff0c;而且都是添加写的场景。相比于 btree 索引&#xff0c;它的体积小得多&#xff0c;非常适用于大数据量的场景。 原理 pos…...

安全加固方案

交换机安全加固 查看是否关闭未使用的接口 25GE1/0/1、25GE1/0/47、25GE1/0/48需要使用&#xff0c;暂不关闭 system-view # interface Eth-Trunk99 shutdown quit interface Eth-Trunk100 shutdown quit interface Eth-Trunk110 shutdown quit interface 25GE1/…...

Adobe Illustrator 2024 安装教程与下载分享

介绍一下 下载直接看文章末尾 Adobe Illustrator 是一款由Adobe Systems开发的矢量图形编辑软件。它广泛应用于创建和编辑矢量图形、插图、徽标、图标、排版和广告等领域。以下是Adobe Illustrator的一些主要特点和功能&#xff1a; 矢量绘图&#xff1a;Illustrator使用矢量…...

WSL安装不同版本ubuntu(已有ubuntu20.04,再装ubuntu18.04)

参考&#xff1a; 如何在 WSL 中删除指定版本的 Ubuntu&#xff08;以删除 Ubuntu 22.04 为例&#xff09;_wsl卸载某个-CSDN博客 已有ubuntu20.04&#xff0c;现在再安装一个ubuntu18.04 直接参考下面我写的链接的第四步&#xff0c;前面的步骤都不需要再做了 Win11安装WSL…...

指针测试总结(一)(一维数组)

1.取一维数组的首地址 int main() {int arr[3] {5,8,1}; printf("%d\n",arr);printf("%d\n",&arr);printf("%d\n",&arr[0]);printf("%d\n",&arr0); }输出结果&#xff1a; 1096809108 1096809108 1096809108 1096809108…...

CentOS环境上离线安装python3及相关包

0. 准备操作系统及安装包 准备操作系统环境&#xff1a; 首先安装依赖包&#xff0c;安装相应的编译工具 [rootbigdatahost bin]# yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-d…...

Matlab 深度学习工具箱 案例学习与测试————求二阶微分方程

clc clear% 定义输入变量 x linspace(0,2,10000);% 定义网络的层参数 inputSize 1; layers [featureInputLayer(inputSize,Normalization"none")fullyConnectedLayer(10)sigmoidLayerfullyConnectedLayer(1)sigmoidLayer]; % 创建网络 net dlnetwork(layers);% 训…...

算法笔记:回溯算法

回溯法理论基础 「回溯是递归的副产品&#xff0c;只要有递归就会有回溯」&#xff0c;所以回溯法也经常和二叉树遍历&#xff0c;深度优先搜索混在一起&#xff0c;因为这两种方式都是用了递归。 回溯法就是暴力搜索&#xff0c;并不是什么高效的算法&#xff0c;最多再剪枝一…...

[工具分享] 根据Excel数据根据Word文档模板,批量创建生成Word文档并重命名,方便快速查找打印

前几天交楼的小姐姐要多份Word文档合同打印给客户&#xff0c;那么100份就需要修改100次 上面好多都是模板的制式文件&#xff0c;里面的部分数据都是要根据实际值来变动的&#xff0c; 那么有没有快速的方法来操作呢&#xff0c;还是只能一个个手动的改&#xff0c;又容易出…...

数据库的联合查询

数据库的联合查询 简介为什么要使⽤联合查询多表联合查询时MYSQL内部是如何进⾏计算的构造练习案例数据案例&#xff1a;⼀个完整的联合查询的过程 内连接语法⽰例 外连接语法 ⽰例⾃连接应⽤场景示例表连接练习 ⼦查询语法单⾏⼦查询多⾏⼦查询多列⼦查询在from⼦句中使⽤⼦查…...

DICOM图像深入解析:为何部分DR/CR图像默认显示为反色?

概述 在数字医学影像处理中,CR(Computed Radiography,计算机放射摄影)和DR(Digital Radiography,数字放射摄影)技术广泛应用于医疗影像获取与分析。然而,临床实践中常常遇到这样一个问题:部分CR/DR图像在默认打开时呈现为反色(即负片效果),需手动反色后才能正常阅片…...

C#基础上机练习题

21.计算500-800区间内素数的个数cn&#xff0c;并按所求素数的值从大到小的顺序排列&#xff0c;再计算其间隔加、减之和&#xff0c;即第1个素数-第2个素数第3个素数-第4个素数第5个素数……的值sum。请编写函数实现程序的要求&#xff0c;把结果cn和sum输出。 22.在三位整数…...

UE5 实现组合键触发事件的方法

因为工作原因。 需要用大括号{和}来触发事件 但是在蓝图中搜了一下&#xff0c;发现键盘事件里根本就没有{}这两个键。 花费了一下午&#xff0c;终于找到解决的方法了&#xff0c;也就是增强输入的弦操作 首先创建一个项目 纯蓝图或者C都可行 进入到内容浏览器的默认页面 …...

Linux麦克风录音实战

在 Linux 上使用麦克风进行录音可以通过多种方式实现&#xff0c;包括使用命令行工具、图形界面应用程序以及编程接口。下面我将介绍几种常见的方法&#xff0c;从简单的命令行工具到使用 PortAudio 库进行编程。 一. 使用arecord命令行工具 arecord 是 ALSA&#xff08;Adva…...

Diving into the STM32 HAL-----Timers笔记

嵌入式设备会按时间执行某些活动。对于真正简单且不准确的延迟&#xff0c;繁忙的循环可以执行任务&#xff0c;但是使用 CPU 内核执行与时间相关的活动从来都不是一个聪明的解决方案。因此&#xff0c;所有微控制器都提供专用的硬件外设&#xff1a;定时器。定时器不仅是时基生…...

RPC学习

一、什么是 RPC RPC&#xff08;Remote Procedure Call&#xff09;&#xff0c;即远程过程调用&#xff0c;是一种计算机通信协议&#xff0c;它允许运行在一台计算机上的程序调用另一台计算机上的子程序或函数&#xff0c;就好像调用本地程序中的函数一样&#xff0c;无需程序…...

STM32端口模拟编码器输入

文章目录 前言一、正交编码器是什么&#xff1f;二、使用步骤2.1开启时钟2.2配置编码器引脚 TIM3 CH1(PA6) CH2 (PA7)上拉输入2.3.初始化编码器时基2.4 初始化编码器输入2.5 配置编码器接口2.6 开启定时器2.7获取编码器数据 三、参考程序四、测试结果4.1测试方法4.2串口输出结果…...

深入理解 MyBatis 的缓存机制:一级缓存与二级缓存

MyBatis 是目前 Java 开发中常用的一种 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;它不仅简化了 SQL 语句的编写和管理&#xff0c;还提供了强大的缓存机制&#xff0c;用以提高数据库访问的性能。MyBatis 的缓存分为一级缓存和二级缓存&#xff0c;分别应用于不…...

做一个FabricJS.cc的中文文档网站——面向markdown编程

&#x1f4e2;欢迎点赞 &#xff1a;&#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff0c;赐人玫瑰&#xff0c;手留余香&#xff01;&#x1f4e2;本文作者&#xff1a;由webmote 原创&#x1f4e2;作者格言&#xff1a;新的征程&#xff0c;用爱发电&#…...

Elasticsearch在部署时,对Linux的设置有哪些优化方法?

大家好&#xff0c;我是锋哥。今天分享关于【Elasticsearch在部署时&#xff0c;对Linux的设置有哪些优化方法&#xff1f;】面试题。希望对大家有帮助&#xff1b; Elasticsearch在部署时&#xff0c;对Linux的设置有哪些优化方法&#xff1f; 1000道 互联网大厂Java工程师 精…...

ThingsBoard规则链节点:Azure IoT Hub 节点详解

目录 引言 1. Azure IoT Hub 节点简介 2. 节点配置 2.1 基本配置示例 3. 使用场景 3.1 数据传输 3.2 数据分析 3.3 设备管理 4. 实际项目中的应用 4.1 项目背景 4.2 项目需求 4.3 实现步骤 5. 总结 引言 ThingsBoard 是一个开源的物联网平台&#xff0c;提供了设备…...

SpringBoot线程池的使用

SpringBoot线程池的使用 在现代Web应用开发中&#xff0c;特别是在使用Spring Boot框架时&#xff0c;合理使用线程池可以显著提高应用的性能和响应速度。线程池不仅能够减少线程创建和销毁的开销&#xff0c;还能有效地控制并发任务的数量&#xff0c;避免因线程过多而导致的…...

ubuntu中使用ffmpeg和nginx推流rtmp视频

最近在测试ffmpeg推流rtmp视频&#xff0c;单独安装ffmpeg是无法完成推流的&#xff0c;需要一个流媒体服务器&#xff0c;常用nginx&#xff0c;可以直接在ubuntu虚拟机里面测试一下。 测试过程不涉及编译ffmpeg和nginx&#xff0c;仅使用基本功能&#xff1a; 1 安装ffmpeg …...

如何在CodeIgniter中添加或加载模型

在CodeIgniter框架中&#xff0c;模型&#xff08;Model&#xff09;是用于与数据库进行交互的重要组件。模型通常包含数据库查询、业务逻辑以及与数据库表相关的函数。以下是如何在CodeIgniter中添加或加载模型的步骤&#xff1a; 1. 创建模型文件 首先&#xff0c;你需要在…...

加菲工具 - 好用免费的在线工具集合

加菲工具 https://orcc.online AI 工具 加菲工具 集合了目前主流的&#xff0c;免费可用的ai工具 文档处理 加菲工具 pdf转word、office与pdf互转等等工具都有链接 图片图标 加菲工具 统计了好用免费的在线工具 编码解码 加菲工具 base64编码解码、url编码解码、md5计算…...

Centos 8, add repo

Centos repo前言 Centos 8更换在线阿里云创建一键更换repo 自动化脚本 华为Centos 源 , 阿里云Centos 源 华为epel 源 , 阿里云epel 源vim /centos8_repo.sh #!/bin/bash # -*- coding: utf-8 -*- # Author: make.han...

神经网络12-Time-Series Transformer (TST)模型

Time-Series Transformer (TST) 是一种基于 Transformer 架构的深度学习模型&#xff0c;专门用于时序数据的建模和预测。TST 是 Transformer 模型的一个变种&#xff0c;针对传统时序模型&#xff08;如 RNN、LSTM&#xff09;在处理长时间依赖、复杂数据关系时的限制而提出的…...

在 Ubuntu 上安装 Yarn 环境

在 Ubuntu 上安装 Yarn 环境 步骤 1: 更新系统步骤 2: 安装 Node.js步骤 3: 安装 Yarn方法 1: 使用 npm 安装方法 2: 使用 APT 安装 步骤 4: 验证安装总结 在 Ubuntu 上安装 Yarn 环境可以通过以下步骤完成&#xff1a; 步骤 1: 更新系统 首先&#xff0c;确保你的系统是最新…...

OEM sql monitoring 类似SQL

不够OEM 全面 select a.inst_id,a.time_since_last_wait_micro,a.last_call_et,a.sql_id,a.sql_hash_value, a.username,a.pdml_enabled,a.pdml_status ,a.prev_sql_id from gv$session a where a.statusACTIVE and a.sql_id is not null and a.username is not null select …...

【数据分析】基于GEE实现大津算法提取洞庭湖流域水体

大津算法提取水体 1.写在前面2.洞庭湖水体识别1.写在前面 最大类间方差法,也称为Otsu或大津法,是一种高效的图像二值化算法,由日本学者Otsu于1979年提出。该算法基于图像的频率分布直方图,假设图像包含两类像素(前景和背景),并计算出一个最佳阈值,以最大化类间方差,从…...

MySQL中的ROW_NUMBER窗口函数简单了解下

ROW_NUMBER() 是 MySQL8引入的窗口函数之一&#xff0c;它为查询结果集中的每一行分配一个唯一的顺序号&#xff08;行号&#xff09;。这个顺序号是基于窗口函数的 ORDER BY 子句进行排序的&#xff0c;可以根据指定的排序顺序生成连续的整数值。 ROW_NUMBER() 在分页、去重、…...

pyhton django web集群基于linux定时任务

基于django management/commands目录下的脚本 from django.core.management import BaseCommand import logging import uuid from pia.utils.cache import reset_redis_expire from pia.utils.reids_key import TASK_KEYlogging logging.getLogger(task)""" …...

STM32串口——5个串口的使用方法

参考文档 STM32串口——5个串口的使用方法_51CTO博客_stm32串口通信的接收与发送 串口是我们常用的一个数据传输接口&#xff0c;STM32F103系列单片机共有5个串口&#xff0c;其中1-3是通用同步/异步串行接口USART(Universal Synchronous/Asynchronous Receiver/Transmitter)…...

友思特新闻 | 友思特荣获广州科技创新创业大赛智能装备行业赛初创组优胜企业!

2024年11月19日&#xff0c;第十三届中国创新创业大赛&#xff08;广东广州赛区&#xff09;暨2024年广州科技创新创业大赛智能装备行业赛颁奖典礼隆重举行。 赛事奖项介绍&#xff1a;广州科技创新创业大赛智能装备行业赛 第十三届“中国创新创业大赛&#xff08;广东广州赛区…...

react函数式组件中的路由传参方式

React Router 提供了多种方式来传递路由参数&#xff1a; URL 路径参数&#xff1a;通过动态路由和 useParams 获取。查询参数&#xff1a;通过 useLocation 获取 URL 查询字符串。路由状态传递&#xff1a;通过 state 属性在导航时传递数据&#xff0c;不在 URL 中显示&#…...

docker部署微信机器人实现任意群聊发送消息

前言&#xff1a;底层技术用的是wechaty&#xff0c;我只是做了一些集成。 此项目源码如下&#xff1a;https://gitee.com/yang123888/wechaty-wxtest 拉取&#xff1a; docker pull registry.cn-hangzhou.aliyuncs.com/yamyang/kercore-wx-bot:1.0.0运行&#xff1a; dock…...

Vercel 设置自动部署 GitHub 项目

Vercel 设置自动部署 GitHub 项目 问题背景 最近 Vercel 调整了其部署政策&#xff0c;免费版用户无法继续使用自动部署功能&#xff0c;除非升级到 Pro 计划。但是&#xff0c;我们可以通过配置 Deploy Hooks 来实现同样的自动部署效果。 解决方案 通过设置 Vercel 的 Dep…...

【shodan】(七)命令

shodan基础&#xff08;七&#xff09; 声明&#xff1a;该笔记为up主 泷羽的课程笔记&#xff0c;本节链接指路。 警告&#xff1a;本教程仅作学习用途&#xff0c;若有用于非法行为的&#xff0c;概不负责。 查询账号 查看账户扫描次数 shodan info查询域名信息 shodan d…...

文件上传代码分析

目录 不同类型的语言脚本语⾔/解释型语⾔⼀次编译到处运⾏编译型语⾔ 不同语⾔的webshell上传差异脚本语⾔/解释型语⾔⼀次编译到处运⾏编译型语⾔ ⽂件上传到webshell任意⽂件上传js检测解析规则MIME⽂件头后缀检测失效 NTFS Tricks 不同类型的语言 脚本语⾔/解释型语⾔ 代表…...