Shader-简单的顶点/片元着色器

实现一个简单地Shader


11173460-a4aee6ceea4cbe0c.png
简单的Shader
Shader "Unlit/SimpleShader"
{
    SubShader{
        Pass{
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            float4 vert(float4 v : POSITION) :SV_POSITION{
                return mul(UNITY_MATRIX_MVP,v);
            }
            float4 frag() : SV_TARGET{
                return fixed4(1.0,1.0,0.5f,1.0);
            }
            ENDCG
        }
    }
}

在CGPROGRAM到CGEND中间添加代码片段的编译指令和Cg代码.

#pragma vertex vert
#pragma fragment frag

告诉编译器那个函数执行顶点着色器,那个函数执行片元着色器.

float4 vert(float4 v : POSITION) :SV_POSITION{
                return mul(UNITY_MATRIX_MVP,v);
            }

使用顶点着色器代码,它是逐顶点进行,输入的参数包含了顶点位置,通过POSITION语义指定.
返回一个float4,它是该顶点在裁剪空间中的位置,通过SV_POSITION定义,UNITY_MATRIX_MVP是Unity内置的模型-观察-投影矩阵.

float4 frag() : SV_Target{
                return fixed4(1.0,1.0,0.5f,1.0);
            }

frag没有任何输入,输出一个float4,用SV_Target定义,等同于告诉渲染器,用户输出的颜色存储到一个渲染目标.


当我们需要更多的模型数据的时候,我们将为顶点着色器定义一个新的参数,这个参数将是一个结构体,结构体中包含了法线 切线 纹理坐标等诸多数据

struct a2v {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float4 texcoord : TEXCOORD0;
            };

这里的a2v表示a(application) v(vertex shader),表示数据从应用阶段传递到顶点着色器中
POSITION等语义中的数据从MeshRender中传递过来,每帧调用DrawCall的时候,MeshRender将他负责渲染的数据传递给UnityShader.
在给顶点着色器传参数的时候传a2v结构体

我们声明一个结构体v2f,用于在顶点着色器和片元着色器之间进行传递数据,

struct v2f {
                float4 pos : SV_POSITION;
                fixed3 color : COLOR0;//COLOR0语义用于存储颜色信息
            };

在顶点着色器中我们返回v2f的结构体,此结构体中包含了SV_POSITION,在顶点着色器中,我们给color值,来进行逐顶点的color插值,数值选用法线的相关值。

v2f vert(a2v v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.color = v.normal*0.5 + fixed3(0.5,0.5,0.5);
                return o;
            }

在fragment中我们只用返回插值后的color

fixed4 frag(v2f i) : SV_Target{
                return fixed4(i.color,1.0);
            }

最终效果:


11173460-d09a50741960daa5.PNG
插值得到的新结果
使用Cg/HLSL语言编写顶点/着色器时,可按以下步骤进行: ### 定义编译指令 真正顶点/着色器的代码定义在 `CGPROGRAM` 和 `ENDCG` 之间,需添加两行重要的编译指令: ```glsl #pragma vertex vert // 声明顶点着色器函数vert #pragma fragment frag // 声明着色器函数frag ``` 这两行指令分别指定了顶点着色器着色器对应的函数名 [^1][^4]。 ### 定义输入结构体 声明一个新的结构体(如 `a2v`),包含顶点着色器需要的模型数据。使用Unity支持的语义来填充这个结构体,对于顶点着色器的输入,Unity支持的语义有 `POSITION`、`TANGENT`、`NORMAL`、`TEXCOORD0`、`TEXCOORD1`、`TEXCOORD2`、`TEXCOORD3`、`COLOR` 等。示例代码如下: ```glsl struct a2v { float4 vertex : POSITION; // 用模型空间顶点坐标填充vertex float3 normal : NORMAL; // 用模型空间法线方向填充normal float4 texcoord : TEXCOORD0; // 用模型的第一套纹理坐标填充texcoord }; ``` 这里使用 `POSITION` 语义填充顶点坐标,`NORMAL` 语义填充法线方向,`TEXCOORD0` 语义填充第一套纹理坐标 [^3][^4]。 ### 定义输出结构体 定义一个负责顶点着色器输出的结构体(如 `v2f`),用于将顶点着色器的数据传递给着色器。从顶点着色器传递数据给着色器时,Unity使用的常用语义有 `SV_POSITION`、`COLOR0`、`COLOR1`、`TEXCOORD0 - TEXCOORD7` 等。其中 `SV_POSITION` 用于裁剪空间中的顶点坐标,结构体中必须包含一个用该语义修饰的变量。示例代码如下: ```glsl struct v2f { float4 pos : SV_POSITION; // pos中包含了裁剪空间的位置信息 fixed3 color : COLOR0; // 把颜色信息储存给color }; ``` `SV_POSITION` 语义修饰的 `pos` 变量存储裁剪空间的位置信息,`COLOR0` 语义修饰的 `color` 变量存储颜色信息 [^4][^5]。 ### 实现顶点着色器函数 实现顶点着色器函数(如 `vert`),该函数接收输入结构体(`a2v`),处理数据后返回输出结构体(`v2f`)。示例代码如下: ```glsl v2f vert(a2v v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); // 将模型空间顶点坐标转换为裁剪空间坐标 o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5); // 处理颜色信息 return o; } ``` 这里使用 `UnityObjectToClipPos` 函数将模型空间顶点坐标转换为裁剪空间坐标,并对法线信息进行处理得到颜色信息 [^4]。 ### 实现着色器函数 实现着色器函数(如 `frag`),该函数接收顶点着色器输出的结构体(`v2f`),处理数据后返回最终的颜色值。示例代码如下: ```glsl fixed4 frag(v2f i) : SV_Target { return fixed4(i.color, 1.0); // 返回最终颜色值 } ``` `SV_Target` 语义表示该函数的返回值是最终的颜色输出 [^4]。 ### 颜色空间与数据转换 在处理颜色时,需要考虑颜色空间与数据转换。在Cg/HLSL中,线性颜色空间是默认的。颜色属性在Inspector中输入的sRGB颜色会自动转换为线性值传入着色器;非颜色属性(`Float/Vector` 类型)默认不转换,如需视为sRGB值,需在ShaderLab中添加 `[Gamma]` 标签。示例代码如下: ```hlsl #include "UnityCG.cginc" fixed4 hdrColor = tex2D(_MainTex, uv); fixed4 linearColor = DecodeHDR(hdrColor, _MainTex_HDR); // 解码HDR颜色 ``` 这里使用 `DecodeHDR` 函数解码HDR颜色 [^2]。 以下是一个完整的示例代码: ```glsl Shader "Unity Shaders Book/Chapter 5/Simple Shader" { SubShader { // 没进行渲染设置和标签设置,使用默认的 Pass { // 没自定义Pass的渲染设置和标签设置 CGPROGRAM // 两行非常重要的编译指令: #pragma vertex vert // 指定哪个函数包含了顶点着色器的代码 -> vert() #pragma fragment frag // 指定哪个函数包含了着色器的代码 -> frag() struct a2v { float4 vertex : POSITION; // 用模型空间顶点坐标填充vertex float3 normal : NORMAL; // 用模型空间法线方向填充normal float4 texcoord : TEXCOORD0; // 用模型的第一套纹理坐标填充texcoord }; struct v2f { float4 pos : SV_POSITION; // pos中包含了裁剪空间的位置信息 fixed3 color : COLOR0; // 把颜色信息储存给color }; v2f vert(a2v v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5); return o; } fixed4 frag(v2f i) : SV_Target { return fixed4(i.color, 1.0); } ENDCG } } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值