Chapter5 开始Unity Shader——Shader入门精要学习笔记

一、最简单的顶点/片元着色器

1.第一个简单代码

Shader "Custom/Chapter5_SimpleShader"
{
    SubShader{
        Pass{
            CGPROGRAM //由CGPROGRAM和ENDCG包含的CG代码片段

            #pragma vertex vert
            #pragma fragment frag
            //这两行告诉Unity,哪个函数包含了顶点着色器的代码,哪个函数包含了片元着色器的代码
            //#pragma vertex name —— name即为函数名
            //#pragma fragment name

            float4 vert(float4 v : POSITION) : SV_POSITION{ //vert函数输入v包含了这个顶点的位置,是通过POSITION这个语义指定的,返回float4类型的值,是该顶点在裁剪空间中的位置
                //POSITION 和 SV_POSITION都是不可省略的,告诉用户输入输出是什么
                    //POSTION告诉unity把模型顶点坐标填充到输入参数中
                    //SV_POSITION告诉输出是裁剪空间中的顶点坐标
                return mul (UNITY_MATRIX_MVP, v); //把顶点坐标从模型空间坐标转换到裁剪空间
                }
            fixed4 frag():SV_Target{ //SV_Target告诉用户输出颜色存储到一个渲染目标中(这里输出到默认的帧缓存)
                return fixed4(1.0, 1.0, 1.0, 1.0); //白色的fixed4类型的变量
                }
            ENDCG
            }
        }
}
  • 可以得到一个白色的球
    在这里插入图片描述

2.想要得到顶点纹理坐标和法线方向

定义一个结构体

在这里插入图片描述

Shader "Custom/Chapter5_SimpleShader"
{
    SubShader{
        Pass{
            CGPROGRAM //由CGPROGRAM和ENDCG包含的CG代码片段

            #pragma vertex vert
            #pragma fragment frag
            //这两行告诉Unity,哪个函数包含了顶点着色器的代码,哪个函数包含了片元着色器的代码
            //#pragma vertex name —— name即为函数名
            //#pragma fragment name

            //使用一个结构体来定义顶点着色器的输入
            struct a2v{ //a2v表示把数据从应用阶段传递到顶点着色器中
                //POSITION告诉unity,用模型空间的顶点坐标填充vertex变量
                float4 vertex : POSITION;
                //NORMAL告诉unity,用模型空间的法线方向填充normal变量
                float3 normal : NORMAL; 
                //TEXCOORD0告诉unity,用模型的第一套纹理坐标填充texcoord
                float4 texcoord : TEXCOORD0;
                };

            float4 vert(a2v v) : SV_POSITION{ //vert函数输入v包含了这个顶点的位置,是通过POSITION这个语义指定的,返回float4类型的值,是该顶点在裁剪空间中的位置
                //POSITION 和 SV_POSITION都是不可省略的,告诉用户输入输出是什么
                    //POSTION告诉unity把模型顶点坐标填充到输入参数中
                    //SV_POSITION告诉输出是裁剪空间中的顶点坐标
                return UnityObjectToClipPos (v);
                }
            fixed4 frag():SV_Target{
                return fixed4(1.0, 1.0, 1.0, 1.0);
                }
            ENDCG
            }
        }
}

  • POSITION、TANGENT、NORMAL这些语义是由使用该材质的Mesh Render组件提供的,每帧调用Draw Call的时候,Mesh Render就会把负责渲染的模型数据发给Unity Shader

3.顶点着色器和片元着色器直接如何通信

把法线、纹理坐标等传递给片元着色器

