Shader学习笔记 (一) :利用shader在一个面绘制出圆
在各种游戏中,想必大家一定和我一样总是惊叹于游戏画面的各种炫酷的特效。
作为游戏开发中单独列出的一个职业TA(Technology Art),他们会利用GLSL或者HLSL等着色器语言绘制出一幅幅美丽的画面。
于是作为小白,从零开始学习ShaderLab,记录一下学习历程。
ShaderLab从入门到放弃过很多次,总是想搞清楚一个shader完整的语法结构和每一行声明的意义。
事实上,作为小白,如果不想总是碰壁然后放弃,也许先从修改开始比较合适。
1.创建第一个Shader
我们在Unity 工程文件中新建一个Unlit Shader文件,默认的Shader文件是一个具备基本功能的shader。
1 |
Shader "Unlit/DrawCircle"
//定义了着色器的名称为 “Unlit/DrawCircle”,表示这是一个不受光照影响的着色器。
{
//着色器属性列表,定义了着色器材质上可以自定义的属性
Properties
{
//主纹理
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
//指定了渲染类型为不透明物体。
Tags { "RenderType"="Opaque" }
//指定了细节层级为 100
LOD 100
//在 SubShader 中定义了一个 Pass。
Pass
{
//在 Pass 中,通过 CGPROGRAM 和 ENDCG 标记将着色器代码包裹起来,表示代码块开始和结束。
CGPROGRAM
//定义了顶点着色器函数的入口
#pragma vertex vert
//定义了片元着色器函数的入口
#pragma fragment frag
// 启用雾效
#pragma multi_compile_fog
#include "UnityCG.cginc"
//定义了输入顶点数据的数据结构,包括顶点坐标和纹理坐标。
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
//定义了传递给片元着色器的数据结构,包括纹理坐标、雾效坐标、裁剪空间位置和世界空间位置。
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
//顶点着色器函数的定义,将输入顶点数据转换为屏幕空间坐标,并传递给片元着色器。
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
//片元着色器函数的定义,接收顶点着色器传递的数据,并计算最终的像素颜色。
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
2.做出一些修改。比如说绘制一个自定义颜色的圆
首先应该公开定义一个颜色变量,所有在着色器列表属性中公开的属性都应该遵循①在Properties中定义 ②在Pass中声明
比如在这里我们声明一个颜色Color1
1 2 3 4 5 6 7 | //着色器属性列表,定义了着色器材质上可以自定义的属性 Properties { //主纹理 _MainTex ( "Texture" , 2D) = "white" {} _Color1( "Color1" ,Color)=(1,1,1,1) } |
1 2 3 | sampler2D _MainTex; float4 _MainTex_ST; fixed4 _Color1; |
其次绘制圆,要定下来圆心,比如把圆心放在面的中心点。
那么我需要知道每个像素点的坐标,我们可以在传递给片元着色器的数据结构中加入一个自定义的字段worldPos,就是每个像素点的坐标
1 2 3 4 5 6 7 8 | //定义了传递给片元着色器的数据结构,包括纹理坐标、雾效坐标、裁剪空间位置和世界空间位置。 struct v2f { float2 uv : TEXCOORD0; UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; float3 worldPos :TEXCOORD1; }; |
其次在顶点着色器函数中增加一段逻辑,将输入的顶点数据转化为屏幕坐标,并传递给片元着色器
1 2 3 4 5 6 7 8 9 10 11 | //顶点着色器函数的定义,将输入顶点数据转换为屏幕空间坐标,并传递给片元着色器。 v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); // o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.uv = mul(unity_ObjectToWorld, v.vertex).xyz;; o.worldPos=v.vertex; UNITY_TRANSFER_FOG(o,o.vertex); return o; } |
最后我们调整片元着色器函数的逻辑,接受顶点着色器传递的数据,并计算最终像素颜色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //片元着色器函数的定义,接收顶点着色器传递的数据,并计算最终的像素颜色。 fixed4 frag (v2f i) : SV_Target { // sample the texture fixed4 col = tex2D(_MainTex, i.uv); //定义一个变量存储:当前像素点距离中心点的位置 float dist=distance(i.worldPos,float3(0,0,0)); //如果像素点距离中心点大于0.3小于0.32,将自定义的颜色赋值在这区间的像素内; //否则丢弃当前像素点 if(dist>0.3&&dist<0.32){ col*=_Color1; } else{ discard; } // apply fog UNITY_APPLY_FOG(i.fogCoord, col); return col; } |
附上最后的效果:
以及完整的Shader代码
Shader "Unlit/DrawCircle123" //定义了着色器的名称为 “Unlit/DrawCircle”,表示这是一个不受光照影响的着色器。 { //着色器属性列表,定义了着色器材质上可以自定义的属性 Properties { //主纹理 _MainTex ("Texture", 2D) = "white" {} _Color1("Color1",Color)=(1,1,1,1) } SubShader { //指定了渲染类型为不透明物体。 Tags { "RenderType"="Opaque" } //指定了细节层级为 100 LOD 100 //在 SubShader 中定义了一个 Pass。 Pass { //在 Pass 中,通过 CGPROGRAM 和 ENDCG 标记将着色器代码包裹起来,表示代码块开始和结束。 CGPROGRAM //定义了顶点着色器函数的入口 #pragma vertex vert //定义了片元着色器函数的入口 #pragma fragment frag // 启用雾效 #pragma multi_compile_fog #include "UnityCG.cginc" //定义了输入顶点数据的数据结构,包括顶点坐标和纹理坐标。 struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; //定义了传递给片元着色器的数据结构,包括纹理坐标、雾效坐标、裁剪空间位置和世界空间位置。 struct v2f { float2 uv : TEXCOORD0; UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; float3 worldPos :TEXCOORD1; }; sampler2D _MainTex; float4 _MainTex_ST; fixed4 _Color1; //顶点着色器函数的定义,将输入顶点数据转换为屏幕空间坐标,并传递给片元着色器。 v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); // o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.uv = mul(unity_ObjectToWorld, v.vertex).xyz;; o.worldPos=v.vertex; UNITY_TRANSFER_FOG(o,o.vertex); return o; } //片元着色器函数的定义,接收顶点着色器传递的数据,并计算最终的像素颜色。 fixed4 frag (v2f i) : SV_Target { // sample the texture fixed4 col = tex2D(_MainTex, i.uv); //定义一个变量存储:当前像素点距离中心点的位置 float dist=distance(i.worldPos,float3(0,0,0.5)); //如果像素点距离中心点大于0.3小于0.32,将自定义的颜色赋值在这区间的像素内; //否则丢弃当前像素点 if(dist>0.3&&dist<0.32){ col*=_Color1; } else{ discard; } // apply fog UNITY_APPLY_FOG(i.fogCoord, col); return col; } ENDCG } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通