《Shader入门精要》透明效果

news2024/11/28 12:10:31

代码以及实例图可以看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  test
            clip  (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 test
					clip(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  Off
            Blend  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 Off
				Blend SrcAlpha OneMinusSrcAlpha

				CGPROGRAM

				#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 only
			Pass {
				ZWrite On
				ColorMask 0
			}

			Pass {
				Tags { "LightMode" = "ForwardBase" }

				ZWrite Off
				Blend SrcAlpha OneMinusSrcAlpha

				CGPROGRAM

				#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  Min
        Blend  One  One

        // 变亮(Lighten)
        BlendOp  Max

        Blend  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  culling
            Cull  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  faces
                  Cull  Front

                  // 和之前一样的代码
              }

              Pass  {
                  Tags  {  "LightMode"="ForwardBase"  }

                  //  Second  pass  renders  only  front  faces
                  Cull  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 Front

				ZWrite Off
				Blend SrcAlpha OneMinusSrcAlpha

				CGPROGRAM

				#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 Back

				ZWrite Off
				Blend SrcAlpha OneMinusSrcAlpha

				CGPROGRAM

				#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
			}
		}
}

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

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

相关文章

Exploring Prompt Engineering: A Systematic Review with SWOT Analysis

文章目录 题目摘要简介方法论背景相关工作评估结论 题目 探索快速工程&#xff1a;基于 SWOT 分析的系统评价 论文地址&#xff1a; https://arxiv.org/abs/2410.12843 摘要 在本文中&#xff0c;我们对大型语言模型 (LLM) 领域的提示工程技术进行了全面的 SWOT 分析。我们强…

LLM-pruner源码解析

1.超参数 模型剪枝的超参数 模型 模型检查点和日志的保存地址 剪枝比例&#xff0c;这里默认0.5 剪枝类型&#xff0c;这里模型L2 模型生成时的超参数 温度 top_p 最大序列长度 逐通道&#xff0c;逐块&#xff0c;逐层&#xff0c;这个逐层我不记得在论文里面提过啊 layer…

Stable Diffusion 3详解

&#x1f33a;系列文章推荐&#x1f33a; 扩散模型系列文章正在持续的更新&#xff0c;更新节奏如下&#xff0c;先更新SD模型讲解&#xff0c;再更新相关的微调方法文章&#xff0c;敬请期待&#xff01;&#xff01;&#xff01;&#xff08;本文及其之前的文章均已更新&…

零基础学安全--shell(8)脚本相互利用

目录 学习连接 脚本相互利用 脚本利用 利用脚本中的变量 重定向 输出重定向 错误输出 输入重定向 学习连接 声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探…

图书系统小案例

目前就实现了分页查询&#xff0c;修改&#xff0c;删除功能 这个小案例练习到了很多技能&#xff0c;比如前后端交互、异步请求、三层架构思想、后端连接数据库、配置文件、基础业务crud等等 感兴趣的小伙伴可以去做一个试试 准备工作 1、使用maven构建一个web工程 打开i…

深度理解进程的概念(Linux)

目录 一、冯诺依曼体系 二、操作系统(OS) 设计操作系统的目的 核心功能 系统调用 三、进程的概念与基本操作 简介 查看进程 通过系统调用获取进程标识符 通过系统调用创建进程——fork() 四、进程的状态 操作系统中的运行、阻塞和挂起 理解linux内核链表 Linux的进…

自媒体图文视频自动生成软件|03| 页面和结构介绍

代码获取方式在文本末尾&#x1f51a; *代码获取方式在文本末尾&#x1f51a; *代码获取方式在文本末尾&#x1f51a; *代码获取方式在文本末尾&#x1f51a; 视频图片生成器 一个基于 Python 和 Web 的工具&#xff0c;用于生成带有文字和语音的视频以及图片。支持多种尺寸、…

STM32的一些知识技巧

STM32的一些知识技巧 STM32命名规则 查看代码编译所占用的flash和SRAM的大小 单位为字节&#xff08;Byte&#xff09; 1、使用keil编译结果进行计算 2、查看.map文件 STM32启动模式 主闪存启动地址为0x08000000 查看程序段/函数执行时间 global.prop文件 保存字体配置&…

我们来学mysql -- EXPLAIN之rows(原理篇)

EXPLAIN之rows 题记rows 题记 书接上文《 EXPLAIN之ref》2024美国大选已定&#xff0c;川普剑登上铁王座&#xff0c;在此过程中出谋划策的幕僚很重要&#xff0c;是他们决定了最终的执行计划在《查询成本之索引选择》中提到&#xff0c;explain的输出&#xff0c;就是优化器&…