Shader "Custom/Chapter5_SimpleShader"
{
    SubShader{
        Pass{
            CGPROGRAM //由CGPROGRAM和ENDCG包含的CG代码片段

            #pragma vertex vert
            #pragma fragment frag
            //这两行告诉Unity,哪个函数包含了顶点着色器的代码,哪个函数包含了片元着色器的代码
            //#pragma vertex name —— name即为函数名
            //#pragma fragment name

            //使用一个结构体来定义顶点着色器的输入
            struct a2v{ //a2v表示把数据从应用阶段传递到顶点着色器中
                //POSITION告诉unity,用模型空间的顶点坐标填充vertex变量
                float4 vertex : POSITION;
                //NORMAL告诉unity,用模型空间的法线方向填充normal变量
                float3 normal : NORMAL; 
                //TEXCOORD0告诉unity,用模型的第一套纹理坐标填充texcoord
                float4 texcoord : TEXCOORD0;
                };

             struct v2f{ //v2f定义顶点着色器的输出
                //SV_POSITION告诉unity,pos里包含了顶点在裁剪空间中的位置信息
                float4 pos : SV_POSITION;
                //COLOR0语义可以用于存储颜色信息
                fixed3 color : COLOR0;
                };

            v2f vert(a2v v){ //vert函数输入v包含了这个顶点的位置,是通过POSITION这个语义指定的,返回float4类型的值,是该顶点在裁剪空间中的位置
                //POSITION 和 SV_POSITION都是不可省略的,告诉用户输入输出是什么
                    //POSTION告诉unity把模型顶点坐标填充到输入参数中
                    //SV_POSITION告诉输出是裁剪空间中的顶点坐标
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                //v.normal包含顶点的法线方向,范围在(-1.0,1.0)
                //下面代码把分量范围映射到 [0.0,1.0)
                //存储到o.color传递给片元着色器
                o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5);
                return o;
                }

            fixed4 frag(v2f i) : SV_Target{
                //将插值后的i.color显示到屏幕上
                return fixed4(i.color , 1.0);
                }
            ENDCG
            }
        }
}

在这里插入图片描述

  • 片元着色器的输入实际上是把顶点着色器的输入进行插值后的结果

4.如何使用属性

  • 通过在材质中调节参数,可以达到随时调整材质的效果
  • 参数需要写在Properties语义块中

Shader "Custom/Chapter5_SimpleShader"
{
    Properties{
        _Color ("Color Tint", Color) = (1.0, 1.0, 1.0, 1.0) 
        //见第24行
        }
    SubShader{
        Pass{
            CGPROGRAM //由CGPROGRAM和ENDCG包含的CG代码片段

            #pragma vertex vert
            #pragma fragment frag
            //这两行告诉Unity,哪个函数包含了顶点着色器的代码,哪个函数包含了片元着色器的代码
            //#pragma vertex name —— name即为函数名
            //#pragma fragment name

            fixed4 _Color; //需要在CG片段中提前定义一个新变量,名称与Properties的属性相同

            //使用一个结构体来定义顶点着色器的输入
            struct a2v{ //a2v表示把数据从应用阶段传递到顶点着色器中
                //POSITION告诉unity,用模型空间的顶点坐标填充vertex变量
                float4 vertex : POSITION;
                //NORMAL告诉unity,用模型空间的法线方向填充normal变量
                float3 normal : NORMAL; 
                //TEXCOORD0告诉unity,用模型的第一套纹理坐标填充texcoord
                float4 texcoord : TEXCOORD0;
                };

             struct v2f{ //v2f定义顶点着色器的输出
                //SV_POSITION告诉unity,pos里包含了顶点在裁剪空间中的位置信息
                float4 pos : SV_POSITION;
                //COLOR0语义可以用于存储颜色信息
                fixed3 color : COLOR0;
                };

            v2f vert(a2v v){ //vert函数输入v包含了这个顶点的位置,是通过POSITION这个语义指定的,返回float4类型的值,是该顶点在裁剪空间中的位置
                //POSITION 和 SV_POSITION都是不可省略的,告诉用户输入输出是什么
                    //POSTION告诉unity把模型顶点坐标填充到输入参数中
                    //SV_POSITION告诉输出是裁剪空间中的顶点坐标
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                //v.normal包含顶点的法线方向,范围在(-1.0,1.0)
                //下面代码把分量范围映射到 [0.0,1.0)
                //存储到o.color传递给片元着色器
                o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5);
                return o;
                }

            fixed4 frag(v2f i) : SV_Target{
                fixed3 c = i.color;
                //使用_Color属性来控制输出颜色
                c *= _Color.rgb;
                return fixed4(c , 1.0);
                }
            ENDCG
            }
        }
}
  • 在Properties声明一个color类型的属性_Color,类型是Color,初始值是 (1.0, 1.0, 1.0, 1.0)白色
  • 需要在CG片段中提前定义一个新变量,名称与Properties的属性相同
    在这里插入图片描述

