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
        }
    }
}
复制代码

 

posted @   SummerTrainnn  阅读(597)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示