UnityShader6:最简单的顶点/片元着色器

一、顶点/片元着色器基本结构

直接上代码:

这个着色器可以得到蓝色的纯色输出,如果顶点着色器得出了错误的裁剪空间坐标,那么会出现很明显表现错误

Shader "Jaihk662/NewSurfaceShader"
{
    Properties
    {

    }
    SubShader
    {
        Tags { "RenderType" = "Opaque" }
        LOD 200
        PASS 
        {
            CGPROGRAM
            #pragma vertex vert             //声明顶点着色器的函数
            #pragma fragment frag           //声明片段着色器的函数

            float4 vert(float4 v: POSITION): SV_POSITION {
                return UnityObjectToClipPos(v);             //将顶点乘上MVP矩阵,转入裁剪空间,同等于旧版的 mul(UNITY_MATRIX_MVP, v)
            }
            fixed4 frag(): SV_Target {
                return fixed4(0.0, 0.0, 1.0, 1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

很多语句已经在 UnityShader4:UnityShader的形式 这一章提到过了,因此不再记录

#pragma vertex [Name]:告诉Unity哪个函数包含了顶点着色器的代码,函数名随意,片段着色器同理

POSITION 和 SV_POSITION 则是 CG/HLSL 中的语义 (semantics),它们是不可省略的,这些语义将告诉系统用户需要哪些输入值,以及用户的输出是什么

目前常见的语义如下:

  • POSITION:让 Unity 将模型的顶点坐标填入对应的参数(代码中是参数 v)
  • NORMAL/TEXCOORD0~9/TANGENT:同上,对应法向量、第x套纹理坐标、切向量
  • SV_POSITION:Unity 顶点着色器输出的裁剪空间中的顶点坐标,顶点着色器的数据结构中必须包含此变量
  • COLOR0~9:Unity 顶点着色器输出的自定义数据,一般都用于指定颜色
  • SV_Target:通知渲染器把用户的输出颜色存储到某个渲染目标(render target)中,这里将输出到默认帧缓冲

填充到 POSITION/TANGENT/NORMAL 这些语义中的数据来源于材质中的 MeshRender 组件,在每帧调用 DrawCall 的时候 MeshRender 组件就会把它负责渲染的模型数据发送给 UnityShader

二、数据传递

上面那个例子将模型的顶点位置传递给了顶点着色器,但是除了位置,顶点还有纹理坐标和法线方向等属性,如果这些数据也需要拿来参与计算,那么最好把它们放在一个结构体里统一定义,并将整个结构体作为参数传入顶点着色器

struct vertData {
    float4 vertex: POSITION;
    float3 normal: NORMAL;
    float4 texcoord: TEXCOORD0;
};
float4 vert(vertData v): SV_POSITION {
    return UnityObjectToClipPos(v.vertex);             //将顶点乘上MVP矩阵,转入裁剪空间,同等于旧版的 mul(UNITY_MATRIX_MVP, v)
}

顶点着色器到片段着色器的数据传递也是类似的实现:

扫描二维码关注公众号,回复: 12476354 查看本文章
struct vert2frag {
    float4 pos: SV_POSITION;
    fixed4 color: COLOR0;
};

vert2frag vert(vertData v) {
    vert2frag o;
    o.pos = UnityObjectToClipPos(v.vertex);
    o.color = fixed4(v.normal * 0.5, 1);
    return o;
}
fixed4 frag(vert2frag i): SV_Target {
    return i.color; 
}

三、Properties使用

前面并没有在 Properties 语义块中定义任何属性,所有的属性都是内置的,那么在 Properties 语义块中定义的属性该如何使用呢?

很简单,只需要在下面的 CG 代码块中声明一个同名同类型的属性就可以了:

本章完整的代码和测试效果:

Shader "Jaihk662/NewSurfaceShader"
{
    Properties
    {
        _Color ("Color", Color) = (0.0, 0.0, 1.0, 1.0)
    }
    SubShader
    {
        Tags { "RenderType" = "Opaque" }
        LOD 200
        PASS 
        {
            CGPROGRAM
            #pragma vertex vert             //声明顶点着色器的函数
            #pragma fragment frag           //声明片段着色器的函数

            fixed4 _Color;
            struct vertData {
                float4 vertex: POSITION;
                float3 normal: NORMAL;
                float4 texcoord: TEXCOORD0;
            };
            struct vert2frag {
                float4 pos: SV_POSITION;
                fixed4 color: COLOR0;
            };

            vert2frag vert(vertData v) {
                vert2frag o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.color = _Color;
                return o;
            }
            fixed4 frag(vert2frag i): SV_Target {
                return i.color; 
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

 

同名好理解,但是同类型呢?Properties 语义块好像没有声明对应的类型啊,该怎么对应呢?

这是因为同一属性可以用多个不同的类型表示,所以 Properties 语义块中无需提前声明,在 UnityShader3:ShaderLab 这一章中提到了 Properties 语义块支持的属性类型,下面是对其表的一个补充,列出了 ShaderLab 属性类型和 CG 变量类型的匹配关系

其中左侧为 ShaderLab 属性类型,右侧为对应的 CG 变量类型(对应任意其一即可)

  • Color, Vector:float4, half4, fixed4
  • Range, Float:float, half, fixed
  • 2D:sampler2D
  • Cube:samplerCube
  • 3D:sampler3D

猜你喜欢

转载自blog.csdn.net/Jaihk662/article/details/110122082
今日推荐