这是一段跟随时间改变物体颜色的Shader代码。
Shader "Custom/ColorChangingShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color ("Color", Color) = (1,1,1,1)
_Speed ("Speed", Float) = 1.0
}
SubShader
{
Tags { "RenderType"="Opaque" }
CGPROGRAM
#pragma surface surf Standard
sampler2D _MainTex;
fixed4 _Color;
float _Speed;
struct Input
{
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Calculate color based on time
float time = _Time.y * _Speed;
float r = sin(time);
float g = cos(time);
float b = tan(time);
// Apply color and texture
fixed4 tex = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = tex.rgb * float3(r, g, b);
o.Alpha = tex.a;
}
ENDCG
}
}
Shader "Custom/ColorChangingShader"
该行为命名行,写上这行后可以将某个Material更改为该Shader,如下图所示。
备注:
命名Shader的名称可以和脚本名不一样,如该Shader的脚本名如下。
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color ("Color", Color) = (1,1,1,1)
_Speed ("Speed", Float) = 1.0
}
该段为Properties语义块,即属性声明,在此声明的属性会暴露在面板上,暴露的属性可以在面板上直接修改。
_Color ("Color", Color)
第一个_Color:属性名字(Name),代码调用中使用。
第二个"Color":材质面板上显示名称(displayname)。可以随便修改,不影响后续代码,只影响面板上该属性显示的名字。
第三个Color:该属性的类型(PropertyType)。
SubShader
{
//...
}
SubShader语义块,放渲染的具体内容。
Tags { "RenderType"="Opaque" }
Tags:标签声明。用于指定Shader的渲染类型。
在该行代码中,标签为"RenderType"硬件设备,指定渲染类型为"Opaque"。
"Opaque"渲染类型表示Shader是完全不透明的,会完全遮挡后面的物体,这种渲染类型通常用于不透明的材质数据报告,如石头、金属等。
为什么要使用Opaque?
渲染不透明材质时,Opaque比Transparent等类型更高效,能减少性能开支。
不过具体用不用Opaque看项目经理心情。
其它类型:"Transparent"(透明材质)、"Fade"(半透明材质)和"Cutout"(有Alpha测试的材质)
CGPROGRAM
//...
ENDCG
告诉引擎这一块才是渲染的具体内容。
#pragma surface surf Standard
告诉引擎(和GPU)你用的是表面着色器,不要搞些乱七八糟的东西。
sampler2D _MainTex;
fixed4 _Color;
float _Speed;
有用的变量,通常和Properties语义块中的内容保持一致。
// 这是一个结构体,叫Input
struct Input
{
// 这是成员变量
// 该变量表示纹理坐标,用于从纹理中获取像素的颜色。
float2 uv_MainTex;
};
输入结构体是一种用于将数据从顶点着色器传递到像素着色器的特殊结构体。
在顶点着色器中,可以通过计算顶点的屏幕空间位置和模型变换矩阵,来计算顶点在纹理中的坐标,并将结果存储在Input结构体的成员变量中。在像素着色器中,可以使用这个纹理坐标来获取纹理中对应的像素颜色,并进行进一步的处理。
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Calculate color based on time
float time = _Time.y * _Speed;
float r = sin(time);
float g = cos(time);
float b = tan(time);
// Apply color and texture
fixed4 tex = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = tex.rgb * float3(r, g, b);
o.Alpha = tex.a;
}
这是Shader具体实现方式unity半透明材质,把“Input IN”这个信息传进去unity半透明材质,再将结果“o”传出来(即渲染)。
IN.uv_MainTex
和C#中参数差不多,IN是输入结构体的名称(在这里是Input结构体的名称),uv_MainTex是结构体中的成员变量。
tex2D(_MainTex, IN.uv_MainTex)
tex2D是Unity自带函数。这段代码意思是从_MainTex纹理中获取IN.uv_MainTex指定的纹理坐标对应的颜色值。(即像素着色器中进行纹理采样,以便渲染出正确的纹理效果)
inout SurfaceOutputStandard o
SurfaceOutputStandard是一种着色器输出结构体,它包含了表面的各种属性(如法线、漫反射和高光等)。o 则表示这个着色器输出结构体的一个实例,在像素着色器中对其中的各种属性进行赋值,以确定表面的最终外观。同时,由于参数传递是按值传递的,在函数执行完毕后,SurfaceOutputStandard结构体中被修改的值并不能返回给调用方(即上级Shader),使用inout关键字可以告诉编译器在函数执行完毕后将o变量中的修改传回到上级Shader中。
关于SurfaceOutputStandard结构体:
很牛逼的一个结构体,只能表面着色器和片元着色器使用,封装了一堆东西,所以能直接输出(也就是直接在语义块里写功能就行了)。隔壁像素着色器的SV_Target不是直接输出渲染,而是将像素输出信息写入渲染目标。顶点着色器不能像素输出,所以连SV_Target都不能用。
SV_Target的示例:
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;//retrun给v2f
}
float3 ProcessFrag(float2 uv);
float4 frag(v2f i) : SV_Target{
return float4(ProcessFrag(i.uv),1.0);
文章来源:https://blog.csdn.net/makyocute/article/details/130810109