【AI系统】昇腾 AI 核心单元

昇腾 AI 核心单元 本文将深入介绍昇腾 AI 处理器的核心单元——AI Core&#xff0c;以及其背后的达芬奇架构。昇腾 AI 处理器是华为针对 AI 领域设计的专用处理器&#xff0c;其核心 AI Core 采用了特定域架构&#xff08;Domain Specific Architecture&#xff0c;DSA&#x…

Hadoop生态圈框架部署(九)- Hive部署

文章目录 前言一、Hive部署&#xff08;手动部署&#xff09;下载Hive1. 上传安装包2. 解压Hive安装包2.1 解压2.2 重命名2.3 解决guava冲突 3. 配置Hive3.1 配置Hive环境变量3.2 修改 hive-site.xml 配置文件3.3 配置MySQL驱动包3.3.1 下在MySQL驱动包3.3.2 上传MySQL驱动包3.…

RHCE——SELinux

SELinux 什么是SELinux呢&#xff1f;其实它是【Security-Enhanced Linux】的英文缩写&#xff0c;字母上的意思就是安全强化Linux的意思。 SELinux是由美国国家安全局(NSA)开发的&#xff0c;当初开发的原因是很多企业发现&#xff0c;系统出现问题的原因大部分都在于【内部…

Vue3的通灵之术Teleport

前言 近期Vue3更新了一些新的内容&#xff0c;我都还没有一个一个仔细去看&#xff0c;但是还是有必要去解读一下新内容的。就先从Teleport 开始吧。 官方对 Teleport 的解释是&#xff1a;<Teleport> 是一个内置组件&#xff0c;它可以将一个组件内部的一部分模板“传…

介绍一下atof(arr);(c基础)

hi , I am 36 适合对象c语言初学者 atof(arr)&#xff1b;是返回浮点数(double型)&#xff0c;浮点数数是arr数组中字符中数字 格式 #include<stdio.h> atof(arr); 返回值arr数组中的数 未改变arr数组 #include<stdio.h> //atof(arr) 返 <stdlib> int…

STM32 USART配置库函数

单片机学习&#xff01; 目录 一、USART配置函数 1.1 USART_DeInit函数 1.2 USART_Init函数 1.3 USART_StructInit函数 二、配置同步时钟输出函数 2.1 USART_ClockInit函数 2.2 USART_ClockStructInit函数 三、USART的外设与中断函数 3.1 USART_Cmd函数 3.2 USART_IT…

通俗理解人工智能、机器学习和深度学习的关系

最近几年人工智能成为极其热门的概念和话题&#xff0c;可以说彻底出圈了。但人工智能的概念在1955年就提出来了&#xff0c;可以说非常古老。我在上小学的时候《科学》课本上就有人工智能的概念介绍&#xff0c;至今还有印象&#xff0c;但那些年AI正处于“寒冬”&#xff0c;…

2024数学建模亚太赛【C题】赛题详细解析

目录 &#x1f4d1;一、竞赛时间 &#x1f5dd;️二、奖项设置 ✏️三、选题思路 &#x1f50d;阶段一&#xff1a;【数据预处理与探索性分析】 1.【数据清洗与预处理】 2.【探索性数据分析&#xff08;EDA&#xff09;】 &#x1f50d;阶段二&#xff1a;【时间序列建模…

数据结构 【堆实现】

上文提到堆是一种特殊的二叉树&#xff0c;其中它的父结点均不大于或者不小于其子结点的值。堆总是一棵完全二叉树。其中&#xff0c;堆的父节点全部不小于它的子结点时称为大堆&#xff0c;堆的父结点全部不大于其子结点的堆称为小堆。 堆可以由两种结构来实现&#xff0c;分别…

【AI绘画】Midjourney进阶:色调详解(下)

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: AI绘画 | Midjourney 文章目录 &#x1f4af;前言&#x1f4af;Midjourney中的色彩控制为什么要控制色彩&#xff1f;为什么要在Midjourney中控制色彩&#xff1f; &#x1f4af;色调纯色调灰色调暗色调 &#x1f4af…

[代码随想录Day24打卡] 93.复原IP地址 78.子集 90.子集II

93.复原IP地址 一个合法的IP地址是什么样的&#xff1a; 有3个’.分割得到4个数&#xff0c;每个数第一个数不能是0&#xff0c;不能含有非法字符&#xff0c;不能大于255。 这个是否属于合法IP相当于一个分割问题&#xff0c;把一串字符串分割成4部分&#xff0c;分别判断每…