二、内置文件和变量

三、Unity CG/HLSL的语义

1.语义的定义

  • 定义:用于描述着色器输入和输出数据类型的字符串。
  • 作用:告诉着色器输入输出的数据类型及其在图形管线中的用途

2.Unity支持的语义

  • 应用阶段 传递模型数据 给顶点着色器
    • POSITION:表示模型空间中的顶点位置,通常类型为 float4。
    • NORMAL:表示顶点法线,通常类型为 float3。
    • TANGENT:表示顶点切线,通常类型为 float4。
    • TEXCOORDn:表示纹理坐标,TEXCOORD0 表示第一组纹理坐标,通常类型为 float2 或 float4。
    • COLOR:表示顶点颜色,通常类型为 fixed4 或 float4。
  • 顶点着色器 传递数据给 片元着色器
    • SV_POSITION:表示裁剪空间中的顶点位置,结构体中必须包含一个用该语义修饰的变量。
    • COLOR0, COLOR1:通常用于输出额外的颜色信息。
    • TEXCOORD0 到 TEXCOORD7:通常用于输出纹理坐标数据。
  • 片元着色器输出 语义
    • SV_Target:表示渲染目标

3.如何定义复杂的变量类型

struct v2f {
  float4 pos : SV_POSITION;
  fixed3 color0 : COLOR0;
  fixed4 color1 : COLOR1;
  half value0 : TEXCOORD0;
  float2 value1 : TEXCOORD1;
};
  • pos 用 SV_POSITION修饰,表示这是一个顶点位置数据
  • color0、color1 用 COLOR0、COLOR1修饰,表示是颜色数据
  • value0、value1 用 TEXCOORD0、TEXCOORD1修饰,表示是纹理坐标数据

四、Debug

1.方法1:使用假彩色图像

  • 主要思想:把需要调试的变量映射到 [0, 1] 之间,把它们作为颜色输出到屏幕上,通过屏幕上的颜色来判断这个值是否正确

2.方法2:Visual Studio

3.方法3:帧调试器

  • unity提供的针对渲染的调试器,打开:Window->Frame Debugger
  • 用于查看渲染该帧时进行的各种渲染事件(包含了Draw Call序列、清空帧缓存等操作)
  • 仅仅是使用停止渲染的方法来查看该帧的渲染结果

五、平台的差异

1.渲染纹理坐标差异

  • OpenGL和DirectX 在屏幕空间坐标上存在差异,OpenGL的(0,0)点在左下角,而DirectX在左上角
  • 会导致渲染纹理时出现图像翻转差异

2.Shader语法差异

  • DirectX对语法要求更加严格

3.Shader语义差异

  • 顶点着色器输出的顶点位置应该使用 SV_POSITION 语义,而不是 POSITION 语义(在PS4或使用了细分着色器的情况下
  • 片元着色器的输出颜色应该使用 SV_Target 语义,而不是 COLOR 或 COLOR0 语义(PS4)

解决方法

  • unity提供了一些内置函数,例如 UNITY_UV_STARTS_AT_TOP 用于判断平台类型,UNITY_MATRIX_MVP 用于坐标变换
  • 使用预处理指令,判断平台类型,针对不同平台进行不同处理
  • 31
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值